前言
在阎宏博士的《JAVA与模式》一书中开头是这样描述原型(Prototype)模式的:原型模式属于对象的创建模式。通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。这就是选型模式的用意。
一、什么是原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆
。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
例子:设计过程中发现一个类中具有100多个属性,但是每次实际需要变换的属性只有几个这个时候我们就要一种设计模式使得我们设计的对象跟之前对象保持一样的属性值,这样我们只需要修改部分属性值就可以达到效果,这个时候就需要使用–原型模式。
二、实现原型模式
原型模式的实现原理非常简单,首先需要一个可以被克隆的原型对象,然后通过调用该原型对象的克隆方法,从而得到一个新的对象。
在 Java 中,Object 类提供了一个 clone() 方法,该方法可以将一个对象复制一份,但需要实现Cloneable 接口。在原型模式中有2中不同深度的clone概念。浅拷贝和深拷贝。下面就以案例详细讲解着2个概念的区别。
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
/**
*浅拷贝案例
**/
public class PersonTest {
public static void main(String[] args) throws CloneNotSupportedException {
//原始对象
Person person = new Person();
Date data=new Date();
//原始对象(引用数据类型赋值)
person.setDate(data);
//原始对象(基本数据类型赋值)
person.setAge(22);
//克隆原始对象产生新对象
Person person1 = (Person) person.clone();
System.out.println(person);
System.out.println(person1);
System.out.println(person==person1);
System.out.println("-------------------");
//改变原始对象(引用数据类型值)
data.setTime(123123213);
person.setDate(data);
//改变原始对象(基本数据类型值)
person.setAge(25);
System.out.println(person);
System.out.println(person1);
System.out.println(person==person1);
}
}
//实现Cloneable接口
@Data
class Person implements Cloneable {
//成员变量(基本数据类型)
private Integer age;
//成员变量(引用数据类型)
private Date date;
//重写Cloneable的clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Person{" +
", age=" + age +
", date=" + date +
'}';
}
}
通过控制台可以得出以下结论
-
clone的对象是在内存中重新创建的对象
-
在浅拷贝中,当原始对象中的基本数据类型的成员变量改变时不会影响新对象,而当原始对象中的引用数据类型的成员变量改变时,新对象中对应的成员变量也会改变。
-
深拷贝(Cloneable 序列化)
在深拷贝中,无论原型对象的成员变量是基本数据类型还是引用类型,都将复制一份给克隆对象,深克隆将原型对象的所有引用对象也复制一份给克隆对象。
简单来说,在深克隆中,除了对象本身被复制外,对象所包含的所有成员变量也将复制,这样对于改变引用数据类型就不会出现clone对象的引用数据值跟着改变
/**
*深拷贝案例
**/
public class PersonTest {
public static void main(String[] args) throws CloneNotSupportedException {
//原始对象
Person person = new Person();
Date data=new Date();
//原始对象(引用数据类型赋值)
person.setDate(data);
//原始对象(基本数据类型赋值)
person.setAge(22);
//克隆原始对象产生新对象
Person person1 = (Person) person.clone();
System.out.println(person);
System.out.println(person1);
System.out.println(person==person1);
System.out.println("-------------------");
//改变原始对象(引用数据类型值)
data.setTime(123123213);
person.setDate(data);
//改变原始对象(基本数据类型值)
person.setAge(25);
System.out.println(person);
System.out.println(person1);
System.out.println(person==person1);
}
}
//实现Cloneable Serializable接口
@Data
class Person implements Cloneable, Serializable {
//成员变量(基本数据类型)
private Integer age;
//成员变量(引用数据类型)
private Date date;
//重写Cloneable的clone方法,该方法内部实现序列化和反序列化
@Override
protected Object clone() throws CloneNotSupportedException {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(bo);
objectOutputStream.writeObject(this);
byte[] bytes = bo.toByteArray();
ByteArrayInputStream bs = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(bs);
Object object = objectInputStream.readObject();
return object;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "Person{" +
", age=" + age +
", date=" + date +
'}';
}
}
通过控制台可以得出以下结论
- 在深拷贝中,当原始对象中的成员变量类型为引用类型还是基本类型改变时不会影响新对象。