原型模式(Prototype)
原型模式是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。调用者不需要知道任何创建细节,不调用构造函数
- 主要应用:
- 浅拷贝
- 深拷贝
原型模式:
- 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
public class Cat implements Cloneable {
private String name;
private int total;
public Cat(String name, int total) {
this.name = name;
this.total = total;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", total=" + total +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Cat c1 = new Cat("短尾猫", 20);
Cat c2 = (Cat)c1.clone();
c2.setName("波斯猫");
System.out.println(" c1---------"+ c1.toString());
System.out.println(" c2---------"+ c2.toString());
}
}
可以看到,把一个Cat复制过来,只是改了name而已,其他属性完全一样没有改变,需要注意的是,一定要在被拷贝的对象上实现Cloneable接口,否则会抛出CloneNotSupportedException异常
浅拷贝
创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址
public class Animal implements Cloneable {
private String type;
private Cat cat;
public Animal(String type, Cat cat) {
this.type = type;
this.cat = cat;
}
public Animal() {}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Animal{" +
"type='" + type + '\'' +
", cat=" + cat +
'}';
}
public static void main(String[] args) throws Exception {
Animal animal = new Animal();
animal.setType("杂食动物");
Cat cat = new Cat("小小猫", 12);
animal.setCat(cat);
Animal a2 = (Animal) animal.clone();
Cat c2 = a2.getCat();
c2.setName("大花猫");
System.out.println("a1-----" + animal.toString());
System.out.println("a2-----" + a2.toString());
}
}
可以看到,当修改了c2
的姓名时,cat
的姓名同样也被修改了,这说明c2和cat
是同一个对象
,这就是浅克隆的特点,对具体原型类中的引用类型的属性进行引用的复制。同时,这也可能是浅克隆所带来的弊端,因为结合该例子的原意,显然是想在Animal
新增一种”大花猫“类型的Cat,而非让所有的Cat都改名叫”大花猫“,于是我们这里就要使用深克隆
深拷贝
创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址
public class Animal implements Cloneable,Serializable{
private String type;
private Cat cat;
public Animal(String type, Cat cat) {
this.type = type;
this.cat = cat;
}
public Animal() {}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
protected Object deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
@Override
public String toString() {
return "Animal{" +
"type='" + type + '\'' +
", cat=" + cat +
'}';
}
public static void main(String[] args) throws Exception {
Animal animal = new Animal();
animal.setType("杂食动物");
Cat cat = new Cat("小小猫", 12);
animal.setCat(cat);
Animal a2 = (Animal)animal.deepClone();
Cat c2 = a2.getCat();
c2.setName("大花猫");
System.out.println("a1-----" + animal.toString());
System.out.println("a2-----" + a2.toString());
}
}
可以看到,当修改了c2
的name
时,cat
的name并没有被修改了,这说明c2和cat已经是不同的对象了,说明Animal 中的Cat也被克隆了,不再指向原有对象地址
,这就是深克隆
。这里需要注意的是,Animal 类和Cat类都需要实现Serializable接口,否则会抛出NotSerializableException异常。
优点
- 性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多。
- 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建的过程。
缺点
- 必须配备克隆(或者可拷贝)方法。
- 当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
- 深克隆、浅克隆需要运用得当。
适用场景
- 类初始化消耗资源较多。
- new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)。
- 构造函数比较复杂。
- 循环体中生产大量对象时。