1、原型模式的定義
原型模式是 使用原型實例指定待創建對象的類型,並且通過複製這個原型來創建新的對象。
- 是 創建型的設計模式
- 分爲 深度克隆 和 淺克隆
2、原型模式UML
原型模式要求對象實現一個可以“克隆” 自身的 接口 ,這樣就可以通過複製一個實例對象本身來創建一個新的實例。這樣一來,通過原型實例創建新的對象,就不再需要關心這個實例本身的類型,只要實現了克隆自身的方法,就可以通過這個方法來獲取新的對象,而無須再去通過 new 來創建。
這種形式涉及到三個角色:
(1)抽象原型(Prototype):這是一個抽象角色,通常由一個Java接口或Java抽象類實現。此角色給出所有的具體原型類所需的接口。
(2)具體原型(Concrete Prototype):被複制的對象。此角色需要實現抽象的原型角色所要求的接口。
(3)客戶(Client) :客戶類提出創建對象的請求。
3、 java 中實現克隆
實現 Cloneable
接口:
Cloneable
接口 就是上面提到的 抽象原型。
Cloneable
接口的作用是在運行時通知虛擬機可以安全地在實現了此接口的類上使用clone方法。在java虛擬機中,只有實現了這個接口的類纔可以被拷貝,否則在運行時會拋出 CloneNotSupportedException
異常。
重寫Object類中的clone方法:
Java中,所有類的父類都是Object類,Object類中有一個clone方法,作用是返回對象的一個拷貝,但是其作用域protected類型的,一般的類無法調用,因此,原型類需要將clone方法的作用域修改爲public類型。
4、原型模式的優缺點
4.1、優點:
1、使用原型模型創建一個對象比直接new一個對象更有效率,因爲它直接操作內存中的二進制流,特別是複製大對象時,性能的差別非常明顯。
2、隱藏了製造新實例的複雜性,使得創建對象就像我們在編輯文檔時的複製粘貼一樣簡單。
4.2、 缺點:
1、由於使用原型模式複製對象時不會調用類的構造方法,所以原型模式無法和單例模式組合使用,因爲原型類需要將clone方法的作用域修改爲public類型,那麼單例模式的條件就無法滿足了。
2、使用原型模式時不能有final對象。
3、Object類的clone方法只會拷貝對象中的基本數據類型,對於數組,引用對象等只能另行拷貝。這裏涉及到深拷貝和淺拷貝的概念。
5、淺拷貝
package design.prototype;
public class Sheep implements Serializable,Cloneable {
private String name;
private int age;
private String color;
private Sheep friend;
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}
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 String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend + "]";
}
@Override
public Sheep clone() throws CloneNotSupportedException {
Sheep sheep=null;
try{
sheep=(Sheep)super.clone();
}catch(Exception e){
System.err.println(e.getMessage());
}
return sheep;
}
}
package design.prototype;
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep friend = new Sheep("JOK", 1, "黑色");
Sheep sheep = new Sheep("tom", 2, "白色");
sheep.setFriend(friend);
Sheep sheep2 = sheep.clone();
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep == sheep2);
System.out.println(sheep.hashCode());
System.out.println(sheep2.hashCode());
System.out.println("friend = "+sheep.getFriend().hashCode());
System.out.println("friend2 = "+sheep2.getFriend().hashCode());
}
}
運行結果:
Sheep [name=tom, age=2, color=白色, friend=Sheep [name=JOK, age=1, color=黑色, friend=null]]
Sheep [name=tom, age=2, color=白色, friend=Sheep [name=JOK, age=1, color=黑色, friend=null]]
false
366712642
1829164700
friend = 2018699554
friend2 = 2018699554
分析: Sheep中的基本類型是複製了值,但是引用類型是複製了引用。
6、淺克隆與深克隆 的區別
無論你是自己實現克隆方法,還是採用Java提供的克隆方法,都存在一個淺度克隆和深度克隆的問題。
- 淺度克隆
如果原型對象的成員變量是值類型(八大基本類型:byte、short、in、long、char、double、float、boolean),那麼就直接複製。
如果是複雜的類型,(枚舉,String,對象)就只複製對應的內存地址。
在換個說法,就是複雜類型的成員變量(String、枚舉,對象)用的是一個。修改了克隆對象的原型對象也會變。他們是共用的。而值類型不是共用的。
- 深度克隆
深克隆,我就不用多說了吧,就是什麼都是單獨的!全部複製,然後各自獨立。你修改克隆對象對於原型對象沒有絲毫影響,完全的影分身!
7、深度克隆
clone()
方法無法進行深度克隆, 又實現deepclone()
方法,通過字節複製實現深度克隆。
package design.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Sheep implements Serializable,Cloneable {
private String name;
private int age;
private String color;
private Sheep friend;
public Sheep(String name, int age, String color) {
super();
this.name = name;
this.age = age;
this.color = color;
}
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 String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Sheep getFriend() {
return friend;
}
public void setFriend(Sheep friend) {
this.friend = friend;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend + "]";
}
public Sheep clone() throws CloneNotSupportedException {
Sheep sheep = null;
try {
sheep = (Sheep) super.clone();
} catch (Exception e) {
System.err.println(e.getMessage());
}
return sheep;
}
public Sheep deepclone() {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (Sheep) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep friend = new Sheep("JOK", 1, "黑色");
Sheep sheep = new Sheep("tom", 2, "白色");
sheep.setFriend(friend);
Sheep sheep2 = sheep.deepclone();
System.out.println(sheep);
System.out.println(sheep2);
System.out.println(sheep == sheep2);
System.out.println(sheep.hashCode());
System.out.println(sheep2.hashCode());
System.out.println("friend = "+sheep.getFriend().hashCode());
System.out.println("friend2 = "+sheep2.getFriend().hashCode());
}
}
運行結果:
Sheep [name=tom, age=2, color=白色, friend=Sheep [name=JOK, age=1, color=黑色, friend=null]]
Sheep [name=tom, age=2, color=白色, friend=Sheep [name=JOK, age=1, color=黑色, friend=null]]
false
1442407170
558638686
friend = 589431969
friend2 = 1149319664
https://blog.csdn.net/qq_40709468/article/details/82316418