Java中深拷貝,淺拷貝與引用拷貝的區(qū)別詳解
引用拷貝
引用拷貝: 引用拷貝不會在堆上創(chuàng)建一個新的對象,只 會在棧上生成一個新的引用地址,最終指向依然是堆上的同一個對象。
//實體類 public class Person{ public String name;//姓名 public int height;//身高 public StringBuilder something; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public StringBuilder getSomething() { return something; } public void setSomething(StringBuilder something) { this.something = something; } public Person(String name, int height, StringBuilder something) { this.name = name; this.height = height; this.something = something; } } //測試類 public class copyTest { public static void main(String[] args) { Person p1 = new Person("小張", 180, new StringBuilder("今天天氣很好")); Person p2 = p1; System.out.println("對象是否相等:"+ (p1 == p2)); System.out.println("p1 屬性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething()); System.out.println("p2 屬性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething()); // change p1.name="小王"; p1.height = 200; p1.something.append(",適合出去玩"); System.out.println("...after p1 change...."); System.out.println("p1 屬性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething()); System.out.println("p2 屬性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething()); } }
結(jié)果:
對象是否相等:true
p1 屬性值=小張,180,今天天氣很好
p2 屬性值=小張,180,今天天氣很好
...after p1 change....
p1 屬性值=小王,200,今天天氣很好,適合出去玩
p2 屬性值=小王,200,今天天氣很好,適合出去玩
before change:
after change:
我們可以看出 由于2個引用p1,p2 都是指向堆中同一個對象,所以2個對象是相等的,修改了對象p1,會影響到對象p2
需要注意的
- name屬性,雖然她是引用類型,但她同時也是String類型,不可變,對其修改,JVM會默認在堆上創(chuàng)建新的內(nèi)存空間,再重新賦值
int weight=180;
是成員變量,存放在堆中,不是所有的基本類型變量 都存放在JVM棧中
注意與這篇文章得區(qū)分開來, int num1 = 10;
是基本類型的局部變量,存放在棧中
淺拷貝
淺拷貝 :淺拷貝會在堆上創(chuàng)建一個新的對象,新對象和原對象不等,但是新對象的屬性和老對象相同。
其中:
- 如果屬性是基本類型(int,double,long,boolean等),拷貝的就是基本類型的值。
- 如果屬性是引用類型(除了基本類型都是引用類型),拷貝的就是引?數(shù)據(jù)類型變量的地址值,?對于引?類型變量指向的堆中的對象不會拷貝。
如何實現(xiàn)淺拷貝呢?也很簡單,就是在需要拷貝的類上實現(xiàn)Cloneable接口并重寫其clone()方法。
@Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }
在使用的時候直接調(diào)用類的clone()方法即可
//實體類 繼承Cloneable public class Person implements Cloneable{ public String name;//姓名 public int height;//身高 public StringBuilder something; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public StringBuilder getSomething() { return something; } public void setSomething(StringBuilder something) { this.something = something; } public Person(String name, int height, StringBuilder something) { this.name = name; this.height = height; this.something = something; } @Override public Person clone() throws CloneNotSupportedException { return (Person) super.clone(); } } //測試類 public class shallowCopyTest { public static void main(String[] args) throws CloneNotSupportedException { Person p1 = new Person("小張", 180, new StringBuilder("今天天氣很好")); Person p2 = p1.clone(); System.out.println("對象是否相等:"+ (p1 == p2)); System.out.println("p1 屬性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething()); System.out.println("p2 屬性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething()); // change p1.setName("小王"); p1.setHeight(200); p1.getSomething().append(",適合出去玩"); System.out.println("...after p1 change...."); System.out.println("p1 屬性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething()); System.out.println("p2 屬性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething()); } }
結(jié)果:
對象是否相等:false
p1 屬性值=小張,180,今天天氣很好
p2 屬性值=小張,180,今天天氣很好
...after p1 change....
p1 屬性值=小王,200,今天天氣很好,適合出去玩
p2 屬性值=小張,180,今天天氣很好,適合出去玩
before change:
after change:
我們可以看出:
- 當(dāng)我們修改對象p1的weight屬性時,由于p2的height屬性 是直接復(fù)制修改前的p1的height屬性,所以還是180。
- 當(dāng)我們修改對象p1的name屬性 時,String name指向一個新的內(nèi)存空間,但對象p2的name還是指向舊的內(nèi)存空間,所以對象p2的name屬性還是"小張"。
- 由于對象p1的something屬性和對象p2的something屬性指向是同一個內(nèi)存空間,當(dāng)我們修改對象p1的something屬性,會影響到對象p2的something屬性,所以對象p2的something屬性變?yōu)?quot;今天天氣很好,適合出去玩"。
深拷貝
深拷貝 :完全拷貝?個對象,在堆上創(chuàng)建一個新的對象,拷貝被拷貝對象的成員變量的值,同時堆中的對象也會拷貝。
需要重寫clone方法
@Override public Person clone() throws CloneNotSupportedException { //return (Person) super.clone(); Person person = (Person) super.clone(); person.setSomething( new StringBuilder(person.getSomething()));//單獨為引用類型clone return person; }
shallowCopyTest測試類的結(jié)果:
對象是否相等:false
p1 屬性值=小張,180,今天天氣很好
p2 屬性值=小張,180,今天天氣很好
...after p1 change....
p1 屬性值=小王,200,今天天氣很好,適合出去玩
p2 屬性值=小張,180,今天天氣很好
這時候?qū)ο髉1和對象p2互不干擾了
before change:
after change:
但這樣也有個小問題,對象每有一個引用類型,我們都得重寫其clone方法,這樣會非常麻煩,因此我們還可以借助序列化來實現(xiàn)對象的深拷貝
//實體類 繼承Cloneable public class Person implements Serializable{ public String name;//姓名 public int height;//身高 public StringBuilder something; ...//省略 getter setter public Object deepClone() throws Exception{ // 序列化 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); // 反序列化 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } } //測試類,這邊類名筆者就不換了,在之前的基礎(chǔ)上改改 public class shallowCopyTest { public static void main(String[] args) throws Exception { Person p1 = new Person("小張", 180, new StringBuilder("今天天氣很好")); Person p2 = (Person)p1.deepClone(); System.out.println("對象是否相等:"+ (p1 == p2)); System.out.println("p1 屬性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething()); System.out.println("p2 屬性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething()); // change p1.setName("小王"); p1.setHeight(200); p1.getSomething().append(",適合出去玩"); System.out.println("...after p1 change...."); System.out.println("p1 屬性值=" + p1.getName()+ ","+ p1.getHeight() + ","+ p1.getSomething()); System.out.println("p2 屬性值=" + p2.getName()+ ","+ p2.getHeight() + ","+ p2.getSomething()); } }
這樣也會得到深拷貝的結(jié)果
小結(jié)
引用拷貝: 引用拷貝不會在堆上創(chuàng)建一個新的對象,只 會在棧上生成一個新的引用地址,最終指向依然是堆上的同一個對象。
淺拷貝 :淺拷貝會在堆上創(chuàng)建一個新的對象,新對象和原對象不等,但是新對象的屬性和老對象相同。
其中:
如果屬性是基本類型(int,double,long,boolean等),拷貝的就是基本類型的值。
如果屬性是引用類型(除了基本類型都是引用類型),拷貝的就是引?數(shù)據(jù)類型變量的地址值,?對于引?類型變量指向的堆中的對象不會拷貝。
深拷貝 :完全拷貝?個對象,在堆上創(chuàng)建一個新的對象,拷貝被拷貝對象的成員變量的值,同時堆中的對象也會拷貝。
以上就是Java中深拷貝,淺拷貝與引用拷貝的區(qū)別詳解的詳細內(nèi)容,更多關(guān)于Java拷貝的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于Springboot+Junit+Mockito做單元測試的示例
本篇文章主要介紹了基于Springboot+Junit+Mockito做單元測試的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02SpringBoot項目實現(xiàn)短信發(fā)送接口開發(fā)的實踐
本文主要介紹了SpringBoot項目實現(xiàn)短信發(fā)送接口開發(fā)的實踐,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-10-10SpringBoot整合騰訊云COS對象存儲實現(xiàn)文件上傳的示例代碼
本文主要介紹了SpringBoot整合騰訊云COS對象存儲實現(xiàn)文件上傳的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12淺析Java 常用的 4 種加密方式(MD5+Base64+SHA+BCrypt)
這篇文章主要介紹了Java 常用的 4 種加密方式(MD5+Base64+SHA+BCrypt),本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10java對象對比之comparable和comparator的區(qū)別
今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識,文章圍繞著comparable和comparator的區(qū)別展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下2021-06-06