设计模式之原型模式(java深浅克隆)

一、定义

原型模式(Prototype pattern)属于创造性设计模式,是指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
单例模式多次请求后得到的实例其实都只有一个,但是原型模式每次请求都会得到一个新的实例,只不过这个新的实例不是new出来的,而是原先的实例clone出来的。

二、适用场景
  1. 创建的对象实例较为复杂,如构造函数中含有多个参数,通过new生成对象实例花费高。
  2. 解耦框架与类,不通过具体类名,而是预先的"注册"一个"原型"实例,之后通过复制这个原型实例来生成新的实例。
  3. 原型模式一般一般与工厂模式结合使用,通过clone(),提供实例

Q: 为什么clone会比new的效率高❓

package com.selenium.demo;
public class AA implements Cloneable {
    public AA() {
        System.out.println("构造函数被调用");
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        AA aa = new AA();
        AA bb = (AA) aa.clone();
    }
}

在这里插入图片描述
可以看出只有在new AA()的时候调用了一次构造方法,clone()的时候并没有调用构造方法.

❗️A: 因为clone是native方法,直接在内存中复制数据块,操作二进制流,不会调用构造方法,少了对象的初始化的一些操作。❗️

三、主要角色

在这里插入图片描述

  • Client(使用者): 负责使用复制实例的方法来复制实例
  • Prototype(原型): 原型是接口类型,用来定义复制实例的方法
  • ConcretePrototype(具体原型): 复制实例的方法的具体实现类
四、对象的深浅克隆
1.错误的克隆

刚学java的同学,可能想通过=不就实现了克隆吗?我真是个小天才!!!

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person{
    private int id;
    private String name;
    private Dog dog;

    public static void main(String[] args) throws Exception {
        Dog dog = new Dog(1, "dog");
        Person person = new Person(1, "person", dog);
        Person person1 = person;
        System.out.println("person==person1: " + (person == person1));
    }
}

❌但是这样是错误的,=并没有重新开辟内存空间,而是将新的引用指向了同一块内存空间而已.

在这里插入图片描述
person和person1的内存地址相同,说明两个引用指向了同一块内存空间.
在这里插入图片描述

2.浅克隆

java.lang.Object中有native clone()方法,而我们实现克隆只需要实现Cloneable接口。

Cloneable、Serializable这些接口都是空实现,作用都是起标识作用,标识这个对象可以被克隆、被序列化.

java默认的clone方法实现的是浅克隆,即:只克隆了基本数据类型,而不克隆引用类型

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Cloneable {
    private int id;
    private String name;
    private Dog dog;

    @Override
    protected Person clone() throws CloneNotSupportedException {
        return ((Person) super.clone());
    }
    public static void main(String[] args) throws Exception {
        Dog dog = new Dog(1, "dog");
        Person person = new Person(1, "person", dog);
        Person person2 = person.clone();
        //浅克隆:克隆对象内的基本数据类型,引用类型不被克隆
        System.out.println("person2==person: " + (person2 == person));
        System.out.println("person2.dog==person.dog:" + (person2.dog == person.dog));
    }
}

在这里插入图片描述
可以看到,person的实例不同了,但是它们的dog实例还是同一个实例.
在这里插入图片描述

3.深克隆

了解了浅克隆,很容易就知道深克隆的概念,即:既克隆了基本数据类型,也克隆了引用类型
在这里插入图片描述

方式一:通过将引用类型手动clone实现深克隆
  1. 将Dog也实现Cloneable接口
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog implements Cloneable{
    private int id;
    private String name;

    @Override
    protected Dog clone() throws CloneNotSupportedException {
        return (Dog) super.clone();
    }
}
  1. 手动clone Dog
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Cloneable, Serializable {
    private int id;
    private String name;
    private Dog dog;

    @Override
    protected Person clone() throws CloneNotSupportedException {
        return ((Person) super.clone());
    }

    public static void main(String[] args) throws Exception {
        Dog dog = new Dog(1, "dog");
        Person person = new Person(1, "person", dog);
        //深克隆:1.将引用对象也克隆
        Person person3 = person.clone();
        //手动clone Dog
        person3.setDog(dog.clone());
        System.out.println("person3==person: " + (person3 == person));
        System.out.println("person3.dog==person.dog:" + (person3.dog == person.dog));
    }
}

在这里插入图片描述
可以看到,person和dog都不是原来的实例了,都被clone了.

方式二:通过序列化实现深克隆

方式一实现深克隆的方式很直观,但是弊端也很明显:如果含有多个引用类型,或者引用类型又有引用类型,就很麻烦.
所以通过Serializable实现序列化(Andriod推荐使用Parcelable)的方式实现深克隆才是最常用的方式.

  1. Dog实现Serializable接口
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog implements Serializable {
    private int id;
    private String name;
}
  1. Person实现Serializable接口
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Serializable {
    private int id;
    private String name;
    private Dog dog;
    public static void main(String[] args) throws Exception {
        Dog dog = new Dog(1, "dog");
        Person person = new Person(1, "person", dog);
        //深克隆:2.通过序列化的方式
        //将序列化对象写入流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(bos);
        objectOutputStream.writeObject(person);
        //将序列化对象从流中反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(bis);
        Person person4 = (Person) objectInputStream.readObject();
        System.out.println("person4==person: " + (person4 == person));
        System.out.println("person4.dog==person.dog:" + (person4.dog == person.dog));
    }
}

在这里插入图片描述
可以看到,person和dog也都不是原来的实例了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Selenium399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值