原型模式
概念
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
实现
原型模式的克隆分为浅克隆和深克隆。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Java中已经提供了clone()的方法,他就是一个浅克隆,如果我们写的类,需要使用原型模式,只需要实现Cloneable接口即可。
但是对于String类型来说,String类型有不可变的特性,所以当你改变了克隆后的值后,其实是新建了一个新的对象,所以对原来的对象没有任何影响,所以下例中的username才会被克隆成功。
浅克隆的类
public class ShallowClone implements Cloneable{
private String username;
private int age;
public ShallowClone() {
System.out.println("无参构造方法");
}
public ShallowClone(String username, int age) {
System.out.println("全参构造方法");
this.username = username;
this.age = age;
}
public void setUsername(String username) {
this.username = username;
}
public void setAge(int age) {
this.age = age;
}
@Override
protected ShallowClone clone() throws CloneNotSupportedException {
System.out.println("克隆方法");
return (ShallowClone) super.clone();
}
@Override
public String toString() {
return "ShallowClone{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
测试代码
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
ShallowClone clone1 = new ShallowClone("张三", 12);
ShallowClone clone2 = clone1.clone();
clone2.setUsername("李四");
clone2.setAge(18);
System.out.println("clone1 = " + clone1);
System.out.println("clone2 = " + clone2);
}
}
可以看到,调用clone方法的时候,他底层不是采用构造方法去新建一个对象的,也就是new的方式,他是直接给内存分配一个大小相同的空间,然后采用直接赋值的方式。
使用场景
- 对象的创建非常复杂,可以使用原型模式快捷的创建对象。
-
性能和安全要求比较高。
深克隆
浅克隆中的问题
我们先来看看浅克隆中存在的问题。
这里我们让原来的那个类中的姓名和年龄变成学生类,改造如下:
原型类
public class ShallowClone implements Cloneable{
private Student student;
public void setStudent(Student student) {
this.student = student;
}
public Student getStudent() {
return student;
}
@Override
public String toString() {
return "ShallowClone{" +
"student=" + student +
'}';
}
@Override
protected ShallowClone clone() throws CloneNotSupportedException {
return (ShallowClone) super.clone();
}
}
学生类
public class Student {
private String username;
private int age;
public void setUsername(String username) {
this.username = username;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
测试方法
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
ShallowClone clone = new ShallowClone();
Student student = new Student();
student.setUsername("张三");
student.setAge(12);
clone.setStudent(student);
ShallowClone clone1 = clone.clone();
clone1.getStudent().setUsername("李四");
clone1.getStudent().setAge(18);
System.out.println("clone = " + clone);
System.out.println("clone1 = " + clone1);
System.out.println(clone.getStudent() == clone1.getStudent());
}
}
输出结果如下:
发现student学生类并没有clone成功,修改的还是同一个学生类,这就是浅克隆存在的问题。
实现深克隆方式一
多层嵌套clone方法即可。也就是在原型类的clone方法中,把里面的非基本数据类型也clone一次,然后复制即可。
原型类
public class DeepClone implements Cloneable{
private Student student;
public void setStudent(Student student) {
this.student = student;
}
public Student getStudent() {
return student;
}
@Override
public String toString() {
return "ShallowClone{" +
"student=" + student +
'}';
}
@Override
protected DeepClone clone() throws CloneNotSupportedException {
//注意这里,把非基本类型的学生类clone了一次,并赋值在新的clone对象中
Student clone = student.clone();
DeepClone deepClone = (DeepClone) super.clone();
deepClone.student = clone;
return deepClone;
}
}
学生类
public class Student implements Cloneable{
private String username;
private int age;
public void setUsername(String username) {
this.username = username;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
@Override
protected Student clone() throws CloneNotSupportedException {
//原型类中的非基本数据类型也需要重写clone方法
return (Student) super.clone();
}
}
测试方法和上面的一样,不需要做任何的修改。结果输出如下:
可以看到,深克隆成功了,但是我们需要去手动维护这个clone方法。
实现深克隆方式二
还记得如何破坏单例模式吗,序列化和反序列化是其中的一种方式,所以我们只需要利用这一个特点,把对象写入文件中,然后再读取出来,那么就可以完成一次完美的深克隆啦。
原型类
public class DeepClone implements Cloneable, Serializable {
private Student student;
public void setStudent(Student student) {
this.student = student;
}
public Student getStudent() {
return student;
}
@Override
public String toString() {
return "ShallowClone{" +
"student=" + student +
'}';
}
@Override
protected DeepClone clone() throws CloneNotSupportedException {
DeepClone deepClone = (DeepClone) super.clone();
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("F:\\a.txt"));
oos.writeObject(deepClone);
oos.close();
ois = new ObjectInputStream(new FileInputStream("F:\\a.txt"));
DeepClone clone = (DeepClone) ois.readObject();
ois.close();
return clone;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
学生类
public class Student implements Serializable {
private String username;
private int age;
public void setUsername(String username) {
this.username = username;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
最后测试输出结果如下:
可以看到,也实现了一次深拷贝。
实现深克隆方法三
调用第三方工具类库,比如Json工具类(先转换为Json字符串,然后再转换为对象)、Spring的BeanUtils(Spring项目中使用比较方便)、Cglib的BeanCopier(速度最快)、Apache的BeanUtils(该工具类比较慢,谨慎使用!)。原理同实现方法二,只不过人家帮你封装好了。