![](https://image.cha138.com/20210809/07d92858aa10417481d5dd245ee4f83a.jpg)
![](https://image.cha138.com/20210809/c4f965a6c2454078affd68a15a74fa55.jpg)
————— 第二天 —————
![](https://image.cha138.com/20210809/602c6a254d67405a971083d5df8d5077.jpg)
![](https://image.cha138.com/20210809/64144912809341e88ab2ebfa679d9241.jpg)
![](https://image.cha138.com/20210809/019e1ca606e8408cae84f81b4a87e1d8.jpg)
![](https://image.cha138.com/20210809/bc6124843acf4280a6184838a5eebf92.jpg)
![](https://image.cha138.com/20210809/6d7f1106abbb4c8c9471d9346c53c93d.jpg)
![](https://image.cha138.com/20210809/97400d32b8514593b1509b74c0496971.jpg)
![](https://image.cha138.com/20210809/c556599c2e3447cd800c382aede262ef.jpg)
————————————
![](https://image.cha138.com/20210809/259cda140c334444a07c7683d5ef924d.jpg)
![](https://image.cha138.com/20210809/d8eed8aaafec498982b593960bdef9a3.jpg)
![](https://image.cha138.com/20210809/dc06f3b7a1124ca183d3e3da2b747cc9.jpg)
![](https://image.cha138.com/20210809/9e71639a13394ada9b17741294483b8f.jpg)
![](https://image.cha138.com/20210809/d25b33f77f2d4b428f1ef1479e6f492a.jpg)
![](https://image.cha138.com/20210809/e66715e8287c4c459073a21ed1dee0d6.jpg)
![](https://image.cha138.com/20210809/16fcc18d69c74b72829106d4a69471e7.jpg)
假如有一天,小灰被外星人抓走了,外星人要拿小灰做实验,想了解小灰在吃得好、睡得好、玩得开心的场景下,与现实中小灰的生存状态有什么区别。
于是,外星人克隆了几个一模一样的小灰:
![](https://image.cha138.com/20210809/bbc81f779cfa4a5c8cb6bd24b57bee7c.jpg)
就这样,小灰的原型被留在现实中,而三个复制体分别提供了吃得好、睡得好、玩得开心三种不同环境,小灰的原型则不受三个复制体的影响。
过了一段时间,我们来观察一下本体与分身的生存状态:
![](https://image.cha138.com/20210809/e1d4a3a62c3146629ff5c112aa381d53.jpg)
![](https://image.cha138.com/20210809/c082947959814018b1eddb9d06c5ac05.jpg)
![](https://image.cha138.com/20210809/5a106f0bf75b4496b904c0a2d39dc49d.jpg)
![](https://image.cha138.com/20210809/9a1c06fba8f448fa9422c64a76121c7f.jpg)
![](https://image.cha138.com/20210809/1809eb9723a74451a39540961f3691b6.jpg)
![](https://image.cha138.com/20210809/7534d96df30e4922b0bef47fb945e06a.jpg)
在Java语言中,Object类实现了Cloneable接口,一个对象可以通过调用Clone()方法生成对象,这就是原型模式的典型应用。
但需要注意的是,clone()方法并不是Cloneable接口里的,而是Object类里的,Cloneable是一个标识接口,标识这个类的对象是可被拷贝的,如果没有实现Cloneable接口,却调用了clone()方法,就会报错。
// protected native Object clone() throws
CloneNotSupportedException;protected Object clone() throws
CloneNotSupportedException {
if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException(
"Class " + getClass().getName() +
" doesn't implement Cloneable");
}
return internalClone();
}
// Native helper method for cloning.
private native Object internalClone();
|
![](https://image.cha138.com/20210809/b26b19db661b4faeba2d3128a98f1352.jpg)
Java中的数据类型,分为基本类型和引用类型。在一个方法里的变量如果是基本类型的话,变量就直接存储在这个方法的栈帧里,例如int、long等;而引用类型则在栈帧里存储这个变量的指针,指向堆中该实体的地址,例如String、Array等。深拷贝和浅拷贝是只针对引用数据类型的。
比如一个方法有一个基本类型参数和一个引用类型参数,在方法体里对参数重新赋值,会影响传入的引用类型参数,而不会影响基本类型参数,因为基本类型参数是值传递,而引用类型参数是引用传递。
先定义一个用户类:
// 这是一个非常简单的用户类
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + ", age=" + age +'}';
}
}
|
测试:
private int x=10;
public void updateValue(int value){
value = 3 * value;
}
private User user= new User("大黄",20);
public void updateUser(User student){
student.setName("小灰");
student.setAge(18);
}
public void test(){
System.out.println("调用前x的值:"+x);
updateValue(x);
System.out.println("调用后x的值:"+x);
System.out.println("调用前user的值:"+user.toString());
updateUser(user);
System.out.println("调用后user的值:"+user.toString());
}
|
Log打印结果如下:
调用前x的值:10 调用后x的值:10 调用前user的值:User{name='大黄, age=20} 调用后user的值:User{name='小灰, age=18} |
传递基本类型的方法(updateValue())流程图:
![](https://image.cha138.com/20210809/ff1d14518f374f6c8059f7c75b591664.jpg)
传递引用类型的方法(updateUser())流程图:
![](https://image.cha138.com/20210809/38b06656275f4c848437d982142f6e07.jpg)
这其中也包含着例外,比如String类型和大小不超过127的Long类型,虽然也是引用类型,却像基本类型一样不受影响。这是因为它们会先比较常量池维护的值,这涉及VM的内容,今天不做过多讨论。
浅拷贝是在按位(bit)拷贝对象,这个对象有着原始对象属性值的一份精确拷贝。我们结合应用场景分析一下,还是刚才的User类,我们增加一个存放地址的内部类Address,我们需要用户信息可以被其他module查询,但是不允许它们被其他module修改,新增代码如下:
// 这是一个稍微复杂的、支持拷贝的用户类
public class User implements Cloneable {
// ……省略上文代码……
private Address address;
@NonNull
@NotNull
@Override
public User clone() {
try{
return (User)super.clone();
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public class Address{
// 地市
public String city;
// 区县
public String county;
// 乡镇街道
public String street;
}
} |
![](https://image.cha138.com/20210809/37f694f038234566a1c3496458d61b09.jpg)
![](https://image.cha138.com/20210809/5204616b9daa49f98d5f2a7b8f51f48d.jpg)
![](https://image.cha138.com/20210809/dbf455319c5d49738b8846538d8f56dd.jpg)
// 这是一个更复杂的、支持深拷贝的用户类
public class User implements Cloneable {
// ……省略上文代码……
@NonNull
@NotNull
@Override
public User clone() {
try{
User newUser = (User)super.clone();
newUser.setName(this.name);
newUser.setAddress(this.address.clone());
return newUser;
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
public class Address implements Cloneable{
// ……省略上文代码……
@NonNull
@NotNull
@Override
public Address clone() {
try{
Address newAddress = (Address)super.clone();
newAddress.city = this.city;
newAddress.county = this.county;
newAddress.street = this.street;
return newAddress;
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
}
|
需要注意的是,上面代码的深拷贝其实并不彻底,因为彻底的深拷贝几乎是不可能实现的,那样不但可能存在引用关系非常复杂的情况,也可能存在引用链的某一级上引用了一个没有实现Cloneable接口的第三方对象的情况。
绝大多数设计模式都是牺牲性能提升开发效率的,原型模式则是为数不多的牺牲开发效率提升性能的设计模式。
![](https://image.cha138.com/20210809/20b7cd832a724a06a0708500d2d5ff70.jpg)
![](https://image.cha138.com/20210809/729506f6699c421aa322dfac1d9b8a69.jpg)
![](https://image.cha138.com/20210809/cc1d1dbde2ae4de08046333dbcb8abad.jpg)
![](https://image.cha138.com/20210809/b2fc22774d394733a37da26ac3652955.jpg)
![](https://image.cha138.com/20210809/212e882428724beb9a7b16a0ef835795.jpg)
![](https://image.cha138.com/20210809/76c39b448ed34fde8695025382aef3ab.jpg)
private User user= new User("大黄",20);
public void testNew(){
User user1 = new User("小灰",18);
}
public void testClone(){
User user2 = user.clone();
}
|
通过ASM工具查看bytecode,可以看出二者对栈资源的消耗:
// access flags 0x1 public testNew()V ……省略…… MAXSTACK = 4 MAXLOCALS = 2 // access flags 0x1 public testClone()V ……省略…… MAXSTACK = 1 MAXLOCALS = 2 |
![](https://image.cha138.com/20210809/69cfed9f3d7e4f059b5fe71f6b847ef2.jpg)
@Override
public Object clone() {
return new Intent(this);
}
|
![](https://image.cha138.com/20210809/03154370b0c049069e8c3ad62a1469f2.jpg)
![](https://image.cha138.com/20210809/a28b4ad619274aeb939bf1b2fcd611cf.jpg)
最后我们来总结一下原型模式的核心用途:
1.解决构建复杂对象的资源消耗问题,提升创建对象的效率。
2.保护性拷贝,防止外部对只读对象进行需修改。
![](https://image.cha138.com/20210809/4d76f6b9006a495896f0b5a5d8b25680.jpg)
![](https://image.cha138.com/20210809/6da5800e78c94e09964d348d4b4aa05d.jpg)
![](https://image.cha138.com/20210809/4c4d071c3dbe4870bdf0f36bc9879fce.jpg)
![](https://image.cha138.com/20210809/c24e50dd43c540718888f3f504daf0bd.jpg)
![](https://image.cha138.com/20210809/13a9549d5e4f427d89503ddd058e44a5.jpg)