参考Java知音:https://mp.weixin.qq.com/s/VPJXnviIDuk0Q7v68jA8zQ
概述:
原型模式就是复制现有的对象实例来创建一个新的对象实例
实现:
1、实现Cloneable接口:
Cloneable接口的作用是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
重写Object类中的clone方法:
Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,原型类需要将clone方法的作用域修改为public类型。
一个栗子:
package com.wuhongyu.prototype;
import lombok.Data;
//这里使用了Lomok,其实就是省略了get/set方法
@Data
public class Student implements Cloneable{
private Integer id;
private String name;
private Integer age;
private String address;
private String email;
/**
* 这里将protected改为public
* @return
* @throws CloneNotSupportedException
*/
@Override
public Student clone() {
Student student = null;
try {
student = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
/**
* 测试主函数
* @param args
*/
public static void main(String[] args) {
//往对象里注入数据
Student s1 = new Student();
s1.setId(1111111);
s1.setName("张三");
s1.setAge(20);
s1.setAddress("陕西省");
s1.setEmail("1000000@qq.com");
Student s2 = s1.clone();
System.out.println(s1);
System.out.println(s2);
System.out.println("s1.equals(s2)?"+(s1.equals(s2)));
System.out.println("s1==s2?"+(s1==s2));
System.out.println("s1.getClass()==s2.getClass()?"+ (s1.getClass()==s2.getClass()));
}
}
运行结果:

可以看出,s1与s2确实是两个不同的对象,但是他们的类型相同,并且数据也相同。这就是原型模式就用处。
优点:
原型模式的效率比直接new一个对象要快上很多特别是复制大对象的时候,而且对于一些复杂的对象(比如你需要set 100个属性)复制起来非常的方便。
缺点:
1、由于原型模式创建对象没有使用构造方法,所以该对象不能使用单例模式。
2、不能有final对象
3、Object类的clone方法只会拷贝对象中的基本数据类型,对于数组,引用对象等只能另行拷贝。这里涉及到深拷贝和浅拷贝的概念。
浅拷贝:
将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的(这样不安全)。
深拷贝:
将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。
这里就需要自己手动去创建啦
深拷贝的一个栗子:
首先我们需要实现一个班级的类,和学生类一样也需要Cloneable接口和重写clone()方法
import lombok.Data;
@Data
public class Class implements Cloneable{
private Integer id;
private String name;
@Override
public Class clone() {
Class clazz = null;
try {
clazz = (Class)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clazz;
}
}
接下来在Student类中添加班级的引用和通过班级类的clone方法创建的clazz:
private String address;
private String email;
//添加班级的引用
private Class clazz;
@Override
public Student clone() {
Student student = null;
try {
student = (Student) super.clone();
//这个引用类型也需要实现Colnealbe接口和重写clone方法
student.clazz = this.clazz.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
下面我们来测试一下:
/**
* 测试主函数
* @param args
*/
public static void main(String[] args) {
//往对象里注入数据
Student s1 = new Student();
s1.setId(1111111);
s1.setName("张三");
s1.setAge(20);
s1.setAddress("陕西省");
s1.setEmail("1000000@qq.com");
Class c = new Class();
c.setId(1);
c.setName("2班");
s1.setClazz(c);
Student s2 = s1.clone();
System.out.println(s1);
System.out.println(s2);
System.out.println("s1.equals(s2)?"+(s1.equals(s2)));
System.out.println("s1==s2?"+(s1==s2));
System.out.println("s1.getClass()==s2.getClass()?"+(s1.getClass()==s2.getClass()));
System.out.println("----------------------------");
System.out.println("s1中的班级对象和s2中的班级对象相等吗?");
System.out.println("s1.clazz == s2.clazz?"+(s1.clazz == s2.clazz));
System.out.println("s1.clazz.equals(s2.clazz)?"+(s1.clazz.equals(s2.clazz)));
System.out.println("s1.clazz.getClass()==s2.clazz.getClass()?"+(s1.clazz.getClass()==s2.clazz.getClass()));
}
测试结果:

我们可以看出,和student测试的结果一样,里面的两个clazz分别是不同的对象,但是他们中的值相同。说明我们的深拷贝成功了。
适用场景:
1,复制对象的结构和数据。
2,希望对目标对象的修改不影响既有的原型对象。
3,创建一个对象的成本比较大
网友评论