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;
}