Java设计模式-原型模式

原型模式

1.原型模式含义

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

其实就是实例化一个bean,如果想创建跟这个bean一样的实例,但是又不想在写new然后set属性值的时候,就直接调用这个bean的clone方法就行了,这就是以已经创建的这个实例为原型,创建新的对象出来。这就是原型模式。

2.代码示例

2.1bean类

public class Work implements Cloneable{
    private String timeArea;
    private String company;

    public String getTimeArea() {
        return timeArea;
    }

    public void setTimeArea(String timeArea) {
        this.timeArea = timeArea;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    @Override
    public String toString() {
        return "Work{" +
                "timeArea='" + timeArea + '\'' +
                ", company='" + company + '\'' +
                '}';
    }

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

2.2测试类

public class MainApplication {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a = new Resume();
        a.setName("小菜");
        a.setAge("22");
        a.setSex("男");
        a.setWorkExperience("2019.07-2023.07", "公司");


        Resume b = (Resume) a.clone();
        b.setName("秋秋");
        b.setSex("女");
        b.setWorkExperience("2019.07-2025.07", "公司");

        Resume c = (Resume) a.clone();
        c.setName("dalei");

        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
    }
}

运行结果

Resume{name=‘小菜’, sex=‘男’, age=‘22’, work=Work{timeArea=‘2019.07-2023.07’, company=‘公司’}}
Resume{name=‘秋秋’, sex=‘女’, age=‘22’, work=Work{timeArea=‘2019.07-2025.07’, company=‘公司’}}
Resume{name=‘dalei’, sex=‘男’, age=‘22’, work=Work{timeArea=‘2019.07-2023.07’, company=‘公司’}}

3.总结

其实在我看来,原型模式并不能算是一个设计模式,它更多的算是对象的实例化方法之一。

不过使用原型模式的时候要注意以下几点:

  • 对象必须重写clone方法
  • 类必须实现Cloneable接口,否则直接使用clone方法的话会报错
  • 直接使用clone方法的话,clone是浅拷贝,不是深拷贝。

3.1深浅拷贝说明

下面以一个例子来说明深浅拷贝的区别

假设现在有一个Resume类,代码如下

public class Resume implements Cloneable{
    private String name;
    private String sex;
    private String age;

    private Work work;

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAge() {
        return age;
    }

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

    public Work getWork() {
        return work;
    }

    public void setWork(Work work) {
        this.work = work;
    }

    public void setWorkExperience(String timeArea, String company){
        if (this.work == null) {
            work = new Work();
        }
        this.work.setTimeArea(timeArea);
        this.work.setCompany(company);
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age='" + age + '\'' +
                ", work=" + work +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        /*Work work1 = (Work) this.work.clone();
        Resume resume = new Resume();
        resume.setWork(work1);
        resume.setName(this.name);
        resume.setSex(this.sex);
        resume.setAge(this.age);
        return resume;*/
        return super.clone();
    }
}

可以看到,一开始clone方法是直接调用的Object类的clone方法,但是这样进行实例化的时候,会导致出问题,我们来看测试代码

public class MainApplication {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a = new Resume();
        a.setName("小菜");
        a.setAge("22");
        a.setSex("男");
        a.setWorkExperience("2019.07-2023.07", "公司");


        Resume b = (Resume) a.clone();
        b.setName("秋秋");
        b.setSex("女");
        b.setWorkExperience("2019.07-2025.07", "公司");

        Resume c = (Resume) a.clone();
        c.setName("dalei");
        c.setWorkExperience(null, null);

        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
    }
}

运行结果

Resume{name=‘小菜’, sex=‘男’, age=‘22’, work=Work{timeArea=‘null’, company=‘null’}}
Resume{name=‘秋秋’, sex=‘女’, age=‘22’, work=Work{timeArea=‘null’, company=‘null’}}
Resume{name=‘dalei’, sex=‘男’, age=‘22’, work=Work{timeArea=‘null’, company=‘null’}}

通过运行结果可以看到,一开始小菜和秋秋两个bean的work属性是设置了值的,但是最后dalei这个bean把work属性设为null后,前面两个bean的work属性值也全部变成null了。

这就是浅拷贝,看出问题来了吗?通过clone生成的bean对象,在对引用类型的属性赋值的时候,会直接影响到其他已经生成的bena的引用属性值。这是因为clone在拷贝对象的时候,如果对象的属性是基本类型的数据,那么就把属性值拷贝过来,如果对象的属性是引用类型的,那么就直接把属性值的地址拷贝过来,这就导致所有clone出来的bean里面引用属性的值都指向同一个内存区域,所以这个时候,不论是哪个clone对象修改了这个引用类型属性的值,都会导致其他所有对象的该值变化,大多数时候,这种情况是不符合业务需求的。

那么这种问题有没有办法解决呢?答案是有的,那就是使用深拷贝。所谓深拷贝就是将会对象中引用类型属性的值,也重新拷贝一份完整的数据出来,放到另一个全新的内存上去,这样拷贝出来的属性值,就和原型对象的属性值没有任何关联了,两个是完全独立的内存地址,修改克隆出来的对象的引用类型属性值,也不会影响到其他的对象,这样就可以解决浅拷贝带来的问题。

那怎么才能作出深拷贝操作呢?其实深拷贝操作是需要我们手动去重写clone方法的,我们自己对引用类型的属性进行重新的实例化并赋值,这样就可以实现深拷贝了。

// 重写后的clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Work work1 = (Work) this.work.clone();
        Resume resume = new Resume();
        resume.setWork(work1);
        resume.setName(this.name);
        resume.setSex(this.sex);
        resume.setAge(this.age);
        return resume;
//        return super.clone();
    }

可能有人觉得这样有点麻烦,还得自己手动写一些东西。但是其实这个是需要再重写clone方法放的时候,重写一次即可,以后就可以到处使用,辛苦几分钟,便利无穷尽。

如果解释看得有点乱,不太好理解的话,可以试着自己写一个简单的demo,实操一遍就会恍然大悟的,快试试吧,你也可以的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我的小幸运呢

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

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

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

打赏作者

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

抵扣说明:

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

余额充值