原型模式
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,实操一遍就会恍然大悟的,快试试吧,你也可以的。