Java中物件的深複製和淺複製詳解
1.淺複製與深複製概念
⑴淺複製(淺克隆)
被複制物件的所有變數都含有與原來的物件相同的值,而所有的對其他物件的引用仍然指向原來的物件。換言之,淺複製僅僅複製所考慮的物件,而不復制它所引用的物件。
⑵深複製(深克隆)
被複制物件的所有變數都含有與原來的物件相同的值,除去那些引用其他物件的變數。那些引用其他物件的變數將指向被複制過的新物件,而不再是原有的那些被引用的物件。換言之,深複製把要複製的物件所引用的物件都複製了一遍。
2.Java的clone()方法
⑴clone方法將物件複製了一份並返回給呼叫者。一般而言,clone()方法滿足:
①對任何的物件x,都有x.clone() !=x//克隆物件與原物件不是同一個物件
②對任何的物件x,都有x.clone().getClass()= =x.getClass()//克隆物件與原物件的型別一樣
③如果物件x的equals()方法定義恰當,那麼x.clone().equals(x)應該成立。
⑵Java中物件的克隆
①為了獲取物件的一份拷貝,我們可以利用Object類的clone()方法。
②在派生類中覆蓋基類的clone()方法,並宣告為public。
③在派生類的clone()方法中,呼叫super.clone()。
④在派生類中實現Cloneable介面。
請看如下程式碼:
public class Student implements Cloneable { String name; int age; Student(String name,int age) { this.name=name; this.age=age; } public Object clone() { Object o=null; try { o=(Student)super.clone();//Object 中的clone()識別出你要複製的是哪一個物件。 } catch(CloneNotSupportedException e) { System.out.println(e.toString()); } return o; } public static void main(String[] args) { Student s1=new Student("zhangsan",18); Student s2=(Student)s1.clone(); s2.name="lisi"; s2.age=20; //修改學生2後,不影響學生1的值。 System.out.println("name="+s1.name+","+"age="+s1.age); System.out.println("name="+s2.name+","+"age="+s2.age); } }
說明:
①為什麼我們在派生類中覆蓋Object的clone()方法時,一定要呼叫super.clone()呢?在執行時刻,Object中的clone() 識別出你要複製的是哪一個物件,然後為此物件分配空間,並進行物件的複製,將原始物件的內容一一複製到新物件的儲存空間中。
②繼承自java.lang.Object類的clone()方法是淺複製。以下程式碼可以證明之。
class Professor { String name; int age; Professor(String name,int age) { this.name=name; this.age=age; } } public class Student implements Cloneable { String name;// 常量物件。 int age; Professor p;// 學生1和學生2的引用值都是一樣的。 Student(String name,int age,Professor p) { this.name=name; this.age=age; this.p=p; } public Object clone() { Student o=null; try { o=(Student)super.clone(); } catch(CloneNotSupportedException e) { System.out.println(e.toString()); } o.p=(Professor)p.clone(); return o; } public static void main(String[] args) { Professor p=new Professor("wangwu",50); Student s1=new Student("zhangsan",18,p); Student s2=(Student)s1.clone(); s2.p.name="lisi"; s2.p.age=30; System.out.println("name="+s1.p.name+","+"age="+s1.p.age); System.out.println("name="+s2.p.name+","+"age="+s2.p.age); //輸出結果學生1和2的教授成為lisi,age為30。 } }
那應該如何實現深層次的克隆,即修改s2的教授不會影響s1的教授?程式碼改進如下。
改進使學生1的Professor不改變(深層次的克隆)
class Professor implements Cloneable { String name; int age; Professor(String name,int age) { this.name=name; this.age=age; } public Object clone() { Object o=null; try { o=super.clone(); } catch(CloneNotSupportedException e) { System.out.println(e.toString()); } return o; } } public class Student implements Cloneable { String name; int age; Professor p; Student(String name,int age,Professor p) { this.name=name; this.age=age; this.p=p; } public Object clone() { Student o=null; try { o=(Student)super.clone(); } catch(CloneNotSupportedException e) { System.out.println(e.toString()); } //對引用的物件也進行復制 o.p=(Professor)p.clone(); return o; } public static void main(String[] args) { Professor p=new Professor("wangwu",50); Student s1=new Student("zhangsan",18,p); Student s2=(Student)s1.clone(); s2.p.name="lisi"; s2.p.age=30; //學生1的教授不 改變。 System.out.println("name="+s1.p.name+","+"age="+s1.p.age); System.out.println("name="+s2.p.name+","+"age="+s2.p.age); } }
3.利用序列化來做深複製(主要是為了避免重寫比較複雜物件的深複製的clone()方法,也可以程式實現斷點續傳等等功能)
把物件寫到流裡的過程是序列化(Serilization)過程,但是在Java程式師圈子裡又非常形象地稱為“冷凍”或者“醃鹹菜(picking)” 過程;而把物件從流中讀出來的並行化(Deserialization)過程則叫做 “解凍”或者“回鮮(depicking)”過程。
應當指出的是,寫在流裡的是物件的一個拷貝,而原物件仍然存在於JVM裡面,因此“醃成鹹菜”的只是物件的一個拷貝,Java鹹菜還可以回鮮。
在Java語言裡深複製一個物件,常常可以先使物件實現Serializable介面,然後把物件(實際上只是物件的一個拷貝)寫到一個流裡(醃成鹹菜),再從流裡讀出來(把鹹菜回鮮),便可以重建物件。
如下為深複製原始碼。
public Object deepClone() { //將物件寫到流裡 ByteArrayOutoutStream bo=new ByteArrayOutputStream(); ObjectOutputStream oo=new ObjectOutputStream(bo); oo.writeObject(this); //從流裡讀出來 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return(oi.readObject()); }
這樣做的前提是物件以及物件內部所有引用到的物件都是可序列化的,否則,就需要仔細考察那些不可序列化的物件或屬性可否設成transient,從而將之排除在複製過程之外。上例程式碼改進如下。
class Teacher implements Serializable{ String name; int age; public void Teacher(String name,int age){ this.name=name; this.age=age; } } public class Student implements Serializable{ String name;//常量物件 int age; Teacher t;//學生1和學生2的引用值都是一樣的。 public void Student(String name,int age,Teacher t){ this.name=name; this.age=age; this.p=p; } public Object deepClone() throws IOException, OptionalDataException,ClassNotFoundException{//將物件寫到流裡 ByteArrayOutoutStream bo=new ByteArrayOutputStream(); ObjectOutputStream oo=new ObjectOutputStream(bo); oo.writeObject(this);//從流裡讀出來 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return(oi.readObject()); } public static void main(String[] args){ Teacher t=new Teacher("tangliang",30); Student s1=new Student("zhangsan",18,t); Student s2=(Student)s1.deepClone(); s2.t.name="tony"; s2.t.age=40; //學生1的老師不改變 System.out.println("name="+s1.t.name+","+"age="+s1.t.age); } }
相關文章
- 淺談JS中物件的淺複製和深複製JS物件
- JS物件複製:深複製和淺複製JS物件
- C#中的物件深複製和淺複製C#物件
- 詳談Javascript中的深複製和淺複製JavaScript
- Java 中的深複製和淺複製你瞭解嗎?Java
- Java引用複製、淺複製、深複製Java
- JavaScript 淺複製和深複製JavaScript
- 對於複製普通物件 深複製和淺複製是否一樣物件
- 淺複製和深複製的概念與值複製和指標複製(引用複製)有關 淺複製 “指標複製 深複製 值複製指標
- JavaScript中的淺複製與深複製JavaScript
- go slice深複製和淺複製Go
- python 深複製和淺複製Python
- go的深複製跟淺複製Go
- python深複製和淺複製的區別Python
- c#淺複製與深複製C#
- python 淺複製、深複製坑Python
- js 淺複製和深複製的區別和應用JS
- js 淺拷貝(淺複製、淺克隆)、深拷貝(深複製、深克隆)JS
- js物件深複製JS物件
- python 的深淺複製Python
- 25. 深淺複製
- Day 7.5 資料型別總結 + 複製 淺複製 深複製資料型別
- vue物件的深層複製Vue物件
- 淺複製、深複製與序列化【初級Java必需理解的概念】Java
- js 實現深複製/深複製JS
- Python列表的深淺複製Python
- 淺顯直白的Python深複製與淺複製區別說明Python
- Python中的賦值與淺複製與深複製之間的關係Python賦值
- 聊一聊web前端那些事兒,關於深複製和淺複製Web前端
- 資料共享(淺複製)與資料獨立(深複製)
- 【JavaScript】聊一聊js中的淺複製與深複製與手寫實現JavaScriptJS
- JS 物件如何實現深複製JS物件
- MySQL的主從複製、半同步複製、主主複製詳解MySql
- js 深複製JS
- 設計模式-原型模式(Prototype)【重點:淺複製與深複製】設計模式原型
- 深入理解JavaScript之深淺複製JavaScript
- python 深/淺複製及其區別Python
- 一文帶你瞭解js資料儲存及深複製(深拷貝)與淺複製(淺拷貝)JS