原型模式【浅克隆实现,使用序列化实现深克隆】Java代码演示


原型模式

原型模式(Prototype) 的定义如下:

  • 用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
  • 在这里,原型实例指定了要创建的对象的种类。
  • 用这种方式创建对象非常高效,根本无须知道对象创建的细节。

原型模式的优点:

  • Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。

原型模式的缺点:

  • 需要为每一个类都配置一个 clone 方法
  • clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
  • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。

uml 类图
在这里插入图片描述

  • Prototype:原型类,声明一个克隆自己的接口
  • ConcretePrototype:具体的原型类,实现一个克隆自己的操作
  • Client:让一个原型对象克隆自己,从而创建一个新的对象(属性一样)

代码演示:

  • Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类。
class Realizetype implements Cloneable {
    Realizetype() {
        System.out.println("具体原型创建成功!");
    }
    public Object clone() throws CloneNotSupportedException {
        System.out.println("具体原型复制成功!");
        return super.clone();
    }
}
//原型模式的测试类
public class PrototypeTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Realizetype obj1 = new Realizetype();
        Realizetype obj2 = (Realizetype) obj1.clone();
        System.out.println("obj1==obj2? " + (obj1 == obj2));
    }
}

运行结果:

具体原型创建成功!
具体原型复制成功!
obj1==obj2? false

Process finished with exit code 0

浅克隆

在 Java 提供的 API 中,不需要手动创建抽象原型接口。Java 中的 Object 类提供了浅克隆的 clone() 方法。因为 Java 已经内置了 Cloneable 抽象原型接口,自定义的类型只需实现该接口并重写 Object.clone() 方法就可实现对象的浅克隆。

一般情况下,使用 clone() 方法需要满足以下条件:

  • 对任何对象 o,都有 o.clone() != o。换言之,克隆对象与原型对象不是同一个对象。
  • 对任何对象 o,都有 o.clone().getClass() == o.getClass()。换言之,克隆对象与原型对象的类型一样。
  • 如果对象 o 的 equals() 方法定义恰当,则 o.clone().equals(o) 应当成立。

我们在设计自定义类的 clone() 方法时,应当遵守这 3 个条件。一般来说,这 3 个条件中的前 2 个是必需的,第 3 个是可选的。

代码实现浅克隆:

public class Client {

    public static void main(String[] args) {
        //创建原型对象
        ConcretePrototype type = new ConcretePrototype("original");
        System.out.println(type);
        //复制原型对象
        ConcretePrototype cloneType = null;
        try {
            cloneType = type.clone();
        }  catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        cloneType.desc = "clone";
        System.out.println(cloneType);
    }


    static class ConcretePrototype implements Cloneable { // 实现接口
        private String desc;
        public ConcretePrototype(String desc) {
            this.desc = desc;
        }
        // 重写clone
        @Override
        protected ConcretePrototype clone() throws CloneNotSupportedException {
            ConcretePrototype cloneType = null;
            try {
                cloneType = (ConcretePrototype) super.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return cloneType;
        }
        @Override
        public String toString() {
            return "ConcretePrototype{" + "desc='" + desc + '\'' + '}';
        }
    }
}

运行结果:

ConcretePrototype{desc='original'}
ConcretePrototype{desc='clone'}

Process finished with exit code 0

super.clone() 方法直接从堆内存中以二进制流的方式进行复制,重新分配一个内存块,因此其效率很高。由于 super.clone() 方法基于内存复制,因此不会调用对象的构造函数,也就是不需要经历初始化过程。

但是 super.clone() 也不能满足所有需求,如果类中存在引用类型对象,则原型对象和克隆对象的该属性会指向同一个对象的引用。如下代码演示:

import java.util.List;

public class ConcretePrototype implements Cloneable{
    private int age;
    private String name;
    private List<String> hobbies;

    @Override
    protected Object clone(){
        try {
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }


    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }
}

客户端测试

import java.util.ArrayList;
import java.util.List;

public class Client1 {
    public static void main(String[] args) {
        // 创建原型对象
        ConcretePrototype type = new ConcretePrototype();
        type.setAge(18);
        type.setName("耀");
        List<String> hobbies = new ArrayList<>();
        hobbies.add("对线孙策");
        hobbies.add("对线铠");
        type.setHobbies(hobbies);

        // 拷贝原型对象
        ConcretePrototype cloneType = (ConcretePrototype) type.clone();
        cloneType.getHobbies().add("对线吕布");

        System.out.println("原型对象:" + type);
        System.out.println("克隆对象:" + cloneType);
    }
}

运行结果:

原型对象:ConcretePrototype{age=18, name='耀', hobbies=[对线孙策, 对线铠, 对线吕布]}
克隆对象:ConcretePrototype{age=18, name='耀', hobbies=[对线孙策, 对线铠, 对线吕布]}

Process finished with exit code 0

我们给克隆对象新增一个属性 hobbies 之后,发现原型对象也随之变化,这显然不符合预期。因为我们希望克隆对象和原型对象是俩个独立的对象,不再有联系。

从测试结果来看,应该是 hobbies 共用了一个内存地址,意味着复制的不是值,而是引用的地址。这样的话,如果我们修改任意一个对象中的属性值,protoType 和 cloneType 的 hobbies 值都会改变。这就是我们常说的浅克隆,只是完整复制了值类型数据,没有复制引用对象。

换言之,所有的引用对象仍然指向原来的对象,显然不是我们想要的结果。那如何解决这个问题呢?


深克隆(使用序列化)

在上述基础上继续改造,增加一个 deepClone() 方法。

代码演示:

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class ConcretePrototype implements Cloneable, Serializable {
    private int age;
    private String name;
    private List<String> hobbies;

    @Override
    protected Object clone(){
        try {
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public ConcretePrototype deepCloneHobbies() {
        try {
            ConcretePrototype result = (ConcretePrototype) super.clone();
            result.hobbies = (List)((ArrayList)result.hobbies).clone();
            return result;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public ConcretePrototype deepClone() {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);

            return (ConcretePrototype)ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }


    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }
}

客户端测试

import java.util.ArrayList;
import java.util.List;

public class Client2 {
    public static void main(String[] args) {
        // 创建原型对象
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(18);
        prototype.setName("耀");
        List<String> hobbies = new ArrayList<>();
        hobbies.add("对线孙策");
        hobbies.add("对象铠");
        prototype.setHobbies(hobbies);

        // 拷贝原型对象
        ConcretePrototype cloneType = prototype.deepCloneHobbies();
        cloneType.getHobbies().add("对线吕布");

        System.out.println("原型对象:" + prototype);
        System.out.println("克隆对象:" + cloneType);
        System.out.println(prototype == cloneType);

        System.out.println("原型对象的爱好:" + prototype.getHobbies());
        System.out.println("克隆对象的爱好:" + prototype.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());
    }
}

运行结果:

原型对象:ConcretePrototype{age=18, name='耀', hobbies=[对线孙策, 对象铠]}
克隆对象:ConcretePrototype{age=18, name='耀', hobbies=[对线孙策, 对象铠, 对线吕布]}
false
原型对象的爱好:[对线孙策, 对象铠]
克隆对象的爱好:[对线孙策, 对象铠]
false

Process finished with exit code 0

结果表明,实现了深克隆。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值