模式定义
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
模式结构
代码实现
public interface Prototype {
Prototype clone();
}
public class ConcretePrototype1 implements Prototype {
public Prototype clone() {
return new ConcretePrototype1();
}
}
public class ConcretePrototype2 implements Prototype {
public Prototype clone() {
return new ConcretePrototype2();
}
}
@AllArgsConstructor
public class Client {
private Prototype prototype;
public void operation(){
Prototype newPrototype = prototype.clone();
}
}
模式的优缺点
优点
创建对象的性能高
使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。简化对象的创建
缺点
克隆的对象的成员变量包含引用类型(String除外)需要特殊处理
逃避构造方法的约束
必须实现Cloneable接口
深拷贝和浅拷贝
发生深拷贝的有Java中的8中基本类型以及它们包装类型,另外还有String类型。其余的都是浅拷贝。
浅拷贝
对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象。
@Data
@AllArgsConstructor
public class Student implements Cloneable{
private String studentName;
private Teacher teacher;
@Override
protected Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
}
@Data
public class Teacher implements Serializable, Cloneable {
private static final long UID = 6948989635489677685L;
private String name;
public Teacher(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Teacher teacher;
teacher = (Teacher) super.clone();
return teacher;
}
}
@Data
public class StudentLow implements Serializable, Cloneable {
private static final long UID = 6948989635489677685L;
private String studentName;
private Teacher teacher;
public StudentLow(String studentName, Teacher teacher) {
this.studentName = studentName;
this.teacher = teacher;
}
@Override
protected Object clone() throws CloneNotSupportedException {
StudentLow student;
student = (StudentLow) super.clone();
return student;
}
}
public class Client {
public static void main(String[] args) throws Exception {
Teacher teacher = new Teacher("snail");
StudentLow student1 = new StudentLow("wjk", teacher);
StudentLow student2 = (StudentLow) student1.clone();
student2.getTeacher().setName("snail改变");
System.out.println(student1.getTeacher().getName());
System.out.println(student2.getTeacher().getName());
}
}
//运行结果(修改克隆的Student的成员变量Teacher的成员变量name导致被克隆的对象的也改变)
snail
snail改变
深拷贝
对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制。
//将上面代码StudentLow类使用StudentDeep类替换
@Data
public class StudentDeep implements Serializable, Cloneable {
private static final long UID = 6948989635489677685L;
private String studentName;
private Teacher teacher;
public StudentDeep(String studentName, Teacher teacher) {
this.studentName = studentName;
this.teacher = teacher;
}
@Override
protected Object clone() throws CloneNotSupportedException {
StudentDeep student;
student = (StudentDeep) super.clone();
//继续克隆Teacher
student.setTeacher((Teacher) this.teacher.clone());
return student;
}
/**
* 使用序列化实现深拷贝
*/
/*public Object deepClone() {
try {
//将对象写到流里
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
//从流里读出来
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());
} catch (Exception e) {
return null;
}
}*/
}
思考
模式本质:克隆生成对象。
开发中的应用场景
如果一个系统想要独立于它想要使用的对象时,可以使用原型模式,让系统只面向接口编程,在系统需要新的对象的时候,可以通过克隆来得到。
如果需要实例化的类事在运行时刻动态指定是,可以使用原型模式,通过克隆原型来得到需要的实例。
注意
- 使用原型模式不会调用类的构造方法.
对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,因此不会调用到类的构造方法。不但构造方法中的代码不会执行,甚至连访问权限都对原型模式无效。还记得单例模式吗?单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限,所以,单例模式与原型模式是冲突的,在使用时要特别注意。