深拷貝與淺拷貝的問題,也是面試中的常客,雖然大家都知道兩者表現形式不同點在哪里,但是很少去深究其底層原理,也不知道怎么才能優雅的實作一個深拷貝,其實作業中也常常需要實作深拷貝,今天一燈就帶大家一塊深入剖析一下深拷貝與淺拷貝的實作原理,并手把手教你怎么優雅的實作深拷貝,
1. 什么是深拷貝與淺拷貝
淺拷貝: 只拷貝堆疊記憶體中的資料,不拷貝堆記憶體中資料,
深拷貝: 既拷貝堆疊記憶體中的資料,又拷貝堆記憶體中的資料,
2. 淺拷貝的實作原理
由于淺拷貝只拷貝了堆疊記憶體中資料,堆疊記憶體中存盤的都是基本資料型別,堆記憶體中存盤了陣列、參考資料型別等,

使用代碼驗證一下:
想要實作clone功能,需要實作 Cloneable 介面,并重寫 clone 方法,
- 先創建一個用戶類
// 用戶的物體類,用作驗證
public class User implements Cloneable {
private String name;
// 每個用戶都有一個作業
private Job job;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Job getJob() {
return job;
}
public void setJob(Job job) {
this.job = job;
}
@Override
public User clone() throws CloneNotSupportedException {
User user = (User) super.clone();
return user;
}
}
- 再創建一個作業類
// 作業的物體類,并沒有實作Cloneable介面
public class Job {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
- 測驗淺拷貝
/**
* @author 一燈架構
* @apiNote Java淺拷貝示例
**/
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
// 1. 創建用戶物件,{"name":"一燈架構","job":{"content":"開發"}}
User user1 = new User();
user1.setName("一燈架構");
Job job1 = new Job();
job1.setContent("開發");
user1.setJob(job1);
// 2. 拷貝用戶物件,name修改為"張三",作業內容修改"測驗"
User user2 = user1.clone();
user2.setName("張三");
Job job2 = user2.getJob();
job2.setContent("測驗");
// 3. 輸出結果
System.out.println("user原物件= " + user1);
System.out.println("user拷貝物件= " + user2);
}
}
輸出結果:
user原物件= {"name":"一燈架構","job":{"content":"測驗"}}
user拷貝物件= {"name":"張三","job":{"content":"測驗"}}
從結果中可以看出,物件拷貝把name修改為”張三“,原物件并沒有變,name是String型別,是基本資料型別,存盤在堆疊記憶體中,物件拷貝了一份新的堆疊記憶體資料,修改并不會影響原物件,
然后物件拷貝把Job中content修改為”測驗“,原物件也跟著變了,原因是Job是參考型別,存盤在堆記憶體中,物件拷貝和原物件指向的同一個堆記憶體的地址,所以修改會影響到原物件,
3. 深拷貝的實作原理
深拷貝是既拷貝堆疊記憶體中的資料,又拷貝堆記憶體中的資料,

實作深拷貝有很多種方法,下面就詳細講解一下,看使用哪種方式更方便快捷,
3.1 實作Cloneable介面
通過實作Cloneable介面來實作深拷貝是最常見的,
想要實作clone功能,需要實作Cloneable介面,并重寫clone方法,
- 先創建一個用戶類
// 用戶的物體類,用作驗證
public class User implements Cloneable {
private String name;
// 每個用戶都有一個作業
private Job job;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Job getJob() {
return job;
}
public void setJob(Job job) {
this.job = job;
}
@Override
public User clone() throws CloneNotSupportedException {
User user = (User) super.clone();
// User物件中所有參考型別屬性都要執行clone方法
user.setJob(user.getJob().clone());
return user;
}
}
- 再創建一個作業類
// 作業的物體類,需要實作Cloneable介面
public class Job implements Cloneable {
private String content;
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
protected Job clone() throws CloneNotSupportedException {
return (Job) super.clone();
}
}
- 測驗淺拷貝
/**
* @author 一燈架構
* @apiNote Java深拷貝示例
**/
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
// 1. 創建用戶物件,{"name":"一燈架構","job":{"content":"開發"}}
User user1 = new User();
user1.setName("一燈架構");
Job job1 = new Job();
job1.setContent("開發");
user1.setJob(job1);
// 2. 拷貝用戶物件,name修改為"張三",作業內容修改"測驗"
User user2 = user1.clone();
user2.setName("張三");
Job job2 = user2.getJob();
job2.setContent("測驗");
// 3. 輸出結果
System.out.println("user原物件= " + user1);
System.out.println("user拷貝物件= " + user2);
}
}
輸出結果:
user原物件= {"name":"一燈架構","job":{"content":"開發"}}
user拷貝物件= {"name":"張三","job":{"content":"測驗"}}
從結果中可以看出,user拷貝物件修改了name屬性和Job物件中內容,都沒有影響到原物件,實作了深拷貝,
通過實作Cloneable介面的方式來實作深拷貝,是Java中最常見的實作方式,
缺點是: 比較麻煩,需要所有物體類都實作Cloneable介面,并重寫clone方法,如果物體類中新增了一個參考物件型別的屬性,還需要添加到clone方法中,如果繼任者忘了修改clone方法,相當于挖了一個坑,
3.2 使用JSON字串轉換
實作方式就是:
- 先把user物件轉換成json字串
- 再把json字串轉換成user物件
這是個偏方,但是偏方治大病,使用起來非常方便,一行代碼即可實作,
下面使用fastjson實作,使用Gson、Jackson也是一樣的:
import com.alibaba.fastjson.JSON;
/**
* @author 一燈架構
* @apiNote Java深拷貝示例
**/
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
// 1. 創建用戶物件,{"name":"一燈架構","job":{"content":"開發"}}
User user1 = new User();
user1.setName("一燈架構");
Job job1 = new Job();
job1.setContent("開發");
user1.setJob(job1);
//// 2. 拷貝用戶物件,name修改為"張三",作業內容修改"測驗"
User user2 = JSON.parseObject(JSON.toJSONString(user1), User.class);
user2.setName("張三");
Job job2 = user2.getJob();
job2.setContent("測驗");
// 3. 輸出結果
System.out.println("user原物件= " + JSON.toJSONString(user1));
System.out.println("user拷貝物件= " + JSON.toJSONString(user2));
}
}
輸出結果:
user原物件= {"name":"一燈架構","job":{"content":"開發"}}
user拷貝物件= {"name":"張三","job":{"content":"測驗"}}
從結果中可以看出,user拷貝物件修改了name屬性和Job物件中內容,并沒有影響到原物件,實作了深拷貝,
3.3 集合實作深拷貝
再說一下Java集合怎么實作深拷貝?
其實非常簡單,只需要初始化新物件的時候,把原物件傳入到新物件的構造方法中即可,
以最常用的ArrayList為例:
/**
* @author 一燈架構
* @apiNote Java深拷貝示例
**/
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
// 1. 創建原物件
List<User> userList = new ArrayList<>();
// 2. 創建深拷貝物件
List<User> userCopyList = new ArrayList<>(userList);
}
}
我是「一燈架構」,如果本文對你有幫助,歡迎各位小伙伴點贊、評論和關注,感謝各位老鐵,我們下期見

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/528704.html
標籤:Java
上一篇:萬字詳解JVM,讓你一文吃透
