@TOC
克隆羊的問題
現(xiàn)在有一只羊tom阔蛉,姓名為 : tom弃舒,年齡為 :1,顏色為 :白色状原,請編寫程序創(chuàng)建和tom羊?qū)傩酝耆嗤?0只羊聋呢。
-
傳統(tǒng)方式解決克隆羊的問題
package com.example.demo.prototype;
public class Sheep {
private String name;
private int age;
private String color;
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;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";
}
}
package com.example.demo.prototype;
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("小樣", 12, "白色");
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
System.out.println(sheep.toString());
System.out.println(sheep2.toString());
System.out.println(sheep3.toString());
System.out.println(sheep4.toString());
}
}
傳統(tǒng)的方式的優(yōu)缺點 :
1)優(yōu)點是比較好理解,簡單易操作颠区。
2)在創(chuàng)建新的對象時削锰,總是需要重新獲取原始對象的屬性,如果創(chuàng)建的對象比較復(fù)雜時毕莱,效率較低器贩。
3)總是需要重新初始化對象,而不是動態(tài)地獲得對象運(yùn)行時的狀態(tài)朋截,不夠靈活
思路 : Java中Object類是所有類的根類磨澡,Object類提供了一個clone()方法,該方法可以將一個Java對象復(fù)制一份质和,但是需要實現(xiàn)clone的Java類必須要實現(xiàn)一個接口Cloneable,該接口表示該類能夠復(fù)制且具有復(fù)制的能力 =》原型模式
原型模式 - 基本介紹
1)原型模式(Prototype模式)是指 :用原型實例指定創(chuàng)建對象的種類稚字,并且通過拷貝這些原型饲宿,創(chuàng)建新的對象
2)原型模式是一種創(chuàng)建型設(shè)計模式,運(yùn)行一個對象再創(chuàng)建另外一個可定制的對象胆描,無需知道如何創(chuàng)建的細(xì)節(jié)
3)工作原理是 :通過將一個原型對象傳給那個要發(fā)動創(chuàng)建的對象瘫想,這個要發(fā)動創(chuàng)建的對象通過請求原型對象拷貝它們自己來實現(xiàn)創(chuàng)建,即 對象.clone()
4)形象的理解 :孫大圣拔出猴毛昌讲,變出其它孫大圣
原型結(jié)構(gòu)圖說明 :
1)Prototype : 原型類国夜,聲明一個克隆自己的接口
2)ConcretePrototype : 具體的原型類,實現(xiàn)一個克隆自己的操作
3)Client :讓一個原型對象克隆自己短绸,從而創(chuàng)建一個新的對象(屬性一樣)
package com.example.demo.prototype.improve;
public class Sheep implements Cloneable{
private String name;
private int age;
private String color;
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;
}
/**
* 克隆該實例车吹,使用默認(rèn)的clone方法來完成
*/
@Override
protected Sheep clone() {
Sheep sheep = null;
try {
sheep = (Sheep)super.clone();
} catch (Exception e) {
e.printStackTrace();
}
return sheep;
}
@Override
public String toString() {
return "Sheep [name=" + name + ", age=" + age + ", color=" + color + "]";
}
}
package com.example.demo.prototype.improve;
public class Client {
public static void main(String[] args) {
Sheep sheep = new Sheep("小樣", 12, "白色");
Sheep sheep2 = (Sheep)sheep.clone();
Sheep sheep3 = (Sheep)sheep.clone();
Sheep sheep4 = (Sheep)sheep.clone();
System.out.println(sheep.toString());
System.out.println(sheep2.toString());
System.out.println(sheep3.toString());
System.out.println(sheep4.toString());
}
}
原型模式在Spring框架中源碼分析
1)Spring中原型bean的創(chuàng)建筹裕,就是原型模式的應(yīng)用
淺拷貝的介紹
1)對于數(shù)據(jù)類型是基本數(shù)據(jù)類的成員變量,淺拷貝會直接進(jìn)行值傳遞窄驹,也就是將該屬性值復(fù)制一份給新的對象朝卒。
2)對于數(shù)據(jù)類型是引用數(shù)據(jù)類型的成員變量,比如說成員變量是某個數(shù)組乐埠、某個類的對象等抗斤,那么淺拷貝會進(jìn)行引用傳遞,也就是只是將該成員變量的引用值(內(nèi)存地址)復(fù)制一份給新的對象丈咐。因為實際上兩對象的該成員變量都指向同一個實例瑞眼。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值
3)前面的克隆羊是淺拷貝
4)淺拷貝是使用默認(rèn)的clone()方法來實現(xiàn)
sheep = (Sheep)supper.clone();
深拷貝基本介紹
1)復(fù)制對象的所有基本數(shù)據(jù)類型的成員變量值
2)為所有引用數(shù)據(jù)類型的成員變量申請存儲空間棵逊,并復(fù)制每個引用數(shù)據(jù)類型成員變量所引用的對象伤疙,直到該對象可達(dá)的所有對象。也就是說歹河,對象進(jìn)行深拷貝要對整個對象進(jìn)行拷貝
3)深拷貝實現(xiàn)方式1 :重寫clone方法來實現(xiàn)深拷貝
4)深拷貝實現(xiàn)方式2 :通過對象序列化實現(xiàn)深拷貝
package com.example.demo.prototype.deepclone;
import java.io.Serializable;
public class DeepCloneableTarget implements Serializable, Cloneable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
package com.example.demo.prototype.deepclone;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class DeepProtoType implements Serializable, Cloneable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 屬性
*/
public String name;
/**
* 引用類型
*/
public DeepCloneableTarget deepCloneableTarget;
public DeepProtoType() {
}
/**
* 深拷貝 - 方式1 使用clone 方法
*/
@Override
protected Object clone() throws CloneNotSupportedException {
Object deepObject = null;
// 這里完成對基本數(shù)據(jù)類型(屬性)和String的克隆
deepObject = super.clone();
// 對引用類型的屬性掩浙,進(jìn)行單獨處理
DeepProtoType deepProtoType = (DeepProtoType)deepObject;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();
return deepProtoType;
}
/**
* 深拷貝 - 方式2 通過對象的序列化實現(xiàn)(推薦)
*
* @return
*/
public Object deepClone() {
// 創(chuàng)建流對象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
// 當(dāng)前這個對象以對象流的方式輸出
oos.writeObject(this);
// 反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObjecType = (DeepProtoType)ois.readObject();
return copyObjecType;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
// 關(guān)閉流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.example.demo.prototype.deepclone;
public class Client {
public static void main(String[] args) throws Exception {
DeepProtoType deepProtoType = new DeepProtoType();
deepProtoType.name = "松江";
deepProtoType.deepCloneableTarget = new DeepCloneableTarget("大牛", "大牛的類");
// 方式1 完成深拷貝
//DeepProtoType deepProtoType2 = (DeepProtoType)deepProtoType.clone();
//System.out.println("deepProtoType.name = " + deepProtoType.name + "deepProtoType.deepCloneableTarget = " + deepProtoType.deepCloneableTarget.hashCode());
//System.out.println("deepProtoType.name = " + deepProtoType2.name + "deepProtoType.deepCloneableTarget = " + deepProtoType2.deepCloneableTarget.hashCode());
DeepProtoType deepProtoType2 = (DeepProtoType)deepProtoType.deepClone();
System.out.println("deepProtoType.name = " + deepProtoType.name + "deepProtoType.deepCloneableTarget = " + deepProtoType.deepCloneableTarget.hashCode());
System.out.println("deepProtoType.name = " + deepProtoType2.name + "deepProtoType.deepCloneableTarget = " + deepProtoType2.deepCloneableTarget.hashCode());
}
}
- 原型模型的注意事項和細(xì)節(jié)
1)創(chuàng)建新的對象比較復(fù)雜時,可以利用原型模式簡化對象的創(chuàng)建過程秸歧,同時也能夠提高效率
2)不用重新初始化對象厨姚,而是動態(tài)地獲得對象運(yùn)行時的狀態(tài)
3)如果原始對象發(fā)生變化(增加或者減少屬性),其它克隆對象的也會發(fā)生相應(yīng)的編號键菱,無需修改代碼
4)在實現(xiàn)深克隆的時候可能需要比較復(fù)雜的代碼
5)缺點 : 需要為每一個類裝配一個克隆方法谬墙,這對全新的類來說不是很難,但對已有的類進(jìn)行改造時经备,需要修改其源代碼拭抬,違背了ocp原則,這點需要注意侵蒙。