1 原型模式特点
- 定义 : 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
- 特点 : 不需要知道任何创建的细节,不调用构造函数
- 类型 : 创建型
适用场景
- 类初始化消耗较多资源
- new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂
- 循环体中生产大量对象时
- 优点 : 原型模式性能比直接new一个对象性能高,简化创建过程
- 缺点 : 必须配备克隆方法,对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险,深拷贝、浅拷贝要运用得当
2 Coding
现在有一只羊tom(姓名:tom、年龄:1、颜色:白色),请编写程序创建和tom羊属性完全相同的10只羊
传统的方法
@Data
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;
}
}
@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
@Data
// 实现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();
}
}
@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 浅克隆错误演示
@Data
public class Pig implements Cloneable {
private String name;
private Date birthday;
public Pig(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
@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)
System.out.println(cPig); // Pig(name=佩奇, birthday=Thu Jan 01 08:00:00 CST 1970)
//本意只是修改pig的生日
pig.getBirthday().setTime(666666666666L);
System.out.println(pig); // Pig(name=佩奇, birthday=Sat Feb 16 09:11:06 CST 1991)
//cPig也改了,这个结果不是我想要的,原因出在浅拷贝上
System.out.println(cPig); // Pig(name=佩奇, birthday=Sat Feb 16 09:11:06 CST 1991)
}
3.2 深克隆两种实现方式
@Data
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;}
// 1、深拷贝——使用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;
}
// 2、深拷贝——通过对象的序列化实现 (推荐)
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) {}
}
}
}
@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=Thu Jan 01 08:00:00 CST 1970)
System.out.println(cPig); // Pig(name=佩奇, birthday=Thu Jan 01 08:00:00 CST 1970)
pig.getBirthday().setTime(666666666666L);
System.out.println(pig); // Pig(name=佩奇, birthday=Sat Feb 16 09:11:06 CST 1991)
System.out.println(cPig); // Pig(name=佩奇, birthday=Thu Jan 01 08:00:00 CST 1970)
}
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;
}