java 設計模式之原型模式(三)
java 設計模式之原型模式③
每一個優秀的人,都有一段沉默的時光。那一段時光,是付出了很多努力,忍受孤獨和寂寞,不抱怨不訴苦,日後說起時,連自己都能被感動日子。
設計模式學習,近期我會把23中設計模式都寫成部落格,敬請期待~
什麼是原型模式?
- 原型模式是指:用原型例項指定建立物件的種類,並且通過拷貝這些原型,建立新的物件
- 原型設計模式是一種建立型設計模式,執行一個物件再建立另外一個可定製的物件,無需知道如何建立的細節。
- 工作原理:通過將一個原型物件傳給那個要發動建立的物件,這個要發動建立的物件通過請求原型物件拷貝它們自己來實施建立,既 物件.clone();
什麼是克隆?
什麼是克隆呢?可能大家都聽說過克隆羊多利吧
就是一個已經存在的東西,完完全玩的複製一份,就稱之為克隆,說的通俗一點就是拷貝貼上~
原型設計模式思路
咋們也模仿羊來克隆出來一隻多利(克隆羊),
先用普通的最簡單的做法來克隆
然後在給大家介紹使用原型模式來克隆
普通方法克隆
建立一直普通的’羊’,用來克隆:
public class Sheep implements Cloneable {
String name;//羊的名字
int age;//樣的年齡
String color;//羊的顏色
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
}
這隻羊非常簡單,只有名字,年齡和顏色
來看看是如何克隆的~
Sheep sp1 = new Sheep("tom", 3, "red");
Sheep sp2 = new Sheep("tom", 3, "red");
Log.i("原型模式普通方法:",sp1.toString()+"\t hashCode:"+sp1.hashCode());
Log.i("原型模式普通方法:",sp2.toString()+"\t hashCode:"+sp2.hashCode());
這裡非常好理解,就是new了2個羊達到’克隆’的目的
效果圖(1.1)
:
可以看出hashCode是不一樣的,說明2個記憶體指向不一樣,那麼就來驗證一下.
驗證方式非常簡單,通過修改名字來判斷會不會衝突
驗證程式碼:
Sheep sp1 = new Sheep("tom", 3, "red");
Sheep sp2 = new Sheep("tom", 3, "red");
Log.i("原型模式普通方法:",sp1.toString()+"\t hashCode:"+sp1.hashCode());
Log.i("原型模式普通方法:",sp2.toString()+"\t hashCode:"+sp2.hashCode());
sp2.setName("jack");//修改'克隆'羊的名字為jack
Log.i("原型模式普通方法:",sp1.toString()+"\t hashCode:"+sp1.hashCode());
Log.i("原型模式普通方法:",sp2.toString()+"\t hashCode:"+sp2.hashCode());
修改’克隆’羊的名字為jack
效果圖(1.2)
:
通過這張圖可以看出:即使修改’克隆’羊也不會影響被克隆羊的引數(這裡指名字).
用程式碼來解釋就是:記憶體地址不一樣,記憶體指向也不一樣.這是2個完全沒關係的東西
傳統方式的優缺點
- 比較好理解,簡單易操作
- 在建立新的物件時,總是需要重新獲取原始物件的屬性,如果建立的物件過多,效率不高。
- 總是需要重新初始化物件,而不是動態的獲取物件執行時的狀態。
淺克隆
對於基本資料型別是成員變數,淺拷貝會直接進行值拷貝,也就是將該屬性值複製一份給新的物件。
原型模式使用:
- 實現Cloneable介面
- 實現clone()方法
淺拷貝程式碼實現:
public class Sheep implements Cloneable {
String name;//羊的名字
int age;//樣的年齡
String color;//羊的顏色
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
@NonNull
@Override
public Object clone() {
try {
//引用物件拷貝 將物件的方法拷貝給當前的值
Sheep sheep = (Sheep) super.clone();
return sheep;
} catch (Exception e) {
Log.i("原型模式:", e.getMessage());
return null;
}
}
}
實現程式碼:
Sheep sheep1 = new Sheep("tom", 3, "red",lazinessSheep);
Sheep sheep2 = (Sheep) sheep1.clone();//'克隆羊'
Log.i("原型模式:", "============ 克隆之前 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
Log.i("原型模式:", "===========================");
sheep1.setName("jack");
Log.i("原型模式:", "============ 克隆之後 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
分析:
- sheep1 是本身的羊
- sheep2 是通過sheep1 .clone()克隆出來的羊
- 先輸出結果,然後改變sheep1本身羊的name,來看看會不會s影響heep2( 克隆羊)
效果圖(1.3)
:
可以看出這種克隆方式對於基本資料型別能夠完成很好的克隆,
這就是淺克隆
如果說我的羊現在有一隻朋友’懶洋洋’來看看有如何不同:
我的朋友類:
public class LazinessSheep{
String name = "懶洋洋";
int age = 2;
public LazinessSheep() {
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "LazinessSheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
克隆羊類:
public class Sheep implements Cloneable {
String name;//羊的名字
int age;//樣的年齡
String color;//羊的顏色
public LazinessSheep lazinessSheep;//羊的朋友(懶洋洋)
public Sheep(String name, int age, String color, LazinessSheep lazinessSheep) {
this.name = name;
this.age = age;
this.color = color;
this.lazinessSheep = lazinessSheep;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", lazinessSheep=" + lazinessSheep +
'}';
}
@NonNull
@Override
public Object clone() {
try {
//引用物件拷貝 將物件的方法拷貝給當前的值
Sheep sheep = (Sheep) super.clone();
return sheep;
} catch (Exception e) {
Log.i("原型模式:", e.getMessage());
return null;
}
}
}
實現程式碼:
LazinessSheep lazinessSheep = new LazinessSheep();
Sheep sheep1 = new Sheep("tom", 3, "red",lazinessSheep);
//'克隆羊'
Sheep sheep2 = (Sheep) sheep1.clone();
Log.i("原型模式:", "============ 克隆之前 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
Log.i("原型模式:", "===========================");
sheep1.lazinessSheep.setName("灰太狼");
Log.i("原型模式:", "============ 克隆之後 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
分析:
- lazinessSheep是我的朋友
- sheep1是本身的羊
- sheep2是被克隆的羊
- 先輸出第一次的結果,然後改變我朋友(lazinessSheep)的名字為灰太狼
來看看有什麼不同:
效果圖(1.4)
:
通過效果圖(1.4)
可以看出我們只改變了
sheep1本身的羊朋友的值(名字),但是sheep2克隆羊朋友的值(名字)也改變了
現在的問題如下:
可以看出,淺拷貝無法複製引用型別,因為他沒有複製引用型別,只是吧現在的狀態指向了記憶體地址,不管你複製10個還是20個克隆羊,引用型別還是指向的原本的記憶體地址
這就是淺克隆為什麼不能克隆引用型別,只能克隆基本資料型別了
深克隆
所謂深克隆,就是解決淺克隆不能複製引用資料型別的問題,達到克隆的每一個個羊,都是單獨的,和原本並沒有區別.
深克隆程式碼:
public class LazinessSheep implements Cloneable{
String name = "懶洋洋";
int age = 2;
public LazinessSheep() {
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "LazinessSheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@NonNull
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
將需要克隆的有引用型別也實現Cloneable介面實現clone()方法
public class Sheep implements Cloneable {
String name;//羊的名字
int age;//樣的年齡
String color;//羊的顏色
public LazinessSheep lazinessSheep;//羊的朋友(懶洋洋)
public Sheep(String name, int age, String color, LazinessSheep lazinessSheep) {
this.name = name;
this.age = age;
this.color = color;
this.lazinessSheep = lazinessSheep;
}
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", lazinessSheep=" + lazinessSheep +
'}';
}
@NonNull
@Override
public Object clone() {
try {
//引用物件拷貝 將物件的方法拷貝給當前的值
Sheep sheep = (Sheep) super.clone();
lazinessSheep = (LazinessSheep) sheep.lazinessSheep.clone();
return sheep;
} catch (Exception e) {
Log.i("原型模式:", e.getMessage());
return null;
}
}
}
修改羊本身的類:
重點方法:
@NonNull
@Override
public Object clone() {
try {
//引用物件拷貝 將物件的方法拷貝給當前的值
Sheep sheep = (Sheep) super.clone();
lazinessSheep = (LazinessSheep) sheep.lazinessSheep.clone();
return sheep;
} catch (Exception e) {
Log.i("原型模式:", e.getMessage());
return null;
}
}
因為這裡的引用型別也是由基本資料型別組成的,
所以在這裡通過
lazinessSheep = (LazinessSheep) sheep.lazinessSheep.clone();
把LazinessSheep的clone();賦值給當前類的lazinessSheep即可
實現程式碼:
還是上邊一樣的程式碼:
/// 原型模式之深克隆 \\\\\\\\\\\\\\\\\\\\
LazinessSheep lazinessSheep = new LazinessSheep();
Sheep sheep1 = new Sheep("tom", 3, "red",lazinessSheep);
//'克隆羊'
Sheep sheep2 = (Sheep) sheep1.clone();
Log.i("原型模式:", "============ 克隆之前 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
Log.i("原型模式:", "===========================");
sheep1.lazinessSheep.setName("灰太狼");
Log.i("原型模式:", "============ 克隆之後 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
效果圖(1.5)
:
可以看到,改變sheep1的引用資料型別lazinessSheep為灰太狼之後並沒有影響克隆之後的值,這樣就達到了深克隆的效果
總結
- 淺克隆:只能克隆基本資料型別,因為克隆引用資料型別他不會建立新的記憶體地址,而是吧當前克隆的指向被克隆的地址
- 深克隆:所有資料型別都可以克隆,如果原始物件發生變化,其他克隆物件也會發生相應的變化,無需修改程式碼。
- 建立新的物件比較複雜時,可以使用原型模式簡化物件的建立過程,同時也能提高效率。
- 不用重新初始化物件,而是動態地獲取物件執行的狀態。
最近文章:
原創不易,您的點贊就是對我最大的支援,點個贊支援一下哦~