简述-原型模式

原型模式

原型模式,用起来其实就是做clone操作,clone一个对象,越过构造器,在特定使用场景下增加效率。

使用场景:

  1. 类初始化需要消耗很多资源,比较耗时。
  2. new方式非常繁琐,还涉及到权限之类的。用clone就减少操作。
  3. 一个对象需要提供给其他对象使用,而且各个使用都可能改到值,可以考虑用原型模式,做保护性拷贝。

先说说原型模式主要的浅拷贝和深拷贝:

浅拷贝:

可被clone的对象:

  • 实现 Cloneable接口
  • 重写clone方法
  • 先clone,再给参数赋值
public class Document implements Cloneable {
    private String mText;
    private ArrayList<String> list = new ArrayList<>();

    public String getmText() {
        return mText;
    }

    public void setmText(String mText) {
        this.mText = mText;
    }

    @Override
    protected Document clone() throws CloneNotSupportedException {
        Document document = (Document) super.clone();
        document.mText = this.mText;
        document.list = this.list;
        return document;
    }
}
深拷贝:

对于上面ArrayList,它自身实现类cloneable接口的,所以它本身也是可以clone的,对于对象,赋值后,是指向同一个地址的,对于浅拷贝来说,如果改了list对象,那么原有的对象list也会被修改到。所以对于list对象,我们也需要拷贝一下,修改如下。


public class Document implements Cloneable {
    private String mText;
    private ArrayList<String> list = new ArrayList<>();

    public String getmText() {
        return mText;
    }

    public void setmText(String mText) {
        this.mText = mText;
    }

    @Override
    protected Document clone() throws CloneNotSupportedException {
        Document document = (Document) super.clone();
        document.mText = this.mText;
        document.list = (ArrayList<String>) this.list.clone();
        return document;
    }
}

检验

首先:对于浅拷贝

  • 我们用下面一段代码来做检验:

Document document = new Document();
        document.setmText("11111100000");
        document.getList().add("00");
        System.out.println("内容原始:" + document.toString());

        Document document1 = document.clone();
        document1.setmText("111111");
        document1.getList().add("111");
        System.out.println("内容修改:" + document1.toString());
        System.out.println("内容原始:" + document.toString());

  • 输出:
内容原始:Document{mText='11111100000', list=[00]}
内容修改:Document{mText='111111', list=[00, 111]}
内容原始:Document{mText='11111100000', list=[00, 111]}

这里很明显的能看到,我们改了String类型,也改了list对象。但是输出后,原始的String类型的text没变,但是list却被改变了。
这是为什么呢?

原因其实是:
String类型是不可变类型,所以我们不推荐在for循环中使用+号来拼接一样,因为每+一次就会创建一块内存来装。同理,这里的虽然拷贝了string类型,但是第二个对象修改的时候,string的text是指向了新的地址而不是把原地址数据给改了,所以原对象的text并没被改。但list就不一样了,改的是原对象地址的数据
所以我们才会需要深拷贝。

然后我们再检验一次深拷贝的:

输出内容:

内容原始:Document{mText='11111100000', list=[00]}
内容修改:Document{mText='111111', list=[00, 111]}
内容原始:Document{mText='11111100000', list=[00]}

我们使用深拷贝的时候,将对象list也clone了,就没有指向的不再是原有地址了,所以改动也就影响不到原来数据咯。

另外:实现Cloneable接口只是一种方式。要实现clone也不一定非要实现这个接口(使用Object来重写clone方法的话一定要实现,不然会抛出异常)。

如果使用new的方式并不耗资源等,clone的时候我们传入自己,用new的效率或许会更高:

  public Document(Document document) {
        this.mText = document.getmText();
        this.list = document.getList();
    }

@Override
    public Document clone() throws CloneNotSupportedException {
        Document document = new Document(this);
        return document;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值