原型模型—Java的浅克隆和深克隆

原型模式

原型模式(Prototype Pattern)是一种对象创建型模式,用原型实例制定创建对象的种类,并且通过复制这些原型创建新的对象。

在Java中,用 克隆 实现了原型模式

浅克隆

  • 被复制对象的常量和引用对象都与源对象相同
  • 浅克隆仅复制当前对象,而不复制对象内的成员对象(即成员对象均指向源成员对象)

代码实现:

Teacher类:

@Data	// lombok 自动生成get和set
@AllArgsConstructor// lombok 自动生成构造函数
public class Teacher implements Cloneable { // 实现了Cloneable接口 克隆时使用

    public int age;
    public String name;
    public ArrayList<Student> students;
}

测试结果:

public static void main(String[] args) throws CloneNotSupportedException {

        ArrayList<Student> students = new ArrayList<Student>();
        students.add(new Student("jjj"));

        Teacher teacher = new Teacher(11, "aaa",students);
        Teacher teacher1 = (Teacher) teacher.clone();
            
    	System.out.println(teacher == teacher1);// false
        System.out.println(teacher.students == teacher1.students);// true
        
    }

解释

  • Teacher类实现了Cloneable接口,
  • 则在进行克隆时生成的teacher1是一个新的对象,
  • 但由于没有对类内部成员进行进一步克隆,所以teacher.students指向的是同一个变量
  • 这就是浅克隆

解决内部成员对象的克隆问题,就成为了深克隆↓

深克隆

  • 成员变量与成员对象的值与源克隆对象值相同
  • 成员对象所指向的地址(成员对象)与源克隆对象不同
  • 引用对象均与源对象内的成员不同,但值相同

使用Cloneable接口实现深克隆(推荐)

Cloneable接口

由于浅克隆没有对类内部成员对象进行克隆,则我们可以通过重写clone()方法自定义我们需要克隆的成员对象

需要注意的是,克隆对象的clone()方法并不属于Cloneable接口,它是Object类的一个方法。这一方法的定义如下:

protected native Object clone() throws CloneNotSupportedException;

可以看到他是一个protected方法,即**Object的子类**才可以使用此方法。如果在没有重写Object的clone()方法且没有实现Cloneable接口的实例上调用clone方法,会报CloneNotSupportedException异常。

为什么一定要实现一个空的Cloneable接口呢?

原因在于它相当于一个标志,没有实现该接口的类是无法调用clone()方法的。这里的标志判断是在native方法中进行。

我们在重写clone()方法后,需要将该方法设置为public

重写clone方法
@Override
public Object clone() throws CloneNotSupportedException {
    Teacher teacher = (Teacher) super.clone();// 调用super类克隆一个teacher实例  相对于 new Object
	
    // teacher.age = this.age;  // age为int类型,是常量不需新建对象
    // teacher.name = this.name ;// name为string类型 也是常量不需新建  具体疑惑请看文章结尾
    teacher.students = (ArrayList) this.students.clone();// students为引用类型,需要进一步克隆,使得源students与新students值相同,对象不同。完成深克隆

    return teacher;// 返回克隆成功后的对象
}

解释:

  • 成员变量为基本类型时(常量)不用进行二次克隆
  • 成员变量为引用类型时(对象)需要进行二次克隆
  • 成员变量为String类型时 此时String为变量,不需二次克隆 (具体看结尾处)
测试结果
public static void main(String[] args) throws CloneNotSupportedException {

    ArrayList<Student> students = new ArrayList<Student>();

    students.add(new Student("jjj"));

    Teacher teacher = new Teacher(11, new StringBuffer("aaa"),students);

    Teacher teacher1 = (Teacher) teacher.clone();

    System.out.println(teacher == teacher1);// false
    System.out.println(teacher.students == teacher1.students);// false
}

可见,两位老师的学生都不是同一个学生了,深克隆成功!

使用序列化与反序列化实现深克隆(存在线程安全问题)

序列化 - Serializable

定义:将实现了Serializable接口(标记型接口)的对象转换成一个字节数组,并可以将该字节数组转为原来的对象。****

在此处我们运用Java序列化时能够保存对象信息反序列化时会新建对象存储信息的机制,完成Java的深克隆

要使用序列化与反序列化,只需要实现 Serializable接口即可;

具体实现
public Teacher deepClone() throws IOException, ClassNotFoundException {
    // 将对象写入流中
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
    objectOutputStream.writeObject(this); // 传入自身这个对象
    
    //将对象从流中取出
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
    return (Teacher) objectInputStream.readObject();

}
测试结果

结果与Clone的结果一致,在此就不进行展示了

java中String 类对象在克隆时候的特殊性

点击此处跳转

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值