概述
克隆模式是一種比較簡單的設計模式,基本從字面意思就可以明白這種設計模式是干什么的,簡單來說就是造一個和原來一模一樣的對象,就叫克隆模式。克隆模式分為兩種,一種是淺度克隆,一種是深度克隆,至於這兩者之前的區別,看下面的代碼。
淺度克隆
實體類,沒有特別的作用,作為原型對象(其實就是克隆對象,原型是一個別名)中的一個引用類型的屬性

package com.gxl.demo.DesignPattern.clonepattern; /** * Description: 實體類,作為ConcretePrototype類中的一個引用類型的屬性。 */ public class Address { public String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
原型對象(克隆對象)

package com.gxl.demo.DesignPattern.clonepattern; /** * Description: 克隆模式,淺度克隆 */ public class ConcretePrototype implements Cloneable{ public String name ; public Address address; public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public ConcretePrototype clone(){ Object obj = null; try { obj = super.clone(); return (ConcretePrototype)obj; } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } public static void main(String[] args) { ConcretePrototype concretePrototype = new ConcretePrototype(); Address address = new Address(); address.setAddress("ganloucun"); concretePrototype.setAddress(address); ConcretePrototype concretePrototype1 = concretePrototype.clone(); //結果為true,證明Cloneable接口中的clone對於引用類型的屬性clone的是地址,不是值 System.out.println(concretePrototype1.getAddress() == concretePrototype.getAddress()); //重新修改address,新克隆出來的對象的值也跟着變了 address.setAddress("gunduzi"); concretePrototype.setAddress(address); System.out.println(concretePrototype1.getAddress().getAddress()); } }
解析:原型對象實現Cloneable接口,可以調用Cloneable中的clone方法,這個方法是一個native方法,就是說這個方法不是用java語言實現的,是使用C語言或者C++語言實現的,所以我們看不到源碼,但是要明白這個方法的返回,這個方法的返回就是實現這個接口的類的對象的克隆。但是原生的clone方法是一個淺度克隆,什么是淺度克隆呢?淺度克隆就是如果類中的屬性如果是非引用類型的就直接把值克隆到新對象的對應屬性中,如果類中的屬性是引用類型的,就像例子中的address,是一個對象,是一個引用類型的屬性,克隆的時候只是把原對象的這個字段對應的的引用克隆到新對象中,也就是說,新對象的address字段和舊對象address所引用的是同一個地址。
深度克隆
深度克隆有兩種實現方式,一種實現方式是原型對象中的引用類型屬性也實現了Cloneable接口。另一種方式是,原型對象和原型對象中引用類型的屬性都實現了Serializable接口,然后通過序列化的方法實現。
通過Cloneable實現
實體類

package com.gxl.demo.DesignPattern.clonepattern; /** * Description: 實體類 */ public class AddressDeep implements Cloneable{ public String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public AddressDeep clone(){ Object obj = null; try { obj = super.clone(); return (AddressDeep)obj; } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } }
原型對象

package com.gxl.demo.DesignPattern.clonepattern; /** * Description: 克隆模式,深度克隆,通過Cloneable實現 */ public class ConcretePrototypeDeep implements Cloneable{ public String name ; public AddressDeep addressDeep; public String getName() { return name; } public void setName(String name) { this.name = name; } public AddressDeep getAddressDeep() { return addressDeep; } public void setAddressDeep(AddressDeep addressDeep) { this.addressDeep = addressDeep; } public ConcretePrototypeDeep clone(){ Object obj = null; try { obj = super.clone(); ConcretePrototypeDeep concretePrototypeDeep = (ConcretePrototypeDeep)obj; AddressDeep addressDeep = this.addressDeep.clone(); concretePrototypeDeep.addressDeep = addressDeep; return concretePrototypeDeep; } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } public static void main(String[] args) { ConcretePrototypeDeep concretePrototypeDeep = new ConcretePrototypeDeep(); AddressDeep addressDeep = new AddressDeep(); addressDeep.setAddress("ganloucun"); concretePrototypeDeep.setAddressDeep(addressDeep); ConcretePrototypeDeep concretePrototypeDeep1 = concretePrototypeDeep.clone(); //輸出false,證明克隆出來的新對象的引用類型屬性重新賦值,而不是原來屬性的地址 System.out.println(concretePrototypeDeep.getAddressDeep() == concretePrototypeDeep1.getAddressDeep()); System.out.println(concretePrototypeDeep1.getAddressDeep().getAddress()); } }
解析:原型對象中的引用類型屬性addressDeep也實現了Cloneable接口,這樣在克隆原型對象的時候,把addressDeep對象也克隆一下,然后再把克隆的值賦值給新對象。也就是說無論原型對象中有多少引用類型變量,如果要實現深度克隆,原型對象中的引用類型的屬性都要實現Cloneable接口。
通過Serializable實現
實體類

package com.gxl.demo.DesignPattern.clonepattern; import java.io.Serializable; /** * Description: 實體類 */ public class AddressStream implements Serializable { public String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
原型對象

package com.gxl.demo.DesignPattern.clonepattern; import java.io.*; /** * Description: 深度克隆模式,通過繼承Serializable接口,通過流的方式實現 */ public class ConcretePrototypeStream implements Serializable { public String name; public AddressStream addressStream; public String getName() { return name; } public void setName(String name) { this.name = name; } public AddressStream getAddressStream() { return addressStream; } public void setAddressStream(AddressStream addressStream) { this.addressStream = addressStream; } public ConcretePrototypeStream clone(){ try { //將對象寫入流 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this); //將對象從流中取出 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); return (ConcretePrototypeStream)objectInputStream.readObject(); } catch (Exception e) { e.printStackTrace(); return null; } } public static void main(String[] args) { ConcretePrototypeStream concretePrototypeStream = new ConcretePrototypeStream(); AddressStream addressStream = new AddressStream(); addressStream.setAddress("ganloucun"); concretePrototypeStream.setAddressStream(addressStream); ConcretePrototypeStream concretePrototypeStream1 = concretePrototypeStream.clone(); System.out.println(concretePrototypeStream.getAddressStream() == concretePrototypeStream1.getAddressStream()); } }
解析:原型對象實現了Serializable接口,表明這個類可以序列化,然后用流的方式把原來的對象寫入到流中,之后重新讀出來,就可以實現深度克隆。
深度克隆和淺度克隆的區別
通過上面的敘述,大家應該基本可以搞清楚這兩者之間的區別了,不過這里還是在重新敘述一下,深度和淺度的主要區別就體現在原型對象的引用類型變量的克隆方式上,淺度克隆是把舊對象的引用類型變量的地址傳給新對象的引用類型的變量。而深度克隆是把舊對象的引用類型變量的值傳給新對象的引用類型變量。