原型模式
原型模式
创建对象的方法:构造器,克隆,反射,序列化
什么是原型模式
原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
优点
- java自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点
- 需要为每一个类都配置一个 clone 方法
- clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
- 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
如何实现原型模式
场景:在一个公司中每天都要写日报,记录每天的日程,日报中还有一些建议什么的,大多数情况下员工每天只需要填写今天的日常,建议这一栏大多数情况下都是不用修改的,但我们却每天都需要填写这一栏。
原型模式的实现主要是浅克隆和深克隆
- 浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
- 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
浅克隆
浅克隆是通过Object类提供的clone()方法来实现的,要克隆的类需要实现Cloneable接口
// 浅克隆
public class ShallowClone {
public static void main(String[] args) {
Student stu = new Student();
Address addr = new Address();
addr.setAdd("长沙市");
stu.setNumber(12345);
stu.setAddress(addr);
try {
Student cloneStu = (Student) stu.clone();
System.out.println("学生1:" + stu.getNumber() + "-" + stu.getAddress().getAdd());
System.out.println("学生2:" + cloneStu.getNumber() + "-" + stu.getAddress().getAdd());
stu.setNumber(54321);
// 原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。
// 所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法
addr.setAdd("西湖区");
System.out.println("学生1:" + stu.getNumber() + "-" + stu.getAddress().getAdd());
System.out.println("学生2:" + cloneStu.getNumber() + "-" + stu.getAddress().getAdd());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
class Student implements Cloneable {
private int number;
private Address address;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student stu = null;
try {
// 浅拷贝
stu = (Student) super.clone();
// 深拷贝
stu.address = (Address) address.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
class Address implements Cloneable {
private String add;
public String getAdd() {
return add;
}
public void setAdd(String add) {
this.add = add;
}
@Override
public String toString() {
return "Address{" +
"add='" + add + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Address addr = null;
addr = (Address) super.clone();
return addr;
}
}
深克隆
深克隆是通过对象的序列化和反序列化来实现的
public class DeepClone implements Serializable {
private static final long serializableUID = 369285298572941L;
// 深克隆,需要对象和对象的所有属性都序列化
public static <T extends Serializable> T myDeepClone(T obj) throws IOException, ClassNotFoundException {
// 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
// 将流序列化为对象,从内存中取出数据baos.toByteArray()
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (T) ois.readObject();
}
}
class Person implements Serializable {
private static final long serialVersionUID
= -9102017020286042305L;
private String name; // 姓名
private int age; // 年龄
private Car car; // 座驾
public Person(String name, int age, Car car) {
this.name = name;
this.age = age;
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", car=" + car +
'}';
}
}
/**
* 小汽车类
*
* @author nnngu
*/
class Car implements Serializable {
private static final long serialVersionUID
= -5713945027627603702L;
private String brand; // 品牌
private int maxSpeed; // 最高时速
public Car(String brand, int maxSpeed) {
this.brand = brand;
this.maxSpeed = maxSpeed;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
@Override
public String toString() {
return "test.Car{" +
"brand='" + brand + '\'' +
", maxSpeed=" + maxSpeed +
'}';
}
}
class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person p1 = new Person("郭靖", 33,
new Car("Benz", 300));
Person p2 = DeepClone.myDeepClone(p1); // 深度克隆
// 修改克隆的Person对象p2关联的汽车对象的品牌属性
// 原来的Person对象p1关联的汽车不会受到任何影响
// 因为在克隆Person对象时其关联的汽车对象也被克隆了
p2.getCar().setBrand("BYD");
System.out.println(p1);
}
}
原型模式应用场景
- 对象之间相同或相似,即只是个别的几个属性不同的时候。
- 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
- 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
- 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。