原型模式的定義與特點
原型(Prototype)模式的定義如下:用一個已經創建的實例作爲原型,通過複製該原型對象來創建一個和原型相同或相似的新對象。在這裏,原型實例指定了要創建的對象的種類。用這種方式創建對象非常高效,根本無須知道對象創建的細節。例如,Windows 操作系統的安裝通常較耗時,如果複製就快了很多。在生活中複製的例子非常多,這裏不一一列舉了。
原型模式的結構與實現
由於 Java 提供了對象的 clone() 方法,所以用 Java 實現原型模式很簡單。
1. 模式的結構
原型模式包含以下主要角色。
- 抽象原型類:規定了具體原型對象必須實現的接口。
- 具體原型類:實現抽象原型類的 clone() 方法,它是可被複制的對象。
- 訪問類:使用具體原型類中的 clone() 方法來複制新的對象。
public class Sheep implements Cloneable {
private String name;
private Integer age;
private String color;
private Sheep friend;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer 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
protected Object clone() {
Sheep s = null;
try {
s = (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return s;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend.hashCode() + "]";
}
}
public class PrototypeDemo {
public static void main(String[] args) {
Sheep sheep1 = new Sheep();
sheep1.setName("sheep01");
sheep1.setAge(10);
sheep1.setColor("黃色");
Sheep sheep2 = new Sheep();
sheep2.setName("sheep02");
sheep2.setAge(15);
sheep2.setColor("白色");
sheep2.setFriend(sheep1);
Object clone = sheep2.clone();
System.out.println(sheep2);
System.out.println(clone);
}
}
深拷貝
有上述輸入的值可以看出,拷貝後的 friend 對象的hashCode 相等,所以friend是同一個對象。因此,可以確定通過 super.clone() 的對象是錢拷貝的對象。
淺拷貝介紹
1. 對於數據類型是基本數據類型的成員變量,淺拷貝會直接進行值傳遞,也就是將該屬性值複製一份給新的的對象
2. 對於數據類型是引用數據類型的成員變量,比如說成員變量是某個數組、某個類的對象等,那麼淺拷貝會進行引用傳遞,也就是將該成員變量的引用值(內存地址)複製一份給新的對象。因爲實際上兩個對象的成員變量都指向同一個實例。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值。
深拷貝介紹
1. 複製對象的所有基本數據類型的成員變量值
2. 爲所有引用數據類型的成員變量申請存儲空間,並複製每個引用數據類型成員變量所引用的對象,直到該對象可達的所有對象。也就是說,對象進行深拷貝要對整個對象進行拷貝
3. 深拷貝實現方式1:重寫clone方法來實現深拷貝
4. 深拷貝實現方式2:通過對象序列化來實現深拷貝
第一種方式:重寫clone方法來實現深拷貝
public class Sheep implements Cloneable, Serializable {
private static final long serialVersionUID = 2795877779132802842L;
private String name;
private Integer age;
private String color;
private Sheep friend;
// 省略 getter 和 setter 方法
@Override
protected Object clone() {
Sheep s = null;
try {
s = (Sheep) super.clone();
if (s.getFriend() != null) {
Sheep friend = (Sheep) s.getFriend().clone();
s.friend = friend;
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return s;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend.hashCode() + "]";
}
}
第二種方式:通過對象序列化來實現深拷貝
public class Sheep implements Cloneable, Serializable {
private static final long serialVersionUID = 2795877779132802842L;
private String name;
private Integer age;
private String color;
private Sheep friend;
// 此處省略 getter 和 setter
@Override
protected Object clone() {
ByteArrayOutputStream baos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bais = null;
ObjectInputStream ois = null;
Sheep s = null;
try {
// 序列化
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
// 當前這個對象以對象流的方式輸出
oos.writeObject(this);
// 反序列化
bais = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
s = (Sheep) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
// close流,此處省略
}
return s;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend.hashCode() + "]";
}
}
原型模式的注意事項和細節
1. 創建新的對象比較複雜時,可以利用原型模式簡化對象的創建過程,同時也能夠提高效率
2. 不用重新初始化新的對象,而是動態地獲得對象運行時的狀態
3. jdk的克隆值淺拷貝,需要深拷貝則需要自己寫代碼實行
4. 缺點:需要爲每一個類都提供一個克隆的方法,這對全新的類來說並不困難,但是對已有的類進行修改時,需要需求其他的代碼,違背了ocp原則。