Java拷貝之淺拷貝與深拷貝詳解
在Java開發(fā)中,對(duì)象拷貝是高頻場(chǎng)景——無(wú)論是業(yè)務(wù)數(shù)據(jù)復(fù)用、集合操作,還是多線程環(huán)境下的資源隔離,都離不開拷貝操作。但很多開發(fā)者容易混淆淺拷貝和深拷貝,導(dǎo)致出現(xiàn)“修改拷貝對(duì)象,原對(duì)象莫名變化”的bug,甚至引發(fā)線上故障。
今天這篇博客,將從“概念本質(zhì)→可視化示意圖→完整代碼實(shí)例→誤區(qū)拆解”四個(gè)層面,徹底講透Java的淺拷貝與深拷貝,結(jié)合實(shí)戰(zhàn)案例演示每種拷貝方式的用法、優(yōu)缺點(diǎn),以及最容易踩的坑(比如clone方法的“偽深拷貝”),新手能快速入門,老手能查漏補(bǔ)缺,干貨無(wú)冗余。
先拋出核心結(jié)論(必記): - 淺拷貝:拷貝的是原對(duì)象的地址引用,原對(duì)象與拷貝對(duì)象共享底層數(shù)據(jù),一方修改,另一方隨之變化; - 深拷貝:拷貝的是原對(duì)象的所有數(shù)據(jù)(值拷貝),原對(duì)象與拷貝對(duì)象完全獨(dú)立,一方修改,另一方不受任何影響。
一、淺拷貝:“共享底層,牽一發(fā)而動(dòng)全身”
淺拷貝的核心邏輯的是“拷貝引用,不拷貝數(shù)據(jù)本身”。就像兩個(gè)人共用一把鑰匙,無(wú)論誰(shuí)打開房門、修改房間里的東西,另一個(gè)人看到的都是修改后的結(jié)果——原對(duì)象和拷貝對(duì)象指向的是同一塊內(nèi)存地址,本質(zhì)上還是同一個(gè)對(duì)象的“別名”。
淺拷貝示意圖(通俗理解)
「原對(duì)象」→ 內(nèi)存地址A(存儲(chǔ)數(shù)據(jù):username=張三,password=123456) 「拷貝對(duì)象」→ 直接指向內(nèi)存地址A(未創(chuàng)建新內(nèi)存,僅拷貝原對(duì)象的地址引用) 當(dāng)拷貝對(duì)象修改username為“李四”,本質(zhì)是修改內(nèi)存地址A中的數(shù)據(jù),原對(duì)象讀取的也是地址A的數(shù)據(jù),因此會(huì)同步變化。
實(shí)戰(zhàn)演示:淺拷貝的基本用法與現(xiàn)象
我們通過(guò)簡(jiǎn)單的Java代碼,直觀感受淺拷貝的特性——定義User類,通過(guò)“直接賦值”的方式實(shí)現(xiàn)淺拷貝(Java中直接賦值就是最基礎(chǔ)的淺拷貝)。
步驟1:定義User實(shí)體類
/**
* 實(shí)體類:用于演示拷貝操作
* 包含用戶名、密碼兩個(gè)基本類型屬性
*/
public class User {
// 基本類型屬性(默認(rèn)值初始化)
private String username = "張三";
private String password = "123456";
// getter/setter方法(用于讀取和修改屬性值)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}步驟2:編寫測(cè)試類,實(shí)現(xiàn)淺拷貝并驗(yàn)證
/**
* 拷貝測(cè)試類
* 演示淺拷貝的特性:原對(duì)象與拷貝對(duì)象相互影響
*/
public class CopyTest {
public static void main(String[] args) {
// 1. 創(chuàng)建原對(duì)象user01
User user01 = new User();
// 2. 淺拷貝:直接賦值(拷貝地址引用)
User user02 = user01;
// 3. 修改拷貝對(duì)象user02的username
user02.setUsername("李四");
// 打印原對(duì)象和拷貝對(duì)象的username
System.out.println("修改user02的username后:");
System.out.println("user01的username:" + user01.getUsername());
System.out.println("user02的username:" + user02.getUsername());
System.out.println("-----------------------------------");
// 4. 修改原對(duì)象user01的password
user01.setPassword("654321");
// 打印原對(duì)象和拷貝對(duì)象的password
System.out.println("修改user01的password后:");
System.out.println("user01的password:" + user01.getPassword());
System.out.println("user02的password:" + user02.getPassword());
}
}運(yùn)行結(jié)果與分析
修改user02的username后:
user01的username:李四
user02的username:李四
-----------------------------------
修改user01的password后:
user01的password:654321
user02的password:654321
結(jié)果很明顯: 1. 修改拷貝對(duì)象user02的username,原對(duì)象user01的username同步變成“李四”; 2. 修改原對(duì)象user01的password,拷貝對(duì)象user02的password同步變成“654321”。
核心原因:user01和user02指向同一塊內(nèi)存地址,兩者是“綁定關(guān)系”,無(wú)論修改哪一個(gè),本質(zhì)都是修改同一塊內(nèi)存中的數(shù)據(jù),自然會(huì)相互影響。
注意:淺拷貝并非毫無(wú)用處,當(dāng)我們僅需要“復(fù)用對(duì)象引用”,不需要修改數(shù)據(jù)時(shí),淺拷貝是高效的(無(wú)需創(chuàng)建新內(nèi)存,開銷極?。?;但如果需要“數(shù)據(jù)隔離”,淺拷貝就會(huì)引發(fā)bug。
二、深拷貝:“完全獨(dú)立,各自安好”
深拷貝的核心邏輯是“拷貝數(shù)據(jù)本身,不拷貝地址引用”。就像兩個(gè)人各自擁有一把獨(dú)立的鑰匙,各自的房間完全獨(dú)立,無(wú)論誰(shuí)修改自己房間里的東西,都不會(huì)影響到另一方——原對(duì)象和拷貝對(duì)象指向的是不同的內(nèi)存地址,是兩個(gè)完全獨(dú)立的對(duì)象,只是初始數(shù)據(jù)相同。
深拷貝示意圖(通俗理解)
「原對(duì)象」→ 內(nèi)存地址A(存儲(chǔ)數(shù)據(jù):username=張三,password=123456) 「拷貝對(duì)象」→ 新建內(nèi)存地址B(復(fù)制地址A中的所有數(shù)據(jù),初始值與原對(duì)象一致) 當(dāng)拷貝對(duì)象修改username為“李四”,僅修改內(nèi)存地址B中的數(shù)據(jù),原對(duì)象讀取的還是地址A的數(shù)據(jù),因此不受影響;反之亦然。
常見的5種深拷貝方式(實(shí)戰(zhàn)必備)
Java中實(shí)現(xiàn)深拷貝的方式有多種,各自有優(yōu)缺點(diǎn),我們結(jié)合實(shí)例逐一講解,重點(diǎn)關(guān)注“用法+適用場(chǎng)景”,同時(shí)拆解最容易踩的坑。
① 構(gòu)造函數(shù)方式(簡(jiǎn)單但不推薦)
核心思路:通過(guò)new關(guān)鍵字創(chuàng)建新對(duì)象,在構(gòu)造函數(shù)中傳入原對(duì)象的所有屬性值,實(shí)現(xiàn)“值拷貝”。
// 改造User類,增加帶參構(gòu)造函數(shù)
public class User {
private String username = "張三";
private String password = "123456";
// 帶參構(gòu)造函數(shù):傳入原對(duì)象,拷貝所有屬性值
public User(User user) {
this.username = user.username;
this.password = user.password;
}
// 無(wú)參構(gòu)造函數(shù)(保留默認(rèn))
public User() {}
// getter/setter方法(省略,與前文一致)
}測(cè)試代碼:
public static void main(String[] args) {
// 原對(duì)象
User user01 = new User();
// 深拷貝:通過(guò)帶參構(gòu)造函數(shù)創(chuàng)建新對(duì)象
User user02 = new User(user01);
// 修改拷貝對(duì)象的屬性
user02.setUsername("李四");
user02.setPassword("654321");
// 打印對(duì)比
System.out.println("user01的username:" + user01.getUsername()); // 張三
System.out.println("user02的username:" + user02.getUsername()); // 李四
System.out.println("user01的password:" + user01.getPassword()); // 123456
System.out.println("user02的password:" + user02.getPassword()); // 654321
}優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單,無(wú)需依賴任何工具類,適合簡(jiǎn)單實(shí)體類(屬性少); 缺點(diǎn):當(dāng)對(duì)象屬性較多、層級(jí)較深(比如包含引用類型屬性、嵌套對(duì)象)時(shí),需要手動(dòng)在構(gòu)造函數(shù)中拷貝所有屬性,代碼冗余且易出錯(cuò);同時(shí)創(chuàng)建對(duì)象過(guò)多時(shí),會(huì)增加系統(tǒng)內(nèi)存開銷,因此不推薦用于復(fù)雜場(chǎng)景。
② 重寫clone()方法(重點(diǎn),但易踩坑)
Java中所有類都默認(rèn)繼承自O(shè)bject類,Object類中有一個(gè)native方法clone(),該方法的默認(rèn)實(shí)現(xiàn)是淺拷貝;我們可以通過(guò)“重寫clone()方法+實(shí)現(xiàn)Cloneable接口”,將其改造為深拷貝——但這里有一個(gè)極易踩的坑,下文會(huì)重點(diǎn)拆解。
步驟1:實(shí)現(xiàn)Cloneable接口,重寫clone()方法
Cloneable接口是一個(gè)“標(biāo)記接口”(空接口,沒有任何抽象方法),其作用是“告訴JVM:此類允許進(jìn)行拷貝操作”;如果不實(shí)現(xiàn)該接口,調(diào)用clone()方法會(huì)拋出CloneNotSupportedException異常。
/**
* 重寫clone()方法實(shí)現(xiàn)深拷貝(初步改造)
* 1. 實(shí)現(xiàn)Cloneable標(biāo)記接口
* 2. 重寫Object類的clone()方法,修改訪問權(quán)限為public
* 3. 將返回值強(qiáng)轉(zhuǎn)為當(dāng)前類類型
*/
public class User implements Cloneable {
private String username = "張三";
private String password = "123456";
// 重寫clone()方法
@Override
public User clone() throws CloneNotSupportedException {
// 調(diào)用父類的clone()方法,強(qiáng)轉(zhuǎn)為User類型
return (User) super.clone();
}
// getter/setter方法(省略,與前文一致)
}步驟2:測(cè)試clone()方法的拷貝效果
public static void main(String[] args) {
try {
// 原對(duì)象
User user01 = new User();
// 深拷貝:調(diào)用重寫后的clone()方法
User user02 = user01.clone();
// 打印兩個(gè)對(duì)象的內(nèi)存地址(驗(yàn)證是否是不同對(duì)象)
System.out.println("user01的內(nèi)存地址:" + user01);
System.out.println("user02的內(nèi)存地址:" + user02);
// 修改拷貝對(duì)象的屬性,驗(yàn)證是否影響原對(duì)象
user02.setUsername("李四");
System.out.println("-----------------------------------");
System.out.println("user01的username:" + user01.getUsername()); // 張三
System.out.println("user02的username:" + user02.getUsername()); // 李四
} catch (CloneNotSupportedException e) {
// 捕獲未實(shí)現(xiàn)Cloneable接口的異常
e.printStackTrace();
}
}運(yùn)行結(jié)果與分析
user01的內(nèi)存地址:kaobei.User@15db9742
user02的內(nèi)存地址:kaobei.User@6d06d69c
-----------------------------------
user01的username:張三
user02的username:李四
結(jié)果符合預(yù)期: 1. 兩個(gè)對(duì)象的內(nèi)存地址不同(@后面的哈希值不同),說(shuō)明clone()方法創(chuàng)建了新的對(duì)象,實(shí)現(xiàn)了“值拷貝”; 2. 修改拷貝對(duì)象的username,原對(duì)象的username未變化,說(shuō)明兩者完全獨(dú)立,初步實(shí)現(xiàn)了深拷貝。
重點(diǎn)坑點(diǎn):clone()方法的“偽深拷貝”(必看)
上面的案例中,User類的屬性都是基本類型(String本質(zhì)是常量,也視為基本類型處理),因此重寫clone()方法能實(shí)現(xiàn)深拷貝;但如果User類中包含引用類型屬性(比如自定義對(duì)象),單純重寫clone()方法,就會(huì)變成“偽深拷貝”——僅拷貝引用類型的地址,不拷貝引用對(duì)象本身。
實(shí)戰(zhàn)演示:偽深拷貝的現(xiàn)象
步驟1:新增引用類型實(shí)體類Person
/**
* 引用類型實(shí)體類:用于演示clone()方法的坑
*/
public class Person {
private String name;
// getter/setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}步驟2:修改User類,增加Person類型的引用屬性
public class User implements Cloneable {
private String username = "張三";
private String password = "123456";
// 新增引用類型屬性:Person對(duì)象
private Person person = new Person();
// 新增方法:修改Person對(duì)象的name屬性
public void changePersonName(String name) {
this.person.setName(name);
}
// 新增方法:獲取Person對(duì)象的name屬性
public String getPersonName() {
return this.person.getName();
}
// 重寫的clone()方法(未修改)
@Override
public User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
// getter/setter方法(省略)
}步驟3:測(cè)試clone()方法對(duì)引用類型的拷貝效果
public static void main(String[] args) {
try {
User user01 = new User();
User user02 = user01.clone();
// 打印兩個(gè)User對(duì)象的內(nèi)存地址(確認(rèn)是不同對(duì)象)
System.out.println("user01的內(nèi)存地址:" + user01);
System.out.println("user02的內(nèi)存地址:" + user02);
// 修改原對(duì)象user01中的Person對(duì)象的name
user01.changePersonName("李四");
// 打印兩個(gè)User對(duì)象中Person的name
System.out.println("-----------------------------------");
System.out.println("user01的Person.name:" + user01.getPersonName());
System.out.println("user02的Person.name:" + user02.getPersonName());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}運(yùn)行結(jié)果與關(guān)鍵結(jié)論
user01的內(nèi)存地址:kaobei.User@15db9742
user02的內(nèi)存地址:kaobei.User@6d06d69c
-----------------------------------
user01的Person.name:李四
user02的Person.name:李四
驚人的現(xiàn)象:兩個(gè)User對(duì)象是獨(dú)立的(內(nèi)存地址不同),但它們內(nèi)部的Person對(duì)象卻相互影響——修改user01的Person.name,user02的Person.name也同步變化!
核心結(jié)論(必記):重寫clone()方法的默認(rèn)實(shí)現(xiàn),只能對(duì)當(dāng)前類的“基本類型屬性”進(jìn)行深拷貝,無(wú)法對(duì)“引用類型屬性”進(jìn)行深拷貝。也就是說(shuō),clone()方法會(huì)拷貝引用類型的地址,讓原對(duì)象和拷貝對(duì)象的引用屬性指向同一塊內(nèi)存地址,本質(zhì)上還是淺拷貝(偽深拷貝)。
解決方案:要實(shí)現(xiàn)真正的深拷貝,需要在clone()方法中,手動(dòng)對(duì)引用類型屬性也進(jìn)行clone()拷貝(遞歸clone),下文會(huì)補(bǔ)充完整實(shí)現(xiàn)。
③ Apache Commons Lang序列化(推薦,簡(jiǎn)潔高效)
Apache Commons Lang是Apache提供的工具類庫(kù),其中的SerializationUtils類提供了serialize()(序列化)和deserialize()(反序列化)方法,能快速實(shí)現(xiàn)深拷貝——核心原理是“將原對(duì)象序列化到字節(jié)流,再?gòu)淖止?jié)流反序列化為新對(duì)象”,反序列化會(huì)創(chuàng)建全新的對(duì)象,自然實(shí)現(xiàn)深拷貝。
使用步驟
1. 導(dǎo)入依賴(Maven):
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>2. 讓實(shí)體類(包括引用類型)實(shí)現(xiàn)Serializable接口(序列化標(biāo)記接口,與Cloneable類似,無(wú)抽象方法):
// User類實(shí)現(xiàn)Serializable
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 序列化版本號(hào),避免反序列化異常
private String username = "張三";
private String password = "123456";
private Person person = new Person(); // 引用類型,也需實(shí)現(xiàn)Serializable
// getter/setter、方法省略
}
// Person類實(shí)現(xiàn)Serializable
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
// getter/setter省略
}3. 調(diào)用工具類方法實(shí)現(xiàn)深拷貝:
import org.apache.commons.lang3.SerializationUtils;
public static void main(String[] args) {
User user01 = new User();
// 深拷貝:序列化+反序列化
User user02 = SerializationUtils.clone(user01);
// 修改原對(duì)象的引用屬性
user01.changePersonName("李四");
// 驗(yàn)證:拷貝對(duì)象不受影響
System.out.println("user01的Person.name:" + user01.getPersonName()); // 李四
System.out.println("user02的Person.name:" + user02.getPersonName()); // null(初始值)
}優(yōu)點(diǎn):無(wú)需手動(dòng)重寫clone()方法,無(wú)需遞歸處理引用類型,一行代碼實(shí)現(xiàn)深拷貝,支持任意層級(jí)的嵌套對(duì)象,簡(jiǎn)潔高效; 缺點(diǎn):需要導(dǎo)入第三方依賴(非JDK原生),且實(shí)體類及其所有引用類型都需實(shí)現(xiàn)Serializable接口,略繁瑣。
④ Gson序列化(推薦,無(wú)依賴限制)
Gson是Google提供的JSON解析工具類,也能通過(guò)“對(duì)象→JSON字符串→新對(duì)象”的方式實(shí)現(xiàn)深拷貝——核心原理是“將原對(duì)象轉(zhuǎn)為JSON字符串(脫離內(nèi)存引用),再將JSON字符串轉(zhuǎn)為新對(duì)象”,完全獨(dú)立于原對(duì)象。
使用步驟
1. 導(dǎo)入依賴(Maven):
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>2. 無(wú)需實(shí)現(xiàn)任何接口,直接調(diào)用方法:
import com.google.gson.Gson;
public static void main(String[] args) {
Gson gson = new Gson();
User user01 = new User();
// 深拷貝:對(duì)象→JSON→新對(duì)象
String json = gson.toJson(user01); // 原對(duì)象轉(zhuǎn)為JSON字符串
User user02 = gson.fromJson(json, User.class); // JSON字符串轉(zhuǎn)為新對(duì)象
// 修改原對(duì)象的屬性(包括引用類型)
user01.setUsername("李四");
user01.changePersonName("王五");
// 驗(yàn)證:拷貝對(duì)象完全獨(dú)立
System.out.println("user01的username:" + user01.getUsername()); // 李四
System.out.println("user02的username:" + user02.getUsername()); // 張三
System.out.println("user01的Person.name:" + user01.getPersonName()); // 王五
System.out.println("user02的Person.name:" + user02.getPersonName()); // null
}優(yōu)點(diǎn):無(wú)需實(shí)現(xiàn)Serializable或Cloneable接口,代碼簡(jiǎn)潔,支持復(fù)雜嵌套對(duì)象,且Gson是開發(fā)中常用的JSON工具,無(wú)需額外引入多余依賴; 缺點(diǎn):序列化/反序列化過(guò)程會(huì)有一定的性能開銷(比clone()方法慢),適合中小規(guī)模數(shù)據(jù)的拷貝場(chǎng)景。
⑤ Jackson序列化(推薦,Spring項(xiàng)目首選)
Jackson是Spring框架默認(rèn)的JSON解析工具,用法與Gson類似,也是通過(guò)“對(duì)象→JSON→新對(duì)象”實(shí)現(xiàn)深拷貝,適合Spring項(xiàng)目(無(wú)需額外導(dǎo)入依賴,Spring已集成)。
使用步驟
1. 導(dǎo)入依賴(Maven,Spring項(xiàng)目可省略):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>2. 調(diào)用Jackson工具類實(shí)現(xiàn)深拷貝:
import com.fasterxml.jackson.databind.ObjectMapper;
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
User user01 = new User();
// 深拷貝:對(duì)象→JSON字節(jié)流→新對(duì)象
byte[] bytes = objectMapper.writeValueAsBytes(user01);
User user02 = objectMapper.readValue(bytes, User.class);
// 修改原對(duì)象,驗(yàn)證拷貝對(duì)象獨(dú)立性
user01.setPassword("654321");
user01.changePersonName("趙六");
System.out.println("user01的password:" + user01.getPassword()); // 654321
System.out.println("user02的password:" + user02.getPassword()); // 123456
System.out.println("user01的Person.name:" + user01.getPersonName()); // 趙六
System.out.println("user02的Person.name:" + user02.getPersonName()); // null
}優(yōu)點(diǎn):Spring項(xiàng)目原生支持,無(wú)需額外導(dǎo)入依賴,性能優(yōu)于Gson,支持復(fù)雜對(duì)象和泛型,適合企業(yè)級(jí)開發(fā); 缺點(diǎn):需要處理IO異常(writeValueAsBytes和readValue方法會(huì)拋出異常),代碼略繁瑣。
三、深拷貝方式對(duì)比與選型建議(實(shí)戰(zhàn)必備)
為了方便大家在實(shí)際開發(fā)中快速選型,我們整理了5種深拷貝方式的優(yōu)缺點(diǎn)和適用場(chǎng)景,一目了然:
拷貝方式 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場(chǎng)景 |
|---|---|---|---|
構(gòu)造函數(shù) | 實(shí)現(xiàn)簡(jiǎn)單,無(wú)依賴 | 代碼冗余,易出錯(cuò),開銷大 | 簡(jiǎn)單實(shí)體類(屬性少),臨時(shí)使用 |
重寫clone()方法 | JDK原生,性能高 | 需實(shí)現(xiàn)Cloneable,引用類型需遞歸clone,易踩坑 | 性能要求高,對(duì)象層級(jí)簡(jiǎn)單 |
Apache Commons Lang | 簡(jiǎn)潔高效,支持復(fù)雜對(duì)象 | 需導(dǎo)入第三方依賴,需實(shí)現(xiàn)Serializable | 普通Java項(xiàng)目,復(fù)雜對(duì)象拷貝 |
Gson序列化 | 無(wú)接口要求,代碼簡(jiǎn)潔,常用依賴 | 性能一般,需導(dǎo)入Gson依賴 | 已有Gson依賴,中小規(guī)模數(shù)據(jù)拷貝 |
Jackson序列化 | Spring原生支持,性能優(yōu),支持復(fù)雜對(duì)象 | 需處理異常,代碼略繁瑣 | Spring Boot/Cloud項(xiàng)目,企業(yè)級(jí)開發(fā) |
四、全文核心總結(jié)(面試高頻考點(diǎn))
1. 淺拷貝拷貝地址,深拷貝拷貝數(shù)據(jù);淺拷貝共享底層資源,深拷貝完全獨(dú)立,這是兩者的核心區(qū)別;
2. Java中直接賦值是淺拷貝,new關(guān)鍵字+構(gòu)造函數(shù)、clone()、序列化等方式可實(shí)現(xiàn)深拷貝;
3. 重寫clone()方法易踩“偽深拷貝”坑——引用類型需遞歸clone,否則僅拷貝引用地址;
4. 實(shí)際開發(fā)中,優(yōu)先選擇Gson/Jackson序列化(Spring項(xiàng)目選Jackson)或Apache Commons Lang,兼顧簡(jiǎn)潔性和實(shí)用性;
5. 無(wú)需數(shù)據(jù)隔離時(shí),淺拷貝更高效;需要數(shù)據(jù)隔離(如多線程、數(shù)據(jù)修改)時(shí),必須使用深拷貝,避免出現(xiàn)數(shù)據(jù)安全問題。
最后提醒:拷貝操作的核心是“數(shù)據(jù)隔離”,選擇哪種方式,關(guān)鍵看“對(duì)象復(fù)雜度、性能要求、項(xiàng)目依賴”——理解了淺拷貝和深拷貝的本質(zhì),無(wú)論遇到哪種場(chǎng)景,都能快速選型、避免踩坑。
到此這篇關(guān)于Java拷貝之一文徹底分清淺拷貝與深拷貝的文章就介紹到這了,更多相關(guān)java淺拷貝與深拷貝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
LinkedBlockingQueue鏈?zhǔn)阶枞?duì)列的使用和原理解析
這篇文章主要介紹了LinkedBlockingQueue鏈?zhǔn)阶枞?duì)列的使用和原理解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
解決SpringBoot項(xiàng)目在啟動(dòng)后自動(dòng)關(guān)閉的問題
今天搭建了一個(gè)SpringBoot項(xiàng)目,但是在啟動(dòng)之后就自行關(guān)閉了,下面通過(guò)本文給大家介紹SpringBoot項(xiàng)目在啟動(dòng)后自動(dòng)關(guān)閉問題及解決方法,需要的朋友可以參考下2023-08-08
Java中l(wèi)ambda表達(dá)式的基本運(yùn)用
大家好,本篇文章主要講的是Java中l(wèi)ambda表達(dá)式的基本運(yùn)用,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01
SpringBoot通過(guò)自定義注解與異步來(lái)管理日志流程
實(shí)現(xiàn)日志管理說(shuō)實(shí)話方式還挺多,個(gè)人使用過(guò)直接在Controller代碼里面寫、AOP+自定義注解、ConstraintValidator。本文主要和大家講的是自定義注解與異步來(lái)管理日志流程,感興趣的可以了解一下2023-03-03
Mybatis-Spring連接mysql 8.0配置步驟出錯(cuò)的解決方法
這篇文章主要為大家詳細(xì)介紹了Mybatis-Spring連接mysql 8.0配置步驟出錯(cuò)的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-06-06
springMVC使用ajaxFailUpload上傳圖片的方法
這篇文章主要介紹了springMVC使用ajaxFailUpload上傳圖片的相關(guān)知識(shí),代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-06-06
詳解springmvc控制登錄用戶session失效后跳轉(zhuǎn)登錄頁(yè)面
本篇文章主要介紹了springmvc控制登錄用戶session失效后跳轉(zhuǎn)登錄頁(yè)面,session一旦失效就需要重新登陸,有興趣的同學(xué)可以了解一下。2017-01-01
什么是 Java 的 CyclicBarrier(代碼示例)
CyclicBarrier 是多線程協(xié)同的利器,適合需要多次同步的場(chǎng)景,本文通過(guò)代碼示例講解什么是 Java 的 CyclicBarrier,感興趣的朋友一起看看吧2025-03-03

