Prototype 设计模式-原型模式-创建型
目录
1、基本实现方式与代码
原型模式的目的是快速复制对象,二进制流的复制要快于对象new创建。
如果想让一个类支持原型模式,需要:
- 实现标记型接口Cloneable(接口中没有定义任何抽象方法和属性,所以叫标记型)。
- 重写clone()方法
如果只是重写clone()方法而没有实现接口,调用时会报异常。
下面是一个基本代码实现:
package creational.prototype.example1;
public class Person implements Cloneable {
int age = 8;
int score = 100;
Location loc = new Location("bj", 22);
@Override
public Object clone() throws CloneNotSupportedException {
Person p = (Person)super.clone();
p.loc = (Location)loc.clone();//支持Person对象深克隆
return p;
}
}
package creational.prototype.example1;
public class Location implements Cloneable {
String street;
int roomNo;
@Override
public String toString() {
return "Location{" +
"street.hashCode='" + street.hashCode() + '\'' +
", roomNo=" + roomNo +
'}'+super.toString();
}
public Location(String street, int roomNo) {
this.street = new String("123");
this.roomNo = roomNo;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package creational.prototype.example1;
public class Test {
public static void main(String[] args) throws Exception {
Person p1 = new Person();
Person p2 = (Person)p1.clone();
System.out.println(p1.loc);
System.out.println(p2.loc);
System.out.println(p1.loc.street == p2.loc.street);//true,"123"=="123"
p1.loc.street = "sh";
System.out.println(p1.loc.street == p2.loc.street);//false,"sh"!="123"
System.out.println(p1.loc.street);//sh
System.out.println(p2.loc.street);//123
//p1.loc.street.replace("sh", "sz");
// System.out.println(p2.loc.street);
}
}
所有代码在这里:
2、关键
关键点是需要区分深克隆与浅克隆(深拷贝浅拷贝),克隆得到的对象的引用类型属性值B与源对象的引用类型属性值A。
我们都了解浅拷贝只拷贝对象的引用(两个人拿着同一房间的两张房卡),深拷贝拷贝的是实际对象的存放空间(两个人拿着两个房间的各自不同的房卡)。
就像上面Person类中的Location loc属性,如果Location类没有实现标记型接口Cloneable和重写clone()方法,则源对象p1和克隆对象p2中的Location loc属性引用值相同,指向的是同一对象所在的内存空间(浅拷贝)。
反之如果Location类实现了实现标记型接口Cloneable和重写clone()方法,则源对象p1和克隆对象p2中的Location loc属性引用值不同,指向的是完全不同的两个内存空间(深拷贝)。
另一个需要注意的点是String,由于String对象值存放在常量池这一特性,我们看到Test类的11-13行,本来String的拷贝只是浅拷贝,即俩对象指向同一内存区域。但之后由于变更了其中一个String street的值,因为String是final类型不可更改,所以变更至相当于重新开辟内存空间并更新street引用值。如此操作之后,两个street的引用指向就不同了。
当然,如果是StringBuilder street,在13行无论怎么改,两个street的引用指向一直是相同的。
如果引用类型的属性类没有实现原型模式的支持(就是上面两点),则克隆得到的对象的引用类型的属性值B与源对象的的引用类型的属性值A完全相同,改其中一个的值,另一个的值也改变,因为根本就是一个;反正则引用值不同。
3、源码中的应用
(1)JDK的ArrayList
该类实现了Cloneable接口,同时重写了clone()方法,重写方法如下:
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
本文地址:https://blog.csdn.net/phs999/article/details/108212420
上一篇: 二十三种设计模式之工厂模式