標籤:位元組 png 選擇 throw 分享圖片 ali throws 兩種 setname
一.淺拷貝(shallow copy)
1.如何?淺拷貝?
Object類 是所有類的直接或間接父類,Object中存在clone方法,如下
protected native Object clone() throws CloneNotSupportedException;
如果想要使一個類的對象能夠調用clone方法 ,則需要實現Cloneable介面, 並重寫 clone方法:
public class Student implements Cloneable{ private int sno ; private String name; //getter ,setter 省略 @Override public Object clone() throws CloneNotSupportedException { Student s = null; try{ s = (Student)super.clone(); }catch (Exception e){ e.printStackTrace(); } return s; }}
現在測試clone方法:
@Test public void test04() throws CloneNotSupportedException { //建立Student對象 Student s1 = new Student(); s1.setSno(1); s1.setName("Rye"); //通過clone 拷貝一個對象 Student s2 = (Student)s1.clone(); System.out.println("s1:"+s1); System.out.println("s2:"+s2); System.out.println("s1 == s2 ? ==> "+(s1 == s2)); }
按照預期,複製出的對象s2中的欄位值應該與s1相同,但與s1對應的對象不在同一塊記憶體空間,結果如下:
s1:Student{sno=1, name=‘Rye‘}s2:Student{sno=1, name=‘Rye‘}s1 == s2 ? ==> false
View Code
此時如果修改 s1中的sno為2,那麼會不會影響到s2中的sno呢?
//修改s1中的snos1.setSno(2);
結果如下:
s1:Student{sno=2, name=‘Rye‘}s2:Student{sno=1, name=‘Rye‘}
View Code
此時看似已經完成了 copy, s1 與 s2有著自己不同的值,但如果為Student中新增了Teacher類型的成員變數,結果還是跟上面一樣嗎?讓我們改造下代碼:
public class Teacher { private int tno; private String name; //getter setter省略... }
public class Student implements Cloneable{ private int sno ; private String name; private Teacher teacher; //getter ,setter ,toString 省略... @Override public Object clone() throws CloneNotSupportedException { Student s = null; try{ s = (Student)super.clone(); }catch (Exception e){ e.printStackTrace(); } return s; } }
此時測試代碼如下:
@Test public void test02() throws CloneNotSupportedException { Student student1 = new Student(); student1.setSno(1); student1.setName("Rye"); Teacher teacher = new Teacher(); teacher.setTno(1); teacher.setName("LinTong"); student1.setTeacher(teacher); Student student2 = (Student)student1.clone(); System.out.println("student1:"+student1); System.out.println("student2:"+student2); System.out.println("student1 == student2 ? ==> "+ (student1 ==student2)); System.out.println("student1.teacher == student2.teacher ? ==> "+ (student1.getTeacher() ==student2.getTeacher())); }
運行結果如下:
student1:Student{sno=1, name=‘Rye‘, teacher=Teacher{tno=1, name=‘LinTong‘}}student2:Student{sno=1, name=‘Rye‘, teacher=Teacher{tno=1, name=‘LinTong‘}}student1 == student2 ? ==> falsestudent1.teacher == student2.teacher ? ==> true
View Code
由此可見,此時經過clone產生的student2, 與 student1.二者中的teacher欄位, 指向同一塊記憶體空間;
那麼可能會有人問,這會有什麼影響嗎?
我們拷貝的目的,更多的時候是希望獲得全新並且值相同的對象,操作原始對象或拷貝的新對象,對彼此之間互不影響;
此時我們修改student1中teacher的tno ,如下:
//修改teacher中的 tno值為2student1.getTeacher().setTno(2);
再次運行test:
student1:Student{sno=1, name=‘Rye‘, teacher=Teacher{tno=2, name=‘LinTong‘}}student2:Student{sno=1, name=‘Rye‘, teacher=Teacher{tno=2, name=‘LinTong‘}}student1 == student2 ? ==> falsestudent1.teacher == student2.teacher ? ==> true
View Code
此時發現,student2中的teacher的tno ,也跟著變化了.
變化的原因是:通過student1執行clone時,基本類型會完全copy一份到student2對應對象記憶體空間中, 但是對於Teacher對象僅僅是copy了一份Teacher的引用而已.
而student1 與 student2的引用 指向的是同一塊堆記憶體,因此不論是通過student1或是student2修改teacher 都會影響另外一個;
通過圖會更直觀一些:
2.淺拷貝中參考型別的變數拷貝的是對象的引用 , 可通過如下思路解決:
Teacher類中也覆寫clone方法:
@Override protected Object clone() { Teacher teacher = null; try { teacher = (Teacher)super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return teacher; }
修改Student中的clone方法,如下:
@Override public Object clone() { Student s = null; try{ s = (Student)super.clone(); Teacher t = (Teacher)this.teacher.clone(); s.setTeacher(t); }catch (Exception e){ e.printStackTrace(); } return s; }
此時再次運行test:
student1:Student{sno=1, name=‘Rye‘, teacher=Teacher{tno=2, name=‘LinTong‘}}student2:Student{sno=1, name=‘Rye‘, teacher=Teacher{tno=1, name=‘LinTong‘}}student1 == student2 ? ==> falsestudent1.teacher == student2.teacher ? ==> false
View Code
由此可見,在copy Student的同時 將Teacher也進行了修改,:
目前來看是滿足了我們的需求,但是如果Teacher類中,同樣也有別的參考型別 的成員變數呢?
那麼就同樣需要一直覆寫clone方法,如果這個關係不是特多還可以接受,如果參考關聯性很複雜就會顯得代碼繁瑣;
此時應該使用序列化完成深度拷貝;
二.深拷貝(deep copy)
使用序列化完成深拷貝
深拷貝是利用物件流程,將對象序列化,再還原序列化得出新的對象. 因此首先需要實現序列化介面,如下:
public class Student implements Serializable{ private static final long serialVersionUID = -2232725257771333130L; private int sno ; private String name; private Teacher teacher;
//getter ,setter,toString()省略...}
Teacher也要實現序列化介面:
public class Teacher implements Serializable{ private static final long serialVersionUID = 4477679176385287943L; private int tno; private String name;
//getter ,setter,toString()省略...
}
工具方法:
//工具方法 public Object cloneObject(Object object) throws IOException, ClassNotFoundException { //將對象序列化 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(object); //將位元組還原序列化 ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); Object obj = objectInputStream.readObject(); return obj; }
測試類別:
public void test05() throws IOException, ClassNotFoundException { Student student1 = new Student(); student1.setSno(1); student1.setName("Rye"); Teacher teacher = new Teacher(); teacher.setTno(1); teacher.setName("LinTong"); student1.setTeacher(teacher); Student student2 = (Student)cloneObject(student1); //修改teacher中的 tno值為2 student1.getTeacher().setTno(2); System.out.println("student1:"+student1); System.out.println("student2:"+student2); System.out.println("student1 == student2 ? ==> "+ (student1 ==student2)); System.out.println("student1.teacher == student2.teacher ? ==> "+ (student1.getTeacher() ==student2.getTeacher())); }
如果Teacher類或者Student類沒有實現序列化介面,則執行時會報異常,如下:
java.io.NotSerializableException: com.example.test.Teacher
在都實現了Serializable介面的情況下,運行結果如下:
student1:Student{sno=1, name=‘Rye‘, teacher=Teacher{tno=2, name=‘LinTong‘}}student2:Student{sno=1, name=‘Rye‘, teacher=Teacher{tno=1, name=‘LinTong‘}}student1 == student2 ? ==> falsestudent1.teacher == student2.teacher ? ==> false
View Code
由此通過物件流程的方式,成功完成了深度拷貝;
三.重寫clone方法 與 通過序列化 兩種拷貝方式比較:
clone方法:
優點:速度快,效率高
缺點:在對象引用比較深時,使用此方式比較繁瑣
通過序列化:
優點:非常簡便的就可以完成深度copy
缺點:由於序列化的過程需要跟磁碟打交道,因此效率會低於clone方式
如何抉擇?
實際開發中,根據兩種方式的優缺點進行選擇即可!
淺談關於java中的深淺拷貝