首发于设计模式
秒懂 Java 的原型模式

秒懂 Java 的原型模式

Java 的原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新的对象,而无需显式地指定它们的类。

在原型模式中,一个对象作为原型,通过复制自己来创建新的对象。原型模式适用于对象的创建过程非常复杂的情况下,可以通过复制已有的对象来避免重复创建相似的对象,从而提高系统的性能。

原型模式有两种实现方式:浅克隆和深克隆。

1. 浅克隆

浅克隆只复制对象本身和其中的基本数据类型,而不会复制对象中的引用类型。也就是说,新对象中的引用类型变量和原对象中的引用类型变量指向同一个对象。

Java 中的 Object 类提供了一个 clone() 方法,可以实现浅克隆。要实现克隆,需要满足以下条件:

  • 实现 Cloneable 接口,否则会抛出 CloneNotSupportedException 异常;
  • 重写 clone() 方法,并使用 super.clone() 来创建新的对象。

以下是一个简单的实现浅克隆的例子:

class Person implements Cloneable {
    private String name;
    private int age;
    private Address address;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        Person clone = (Person) person.clone();
        System.out.println(person.address == clone.address);
    }

}

class Address {
    private String city;
    private String street;
}

在上面的例子中,Person 类包含一个引用类型的成员变量 Address。当我们克隆一个 Person 对象时,它会创建一个新的 Person 对象,并将其成员变量(nameage)复制到新对象中。然而,它不会复制 Address 对象,而是将新对象中的 address 引用指向原对象中的 address 对象。

2. 深克隆

深克隆会复制对象本身和其中的引用类型变量,也就是说,新对象中的引用类型变量和原对象中的引用类型变量指向不同的对象。下面是深克隆的几种方式:

2.1 序列化

序列化是一种将Java对象转换为字节流的技术,然后可以将字节流写入文件或通过网络传输。由于序列化将对象转换为字节流,因此可以使用序列化实现深克隆。首先将对象序列化到字节流中,然后再从字节流中反序列化出一个新的对象。

public static <T> T clone(T object) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            oos.close();

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            T clone = (T) ois.readObject();
            ois.close();

            return clone;
        } catch (Exception e) {
            throw new RuntimeException("Error cloning object", e);
        }
    }

2.2 cloneable 接口

Java中的Cloneable接口用于标记可克隆的对象。当对象实现Cloneable接口时,可以调用其clone()方法进行浅拷贝,但是这不会复制对象的内部状态。要实现深拷贝,必须重写clone()方法并手动复制内部状态。

public class MyClass implements Cloneable {
  private int myInt;
  private List<String> myList;

  @Override
  public MyClass clone() {
    try {
      MyClass clone = (MyClass) super.clone();
      clone.myList = new ArrayList<>(myList);
      return clone;
    } catch (CloneNotSupportedException e) {
      throw new RuntimeException("Error cloning object", e);
    }
  }
}

2.3 三方库

除了使用Java内置的克隆方法,还可以使用第三方库,如`Apache Commons LangGoogle Guava。这些库提供了许多实用程序方法,包括深克隆。

Apache Commons Lang

MyClass clone = (MyClass) SerializationUtils.clone(object);

Google Guava

MyClass clone = (MyClass) com.google.common.base.Objects.clone(object);

3. 总结

Java 原型模式是一种创建型设计模式,它允许你通过克隆现有对象来创建新对象,而无需通过实例化类并手动设置其属性来创建对象。以下是 Java 原型模式的优劣点:

优点:

  1. 减少对象创建的成本:使用原型模式可以通过复制现有对象来创建新对象,而无需创建新对象的成本。因为它避免了重新初始化和设置对象的属性。
  2. 简化对象的创建过程:原型模式可以避免复杂对象的创建过程,尤其是当创建对象需要进行一系列复杂的步骤时。这意味着你可以在实际需要时轻松地创建和配置对象,而不必在程序的许多地方执行大量的初始化和配置代码。
  3. 提高程序性能:在大多数情况下,复制对象比创建新对象更快。原型模式可以在运行时使用它已经存在的对象,这有助于提高程序的性能。

劣点:

  1. 复制对象可能会带来副作用:复制一个对象可能会导致副作用,例如如果原型对象的属性是可变的,那么它们的值可能会在对象被克隆时改变。
  2. 复制对象可能会导致深度克隆问题:如果一个对象具有多层嵌套结构,复制它可能会变得很困难,因为你需要确保复制所有嵌套的对象及其属性。这可能会导致深度克隆问题。
  3. 必须保证克隆的对象是可用的:在某些情况下,原型对象可能无法克隆,这可能是因为它没有实现 Cloneable 接口,或者它的构造函数是私有的。在这种情况下,原型模式无法使用。
编辑于 2023-12-08 17:10・IP 属地浙江