lengjingbuliaoyidian
文章15
标签7
分类7
原型模式

原型模式

原型模式(Prototype Pattern)

  • 概述:
    原型模式(Prototype Pattern)是创建型模式,用于当创建对象的成本高时,通过复制一个现有对象来创建一个新对象,从而避免创建一个新对象的 。
    这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
  • 结构:
    • 抽象原型类:定义了一个克隆自身的接口,即规定了具体原型对象必须实现的clone()方法。
    • 具体原型类:实现抽象原型类的clone()方法,它是可被复制的对象。
    • 客户端类:使用具体原型类中的clone()方法来克隆新的对象。
  • 实现:
    原型模式的克隆分为:浅克隆和深克隆。
  1. 浅克隆创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,任指向原有属性所指向的对象的内存地址。
  2. 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不会指向原有对象地址。
  • 注意:Java中的Object类提供了clone()方法,该方法返回一个对象,该对象是当前对象的一个副本。
    • 浅克隆:调用Object类的clone()方法。
    • 深克隆:
      1. 实现Cloneable接口。
      2. 重写clone()方法。
      3. 在clone()方法中,使用super.clone()方法创建一个新对象,然后将当前对象的属性值复制到新对象中。
      4. 如果属性是引用类型,需要递归调用clone()方法。

在Java中,原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是通过新建类实例的方式。在实现原型模式时,我们通常会使用clone()方法,而复制对象时就会涉及到浅克隆(Shallow Clone)和深克隆(Deep Clone)的概念。

浅克隆(Shallow Clone)

浅克隆是指当复制一个对象时,对于基本数据类型的成员变量,会直接复制其值;而对于引用类型的成员变量,则只复制其引用地址(也就是内存地址),而不会复制引用所指向的实际对象。因此,在浅克隆中,原始对象和克隆对象中的引用类型成员变量将指向同一个对象。
在Java中,通过实现Cloneable接口并重写Object类的clone()方法来实现浅克隆。Object类的clone()方法默认实现就是浅克隆。
示例代码:

class Address {
private String city;
public Address(String city) {
this.city = city;
}
// getter and setter
}
class Person implements Cloneable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// getter and setter
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅克隆
}
}
// 使用
Address address = new Address("Beijing");
Person p1 = new Person("Tom", address);
Person p2 = (Person) p1.clone();
// 此时,p1和p2的address引用指向同一个Address对象
System.out.println(p1.getAddress() == p2.getAddress()); // true

深克隆(Deep Clone)

深克隆是指复制对象时,不仅复制对象本身,而且递归复制对象所引用的其他对象。因此,深克隆会复制整个对象网络,使得原始对象和克隆对象之间互不影响,它们拥有各自独立的内存空间。
实现深克隆的方式有多种:

  1. 重写clone()方法,并在其中对引用类型进行递归克隆(需要引用类型也实现Cloneable接口并重写clone()方法)。
  2. 使用序列化(Serialization)和反序列化(Deserialization)来实现深克隆。将对象写入到字节流中,然后再从字节流中读取回来,这样会创建一个完全独立的副本。
    示例代码(通过重写clone方法实现深克隆):
    class Address implements Cloneable {
    private String city;
    public Address(String city) {
    this.city = city;
    }
    // getter and setter
    @Override
    protected Object clone() throws CloneNotSupportedException {
    return super.clone();
    }
    }
    class Person implements Cloneable {
    private String name;
    private Address address;
    public Person(String name, Address address) {
    this.name = name;
    this.address = address;
    }
    // getter and setter
    @Override
    protected Object clone() throws CloneNotSupportedException {
    Person cloned = (Person) super.clone();
    // 对引用类型单独克隆
    cloned.address = (Address) address.clone();
    return cloned;
    }
    }
    // 使用
    Address address = new Address("Beijing");
    Person p1 = new Person("Tom", address);
    Person p2 = (Person) p1.clone();
    // 此时,p1和p2的address引用指向不同的Address对象
    System.out.println(p1.getAddress() == p2.getAddress()); // false

序列化实现深克隆

如果引用类型嵌套层次很深,或者引用类型没有实现Cloneable接口,则可以通过序列化的方式实现深克隆。需要确保所有涉及的类都是可序列化的(实现Serializable接口)。
示例代码:

import java.io.*;
class Address implements Serializable {
private String city;
public Address(String city) {
this.city = city;
}
// getter and setter
}
class Person implements Serializable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// getter and setter
public Person 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 (Person) ois.readObject();
}
}
// 使用
Address address = new Address("Beijing");
Person p1 = new Person("Tom", address);
Person p2 = p1.deepClone();
System.out.println(p1.getAddress() == p2.getAddress()); // false

总结

  • 浅克隆:复制基本数据类型的值,对于引用类型,只复制引用地址,因此原对象和克隆对象会共享引用类型的成员变量。修改其中一个对象的引用类型成员变量,另一个对象也会受到影响。
  • 深克隆:复制基本数据类型的值,同时递归复制引用类型的对象。因此,原对象和克隆对象之间完全独立,互不影响。
    在实际应用中,选择浅克隆还是深克隆取决于具体需求。如果对象的引用类型成员变量在创建后不会改变,或者你希望共享这些对象,那么浅克隆就足够了,并且效率更高。如果需要完全独立的副本,则应该使用深克隆。但要注意深克隆可能带来的性能问题,特别是在对象图很大的情况下。

关键区别总结

特性 浅克隆 深克隆
引用类型复制 复制内存地址(共享对象) 递归复制实际对象(完全独立)
修改影响 修改引用类型成员会影响所有副本 修改引用类型成员不影响其他副本
实现复杂度 简单(默认clone() 复杂(需递归处理所有引用类型)
性能 高效(不创建新对象) 较低(递归创建新对象)
适用场景 引用类型不可变或无需隔离时 引用类型需完全隔离时

注意事项

  1. String的特殊性
    虽然String是引用类型,但其不可变性(Immutable)使得浅克隆中修改String值会创建新对象,不会影响原对象(行为类似深克隆)。但其他引用类型(如自定义类)仍需谨慎。
  2. 深克隆的替代方案
    序列化(Serializable)是另一种深克隆实现,但要求所有涉及的对象都实现Serializable接口:
    public static <T> T deepClone(T obj) throws IOException, ClassNotFoundException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);

    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bis);
    return (T) ois.readObject();
    }
本文作者:冷静不了一点
本文链接:http://example.com/2025/08/07/Java23%E7%A7%8D%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%20%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可