原型模式

原型模式:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

原型模式可能是除了单例模式与迭代器模式之外23种设计模式中最简单的设计模式了。原型模式的核心就是一个clone方法,Java提供了一个Cloneable接口表示这个对象是可拷贝的,然后重写Object类中的clone方法。
Java中原型模式的通用代码:

public class ProtoType implements Cloneable {

    @Override
    public ProtoType clone() {
        ProtoType protoType = null;
        try {
            protoType = (ProtoType) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return  protoType;
    }
}

只要实现一个接口然后重写一个方法。

优点

性能优良:原型模式是对内存二进制流的拷贝,比new一个对象快的多。
逃避构造函数的约束:这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。

使用场景
  1. 资源优化场景
    类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
  2. 性能和安全要求的场景
    通过new产生-一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
  3. 一个对象多个修改者的场景
    一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与Java融为一体, 大家可以随手拿来使用。

注意事项

别看它这么简单,其中的问题可不少。
1、构造函数不会执行
A类实现了Cloneable接口并且重写clone方法,当B类调用A类的clone方法来创建对象时,A类的构造方法不会执行。下面举个实例来论证
A类:

public class ProtoType implements Cloneable {

    public ProtoType(){
        System.out.println("构造方法执行。。。");
    }

    @Override
    public ProtoType clone() {
        ProtoType protoType = null;
        try {
            protoType = (ProtoType) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return  protoType;
    }
}

B类

public class Client {

    public static void main(String[] args) {
        ProtoType protoType = new ProtoType();
        ProtoType p1 = protoType.clone();
    }
}

结果:
在这里插入图片描述
具体来说B类调用A类的clone方法是从堆内存中以二进制流的方法拷贝,重新分配一个内存块,直接就相当于把创建好的对象复制一份。
2、 浅拷贝深拷贝
看完上述的clone过程,你可能会问,如果对象里有引用数据类型呢?堆内存里保存的只是这个属性的引用啊,属性所指向的对象分配在其他堆中,所以Object类提供的clone方法只拷贝对象,其对象内部的数组,引用对象都不拷贝,还是指向原生对象的内部地址,这就叫浅拷贝。
下面举个例子

public class ProtoType implements Cloneable {

    private List<String> list = new ArrayList<String>();

    @Override
    public ProtoType clone() {
        ProtoType protoType = null;
        try {
            protoType = (ProtoType) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return  protoType;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(String s) {
        list.add(s);
    }
}

测试

public static void main(String[] args) {
       ProtoType protoType = new ProtoType();
       ProtoType p1 = protoType.clone();
       protoType.setList("张三");
       System.out.println(protoType.getList());
       p1.setList("李四");
       System.out.println(p1.getList());
}

输出
在这里插入图片描述
调试观察
在这里插入图片描述
很明显,两个对象共享一个私有变量,很不安全,这就是浅拷贝。浅拷贝时,内部的数组与引用类型不拷贝,其他的基本类型如char、int、long都会拷贝,但String虽然是引用类型,它也会被拷贝。
深拷贝
深拷贝就是对要拷贝的对象的引用类型也进行拷贝

public class ProtoType implements Cloneable {

    private ArrayList<String> list = new ArrayList<String>();

    @Override
    public ProtoType clone() {
        ProtoType protoType = null;
        try {
            protoType = (ProtoType) super.clone();
            protoType.list = (ArrayList<String>) this.list.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return  protoType;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(String s) {
        list.add(s);
    }
}

结果很明显了
在这里插入图片描述
3、clone方法与final关键字会产生冲突
在这里插入图片描述
final关键字就是禁止重新赋值的,就算你克隆成功也没办法赋值。解决的方法只能删去关键字。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值