1 原型模式特點
- 定義 : 指原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象
- 特點 : 不需要知道任何創建的細節,不調用構造函數
- 類型 : 創建型
適用場景
- 類初始化消耗較多資源
- new產生的一個對象需要非常繁瑣的過程(數據準備、訪問權限等)
- 構造函數比較複雜
- 循環體中生產大量對象時
- 優點 : 原型模式性能比直接new一個對象性能高;簡化創建過程
- 缺點 : 必須配備克隆方法;對克隆複雜對象或對克隆出的對象進行復雜改造時,容易引入風險;深拷貝、淺拷貝要運用得當
2 Coding
現在有一隻羊tom(姓名爲:tom, 年齡爲:1,顏色爲:白色),請編寫程序創建和tom羊屬性完全相同的10只羊
public class Sheep {
//名字
private String name;
//年齡
private int age;
//顏色
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
//省略 get set toString 方法
//public class Client {
// @Test
// public void test() {
// //傳統的方法
// Sheep sheep = new Sheep("tom", 1, "白色");
// 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());
// Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
// //....
// System.out.println(sheep);
// System.out.println(sheep2);
// System.out.println(sheep3);
// System.out.println(sheep4);
// System.out.println(sheep5);
// //...
// }
//}
//實現Cloneable
public class Sheep implements Cloneable{
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
//重寫clone
@Override
protected Sheep clone() throws CloneNotSupportedException {
//使用默認的clone方法來完成
return (Sheep)super.clone();
}
//省略 get set toString 方法
//public class Client {
// @Test
// public void test() throws CloneNotSupportedException {
// System.out.println("原型模式完成對象的創建");
// Sheep sheep = new Sheep("tom", 1, "白色");
// Sheep sheep2 = sheep.clone(); //克隆
// Sheep sheep3 = sheep.clone(); //克隆
// Sheep sheep4 = sheep.clone(); //克隆
// Sheep sheep5 = sheep.clone(); //克隆
// System.out.println("sheep2 =" + sheep2);
// System.out.println("sheep3 =" + sheep3);
// System.out.println("sheep4 =" + sheep4);
// System.out.println("sheep5 =" + sheep5);
// }
//}
//抽象類
public abstract class A implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class B extends A {
public static void main(String[] args) throws CloneNotSupportedException {
B b = new B();
B clone = (B) b.clone();
System.out.println(b); // B@6ff3c5b5
System.out.println(clone); // B@3764951d
}
}
3 原型模式擴展
3.1 淺克隆錯誤演示
public class Pig implements Cloneable {
private String name;
private Date birthday;
public Pig(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {return name;}
public void setName(String name) {this.name = name; }
public Date getBirthday() {return birthday; }
public void setBirthday(Date birthday) {this.birthday = birthday;}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Pig{" + "name='" + name + '\'' + ", birthday=" + birthday + '}' + super.toString();
}
}
public class PigTest {
@Test
public void test() throws CloneNotSupportedException {
Date birthday = new Date(0L);
Pig pig = new Pig("佩奇", birthday);
Pig cPig = (Pig) pig.clone();
System.out.println(pig); // Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}Pig@66cd51c3
System.out.println(cPig); // Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}Pig@4dcbadb4
//本意只是修改pig的生日
pig.getBirthday().setTime(666666666666L);
System.out.println(pig); // Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}Pig@66cd51c3
//cPig也改了,這個結果不是我想要的,原因出在淺拷貝上
System.out.println(cPig); // 跟着改了 : Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}Pig@4dcbadb4
}
3.2 深克隆兩種實現方式
public class Pig implements Cloneable, Serializable {
private static final long serialVersionUID = 779970270042384579L;
private String name;
private Date birthday;
public Pig(String name, Date birthday) {this.name = name;this.birthday = birthday;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public Date getBirthday() {return birthday;}
public void setBirthday(Date birthday) {this.birthday = birthday;}
//深拷貝——使用clone方法
//Remove this "clone" implementation; use a copy constructor or copy factory instead.
@Override
protected Pig clone() throws CloneNotSupportedException {
Pig pig = (Pig) super.clone();
pig.birthday = (Date) pig.birthday.clone();
return pig;
}
//深拷貝——通過對象的序列化實現 (推薦)
public Pig 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 (Pig) ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
} finally {
//關閉流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e) {
}
}
}
@Override
public String toString() {
return "Pig{" + "name='" + name + '\'' + ", birthday=" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(birthday) + '}' + " " + super.toString();
}
}
public class PigTest {
@Test
public void test() {
Date birthday = new Date(0L);
Pig pig = new Pig("佩奇", birthday);
//Pig cPig = pig.clone();
Pig cPig = pig.deepClone();
System.out.println(pig); // Pig{name='佩奇', birthday=1970/01/01 08:00:00} Pig@1b9e1916
System.out.println(cPig); // Pig{name='佩奇', birthday=1970/01/01 08:00:00} Pig@61064425
pig.getBirthday().setTime(666666666666L);
System.out.println(pig); // Pig{name='佩奇', birthday=1991/02/16 09:11:06} Pig@1b9e1916
System.out.println(cPig); // Pig{name='佩奇', birthday=1970/01/01 08:00:00} Pig@61064425
}
}
4 原型模式破壞單例模式
單例模式實現了Cloneable一定要小心
//實現Serializable和Cloneable接口
public class Singleton implements Cloneable {
private final static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
@Override
protected Singleton clone() {
//return (Singleton) super.clone();
//注意,返回的是靜態變量,不要調用父類方法然後返回
return instance;
}
}
/**
* 原型模式裏面的克隆可能會破壞單例模式
*/
@Test
public void test() throws Exception {
Singleton hungrySingleton = Singleton.getInstance();
Singleton cloneHungrySingleton = hungrySingleton.clone();
System.out.println(hungrySingleton);//
System.out.println(cloneHungrySingleton);//
}
5 源碼分析
public class HungrySingleton implements Serializable, Cloneable {
private final static HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
private HungrySingleton() {
if (hungrySingleton != null) {
throw new RuntimeException("單例構造器禁止反射調用");
}
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
private Object readResolve() {
return hungrySingleton;
}
//
@Override
protected Object clone() throws CloneNotSupportedException {
return getInstance();
}
}
@Test
public void test() throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
HungrySingleton hungrySingleton = HungrySingleton.getInstance();
Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
method.setAccessible(true);
HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);
System.out.println(hungrySingleton);//HungrySingleton@66cd51c3
System.out.println(cloneHungrySingleton);//HungrySingleton@66cd51c3
}
//java.lang.Cloneable
public interface Cloneable {
//java.util.ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
//<dependency>
// <groupId>org.mybatis</groupId>
// <artifactId>mybatis</artifactId>
// <version>3.5.4</version>
//</dependency>
//org.apache.ibatis.cache.CacheKey
public class CacheKey implements Cloneable, Serializable {
@Override
public CacheKey clone() throws CloneNotSupportedException {
CacheKey clonedCacheKey = (CacheKey) super.clone();
clonedCacheKey.updateList = new ArrayList<>(updateList);
return clonedCacheKey;
}