原型模式
原型模式,屬於對象創建型模式中的一種。通過復制原型對象的方法來創建一個對象實例,且該對象與原對象有相同的數據結構和值。類似我們在備份數據庫信息的時候,將數據庫中表的結構和數據的一同備份,生成一個數據庫文件。
在Java環境中,要實現原型模式,要理解對象創建、引用和克隆的相關知識,在這里通過簡單分析JVM的內存在對象創建、引用和克隆時棧和堆的內容變化,來深入理解原型模式是如何在Java環境中運作的。
1.簡單理解JVM內存中棧和堆
棧:用來存放函數中定義的基本類型的變量和對象的引用變量。
堆:則是存放由new創建的對象和數組,對象內存儲普通的變量和方法。對象創建后將其地址賦值給棧中的引用變量。
方法區:也是堆,這里面存放類代碼、靜態變量、靜態方法和字符串常量等。
2.引用和克隆的區別
引用的示例圖:
克隆的示意圖:
由示例圖我們可以看出,引用,比如person2=person1,棧中兩個不同的成員變量指向對中的同一個對象,他們兩個的值是一樣的,都是該對象在內存中的地址。而克隆是將對象復制一份包括數據結構和值,將復制出的對象的地址賦值給棧中的另外一個成員變量person2。
3.淺層克隆和深層克隆
有沒有注意到一個問題,如果普通變量是一個引用變量,比如數組,列表或map,那么克隆是否把引用變量(person1中的friends)所引用的對象也給復制一份呢。其實並沒有,只是將引用變量的變量名和值復制了一份,他們還是用的同一個引用對象,這就是淺層克隆。如淺層克隆示意圖所示。那么如果想要把引用變量所指的對象也復制一份,則需要重新新建一個對應的對象,將值傳入對象中,返回給復制后的引用變量person2中的friends中。如深層克隆示意圖所示。
淺層克隆示意圖:
深層克隆示意圖:
4.代碼實現
Person類:
在Java中克隆該類需實現Cloneable接口,重寫了Object的 clone() 方法,該方法會創建和返回一個Person類的一個復制,也就是上述所說的淺層復制。該類中添加了淺層克隆shallowClone()和深層克隆deepClone()。
package prototype; import java.util.ArrayList; import java.util.List; public class Person implements Cloneable{ //姓名 private String name; //年齡 private int age; //朋友 private List<String> friends; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List<String> getFriends() { return friends; } public void setFriends(List<String> friends) { this.friends = friends; } //重寫toString方法 @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", friends=" + friends + "]"; } //淺層克隆 public Person shallowClone() { try { return (Person) super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } //深層克隆 public Person deepClone() { try { Person person = (Person) super.clone(); List<String> newFriends = new ArrayList<String>(); for(String friend : this.getFriends()) { newFriends.add(friend); } person.setFriends(newFriends); return person; } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }
MainClass:
package prototype; import java.util.ArrayList; import java.util.List; public class MainClass { public static void main(String[] args) { //創建對象person1 Person person1 = new Person(); //初始化對象 person1.setName("zhangsan"); person1.setAge(20); List<String> friends = new ArrayList<String>(); friends.add("lisi"); friends.add("wangwu"); person1.setFriends(friends); //person2是淺層克隆 Person person2 = person1.shallowClone(); //person3是深層克隆 Person person3 = person1.deepClone(); //獲取淺層克隆的friends的list對象 List<String> person2_friends = person2.getFriends(); //向引用對象中添加值 person2_friends.add("shallow"); person2.setFriends(person2_friends); //獲取深層克隆的friends的list對象 List<String> person3_friends = person3.getFriends(); //向引用對象中添加值 person3_friends.add("deep"); person3.setFriends(person3_friends); System.out.println("原型:"+person1); System.out.println("淺層克隆:"+person2); System.out.println("深層克隆:"+person3); } }
從結果中可以發現,淺層克隆的person2中向friends列表中添加的shallow朋友,而在原型person1中也添加了shallow,驗證了前面的說法。深層克隆person3是在person2之前克隆的,所以沒有添加shallow朋友,而之后添加的deep朋友也沒有影響person1和person2中的friends列表。
通過結合JVM內存中的棧和堆來解釋原型模型,利用Java代碼成功測試。可以發現Java中默認是的克隆模式是淺層克隆,不復制引用變量所對應的對象。那么對於深層次的克隆,需要編寫對應代碼來復制。