1. 原型模式

1.1 介绍

指用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。常用于创建某对象的代价比较大时,例如一个对象需要在一个高代价的数据库操作之后被创建。

原型模式的克隆分为浅克隆和深克隆

  • 浅克隆:创建一个新对象,只负责克隆按值传递的数据(比如基本数据类型、String类型),而对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。Java中浅克隆必须实现Cloneable接口
  • 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。深克隆可通过序列化/反序列化,但克隆对象及克隆对象引用的对象必须实现序列化接口

1.2 优缺点

优点

  • 创建对象的性能提高
  • 无需调用构造函数

缺点

  • 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候
  • 必须实现Cloneable接口

1.3 使用场景

  • 资源优化场景,

  • 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等

  • 性能和安全要求的场景。

  • 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

  • 一个对象多个修改者的场景

1.4 注意事项

  • 与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable并重写Clone方法,调用的是底层JNI方法深拷贝是通过实现 Serializable 读取二进制流

2. 案例代码

现在,汽车工厂新建汽车需要多层审批,为了加载建造过程,我们通过复制原有的汽车实现增产。

abstract class CarMat {
   
    abstract String getName();
}
public class AudiCarMat extends CarMat implements Serializable {
   
    @Override
    String getName() {
   
        return "奥迪车垫";
    }
}

abstract class CarWheel {
   
    abstract String getName();
}
class AudiCarWheel extends CarWheel implements Serializable {
   
    
    @Override
    String getName() {
   
        return "奥迪轮子";
    }
}

public class Car implements Cloneable, Serializable {
   
    CarWheel wheel;
    CarMat mat;

    public void setWheel(CarWheel wheel) {
   
        this.wheel = wheel;
    }

    public void setMat(CarMat mat) {
   
        this.mat = mat;
    }

    public CarWheel getWheel() {
   
        return wheel;
    }

    public CarMat getMat() {
   
        return mat;
    }

	// 浅拷贝通过JNI实现,要求必须实现Cloneable接口
    public static Car shallowClone(Car car) {
   
        Car carClone = null;
        try {
   
            carClone = (Car)car.clone();
        } catch (CloneNotSupportedException e) {
   
            e.printStackTrace();
        }
        return carClone;
    }
    
	// 深拷贝通过序列化实现,要求必须实现Serializable接口
    public static Car deepClone(Car car) {
   
        ObjectOutputStream oos = null;
        Car carClone = null;
        try {
   
            oos = new ObjectOutputStream(new FileOutputStream("temp"));
            oos.writeObject(car);
            oos.flush();
            oos.close();
            
            ObjectInputStream ios = new ObjectInputStream(new FileInputStream("temp"));
            carClone = (Car)ios.readObject();
            ios.close();
        } catch (IOException e) {
   
            e.printStackTrace();
        } catch (Exception e) {
   
            e.printStackTrace();
        }


        return carClone;
    }

}

/** * 原型模式 * *@Author cly *@Date 2021/08/31 1:24 *@Version 1.0 */
public class Prototype {
   
    public static void main(String[] args) {
   
        Car car = new Car();
        car.setMat(new AudiCarMat());
        car.setWheel(new AudiCarWheel());
		// 浅拷贝
        Car cloneCar = Car.shallowClone(car);
        System.out.println(car.mat == cloneCar.mat);	//true
        System.out.println(car.wheel == cloneCar.wheel);//true
		// 深拷贝
        Car deepClone = Car.deepClone(car);
        System.out.println(car.mat == deepClone.mat);	// false
        System.out.println(car.wheel == deepClone.wheel);// false
    }
}

3. 源码实现

3.1 java.lang.object

在 JDK 中所有类的默认父类 java.lang.Object 中的 clone 方法,就实现快速地浅拷贝一个对象,当然有个前提条件,就是被克隆的对象的类需要实现 Cloneable 接口,否则会抛出异常 CloneNotSupportedException。

/** * Creates and returns a copy of this object. The precise meaning * of "copy" may depend on the class of the object. */
protected native Object clone() throws CloneNotSupportedException;