您现在的位置是:首页 >其他 >设计模式——原型模式(浅拷贝和深拷贝)网站首页其他

设计模式——原型模式(浅拷贝和深拷贝)

Strine 2023-07-13 04:00:02
简介设计模式——原型模式(浅拷贝和深拷贝)

是什么?

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象

结构

抽象原型类:规定了具体原型对象必须实现的Clone()方法;

具体原型类:实现抽象原型类的clone()方法,它是可被复制的对象;

访问类:使用具体原型类中的clone()方法来复制新的对象;

实现

浅拷贝

创建一个新对象,新对象的属性和原来对象完全相同,基本类型是值传递,但是对于引用类型则仍指向原有属性所指向的对象的内存地址;

Java中的Object类提供了clone方法来实现浅拷贝,我们只需要实现Cloneable接口再重写Object类中的clone方法即可,而这个Cloneable接口实际上就是上面所说的抽象原型类,而这个实现了Cloneable接口的实现类就是具体的原型类:

public class PrototypeCar implements Cloneable{
    private String name;
    private String no;
    private String Engine;

    public PrototypeCar(String name, String no, String engine) {
        this.name = name;
        this.no = no;
        Engine = engine;

    }

    @Override
    protected PrototypeCar clone() throws CloneNotSupportedException {
        return (PrototypeCar) super.clone();
    }
}
public class PrototypeDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        PrototypeCar tesla = new PrototypeCar("特斯拉", "12", "V12发动机");
        System.out.println("原型对象创建完毕,内存地址:"+tesla);
        PrototypeCar tesla2 = tesla.clone();
        System.out.println("克隆对象创建完毕,内存地址:"+tesla2);
    }
}

 注意:我们重写clone方法必须要实现Cloneable接口,否则它就会抛CloneNotSupportedException异常

存在的问题

当我们的原型类中存在其他引用类型,会存在下面这样的问题:

public class Engine {
    private String name;
    
    public Engine(String name) {
        this.name = name;
    }
}
public class PrototypeCar implements Cloneable{
    private String name;
    private String no;
    private Engine Engine;

    public PrototypeCar(String name, String no, Engine engine) {
        this.name = name;
        this.no = no;
        this.Engine = engine;

    }

克隆后内部的Engine引用对象内存地址还是指向原来的内存地址,并没有帮我们进行克隆,因此面对这种情况我们就要使用深拷贝;

深拷贝

创建一个新对象,属性中引用的其他对象依旧也会被克隆,不再指向原来的内存地址;

我们可以让内部的引用对象的类也去实现Cloneable接口并重写Clone方法,当然如果原型类中的引用对象比较多的话,这种方法明显就很复杂了,重复代码也很多,看起来就很恶心,因此我们一般不用;

我们这里提供一种常用的方案:对象流的方式来实现深拷贝

首先所有需要拷贝的类都需要实现序列化接口,否则会抛NotSerializableException异常;

public class Engine implements Serializable {
    private String name;

    public Engine(String name) {
        this.name = name;
    }
}
public class DeepCloneCar implements Serializable {
    private String name;
    private String no;
    private Engine engine;

通过对象流的方式实现深拷贝:

public DeepCloneCar deepClone() throws IOException, ClassNotFoundException {
    //序列化
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oops = new ObjectOutputStream(baos);
    oops.writeObject(this);
    //反序列化
    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bais);
    DeepCloneCar car = (DeepCloneCar) ois.readObject();
    baos.close();
    oops.close();
    bais.close();
    ois.close();
    return car;
}

最终测试一下:

    /**
     * 原型模式——深拷贝
     * */
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Engine engine = new Engine("V12发动机");
        DeepCloneCar tesla = new DeepCloneCar("特斯拉", "12", engine);
        System.out.println("原型对象创建完毕,内存地址:"+tesla);
        System.out.println("内部Enine对象的内存地址:"+tesla.getEngine());
        System.out.println("======================================");
        DeepCloneCar tesla2 = tesla.deepClone();
        System.out.println("克隆对象创建完毕,内存地址:"+tesla2);
        System.out.println("内部Engine对象的内存地址:"+tesla2.getEngine());

    }
}

 总结

当类需要通过原型模式进行拷贝的时候,如果原型类里面的属性没有引用类型,或者引用类型较少,且该类的属性不会频繁修改的情况下,我们可以使用浅拷贝;

但如果引用类型较多,或者后期需要添加或者修改里面的引用类型的属性时,我们选择深拷贝;

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。