CV程序員:熟練使用ctrl+c和ctrl+v或者command+c和command+v的程序員,CV程序員分兩種,第一種見多識廣,遊走在各類搜索引擎、博客以及項目中,搜尋着本身想要的代碼,第一種是把本身的代碼從一個類複製到另外一個類。java
我以爲若是是做爲一個剛起步的程序員來講,CV別人的代碼挺好的,由於至少他具有了搜索以及本身解決問題的能力。這種能力和學習方式是很是重要的,由於不少問題都須要本身去解決一遍纔會慢慢積累經驗,向更高的水平進階,可是要注意的是懂得回顧和反思,雖然已經把代碼CV過來了,可是過後須要思考代碼爲何要這麼寫,而不是解決了就丟一旁。日積跬步,終有一天你的代碼也會被別人用來CV。而若是是CV本身的代碼就要注意了,由於你在寫着冗餘的代碼,須要思考爲何有這麼多重複的代碼,是否能夠作一些抽象設計或者運用一些設計模式來避免代碼的冗餘。程序員
今天要講的是CV程序員都要掌握的對象拷貝,主要是從如下三個方面開始講解:設計模式
克隆什麼?markdown
怎麼克隆?ide
爲何要克隆?學習
深拷貝和淺拷貝區別是什麼?測試
要講克隆,先弄清楚第一個問題:克隆什麼東西?這還用回答嗎,固然是克隆對象。可是到底克隆的是什麼類型的對象?this
咱們知道在 Java中基本數據類型和引用數據類型之分,基本數據類型與引用數據類型最本質的區別是:搜索引擎
而咱們所說的克隆,能夠分這兩種類型的對象來討論。spa
偷偷引入一個知識點:
裝箱:將基本數據類型轉換爲引用數據類型。好比int轉化爲Integer。
拆箱:將引用數據類型轉換爲基本數據類型。好比Integer轉化爲int。
弄清楚了要克隆什麼,那麼咱們來看看怎麼克隆。仍是分爲克隆基本類型對象和引用類型對象:
下面是一個基本類型的克隆示例:
public static void main(String[] args) { int a = 1; // 用"="克隆 int b = a; System.out.println("a=" + a); System.out.println("b=" + b); // 測試是否修改了a的值 b = 2; System.out.println("修改了b的值後,a=" + a); System.out.println("修改了b的值後,b=" + b); } 複製代碼
運行結果
對於基本類型來講,直接用「=」就能完成克隆,而且這樣克隆出來的對象是徹底獨立於原來的對象的。
引用類型對象的克隆有兩種方法
下面先定義一個類:
public class School implements Cloneable { /** * 校名 */ String name; /** * 校長 */ String president; public School(String name, String president) { this.name = name; this.president = president; } public void setName(String name) { this.name = name; } public void setPresident(String president) { this.president = president; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "School:[name=" + name + "," + "president=" + president + "]"; } } 複製代碼
示例
public static void main(String[] args) throws CloneNotSupportedException { School school = new School("家裏蹲", "crazyhzm"); // 用"="克隆 School schoolDuplicate = school; System.out.println("school的hashcode=" + school.hashCode()); System.out.println("schoolDuplicate的hashcode=" + schoolDuplicate.hashCode()); System.out.println("school的值=" + school.toString()); System.out.println("schoolDuplicate的值=" + schoolDuplicate.toString()); // 修改 schoolDuplicate的值 schoolDuplicate.setPresident("加點代碼調調味"); System.out.println("修改schoolDuplicate的值後,school的值=" + school.toString()); System.out.println("修改schoolDuplicate的值後,schoolDuplicate的值=" + schoolDuplicate.toString()); } 複製代碼
運行結果
從運行結果看,用「=」進行克隆,其實只是克隆了原對象的內存地址,schoolDuplicate和school對象都指向了同一塊內存區域,
示例
public static void main(String[] args) throws CloneNotSupportedException { School school = new School("家裏蹲", "crazyhzm"); // 用"clone"克隆 School schoolDuplicateForClone = (School) school.clone(); System.out.println("school的hashcode=" + school.hashCode()); System.out.println("schoolDuplicateForClone的hashcode=" + schoolDuplicateForClone.hashCode()); System.out.println("school的值=" + school.toString()); System.out.println("schoolDuplicateForClone的值=" + schoolDuplicateForClone.toString()); // 修改 schoolDuplicateForClone的值 schoolDuplicateForClone.setPresident("點個贊"); System.out.println("修改schoolDuplicateForClone的值後,school的值=" + school.toString()); System.out.println("修改schoolDuplicateForClone的值後,schoolDuplicateForClone的值=" + schoolDuplicateForClone.toString()); } 複製代碼
運行結果
瞭解了克隆什麼以及怎麼克隆後,咱們到底爲何要使用克隆?
從JDK1.0版本開始,Java語言就提供了克隆機制。要知道咱們爲何要克隆,先來了解一下克隆這種行爲有什麼方法替代,對於CV程序員來講,若是不能 複製黏貼,那就知道本身把代碼一行行本身敲上去。那麼對於對象的克隆來講,無非就是本身new一個新的對象,而後將其中的屬性一個一個賦值過去,若是遇到引用數據類型,還須要繼續new,繼續賦值。當想要建立的新對象的屬性值與原來的對象的屬性值保持一致時,這種手動new,再一個一個賦值的行爲就看似有點繁瑣了,這種時候咱們就能夠用到克隆。從上面怎麼克隆能夠看到會出現兩種不同的結果,在克隆中,須要搞清楚什麼是深拷貝和淺拷貝。
在這裏姑且把克隆改成拷貝,它存在深拷貝和淺拷貝,咱們知道一個對象的屬性中可能存在着另外一個對象的引用,而這個對象是引用數據類型的。而淺克隆後的對象中非基本對象和原對象指向同一塊內存,所以對這些非基本對象的修改會同時更改克隆先後的對象。深克隆能夠實現徹底的克隆,能夠用反射的方式或序列化的方式實現。具體是怎麼樣的,我修改一下上述的示例來介紹:
下面仍是School類的定義,可是新增了一個Grade屬性。
public class School implements Cloneable { /** * 校名 */ String name; /** * 校長 */ String president; Grade grade; public School(String name, String president) { this.name = name; this.president = president; } public School(String name, String president, Grade grade) { this.name = name; this.president = president; this.grade = grade; } public void setName(String name) { this.name = name; } public void setPresident(String president) { this.president = president; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "School{" + "name='" + name + '\'' + ", president='" + president + '\'' + ", grade=" + grade.toString() + '}'; } } 複製代碼
Grade類定義:
public class Grade { /** * 年級名稱 */ String name; public Grade(String name) { this.name = name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Grade{" + "name='" + name + '\'' + '}'; } } 複製代碼
示例代碼
public static void main(String[] args) throws CloneNotSupportedException { Grade grade = new Grade("一年級"); School school = new School("家裏蹲", "crazyhzm", grade); // 用"clone"克隆 School schoolDuplicateForClone = (School) school.clone(); System.out.println("school的hashcode=" + school.hashCode()); System.out.println("school.grade的hashcode=" + school.grade.hashCode()); System.out.println("schoolDuplicateForClone的hashcode=" + schoolDuplicateForClone.hashCode()); System.out.println("schoolDuplicateForClone.grade的hashcode=" + schoolDuplicateForClone.grade.hashCode()); System.out.println("school的值=" + school.toString()); System.out.println("schoolDuplicateForClone的值=" + schoolDuplicateForClone.toString()); // 修改 schoolDuplicateForClone的值 schoolDuplicateForClone.setPresident("點個贊"); schoolDuplicateForClone.grade.setName("二年級"); System.out.println("修改schoolDuplicateForClone的值後,school的值=" + school.toString()); System.out.println("修改schoolDuplicateForClone的值後,schoolDuplicateForClone的值=" + schoolDuplicateForClone.toString()); } 複製代碼
運行結果
從運行結果能夠看到,School的拷貝對象修改了年級名稱後,被拷貝對象的值也被修改了,而且能夠從他們的hashcode也能看出來,在拷貝的時候都指向了同一個內存區域,這就是所謂的淺拷貝。
深拷貝咱們稍微修改一下上述的School以及Grade代碼
school類
修改了clone方法。
public class School implements Cloneable { /** * 校名 */ String name; /** * 校長 */ String president; Grade grade; public School(String name, String president) { this.name = name; this.president = president; } public School(String name, String president, Grade grade) { this.name = name; this.president = president; this.grade = grade; } public void setName(String name) { this.name = name; } public void setPresident(String president) { this.president = president; } @Override protected Object clone() throws CloneNotSupportedException { School school = (School) super.clone(); school.grade = (Grade) grade.clone(); return school; } @Override public String toString() { return "School{" + "name='" + name + '\'' + ", president='" + president + '\'' + ", grade=" + grade.toString() + '}'; } } 複製代碼
Grade類
Grade實現了Cloneable接口。
public class Grade implements Cloneable{ /** * 年級名稱 */ String name; public Grade(String name) { this.name = name; } public void setName(String name) { this.name = name; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Grade{" + "name='" + name + '\'' + '}'; } } 複製代碼
仍是執行上述示例代碼,運行後結果爲:
能夠看到school中grade屬性也被拷貝,修改年級名稱後,原對象的數據並無隨之改變,這就是深拷貝。