每一个优秀的人,都有一段沉默的时光。那一段时光,是付出了很多努力,忍受孤独和寂寞,不抱怨不诉苦,日后说起时,连自己都能被感动日子。
设计模式学习,近期我会把23中设计模式都写成博客,敬请期待~
什么是原型模式?
- 原型模式是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
- 原型设计模式是一种创建型设计模式,运行一个对象再创建另外一个可定制的对象,无需知道如何创建的细节。
- 工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,既 对象.clone();
什么是克隆?
什么是克隆呢?可能大家都听说过克隆羊多利吧
就是一个已经存在的东西,完完全玩的复制一份,就称之为克隆,说的通俗一点就是拷贝粘贴~
原型设计模式思路
咋们也模仿羊来克隆出来一只多利(克隆羊),
先用普通的最简单的做法来克隆
然后在给大家介绍使用原型模式来克隆
普通方法克隆
创建一直普通的’羊’,用来克隆:
public class Sheep implements Cloneable {
String name;//羊的名字
int age;//样的年龄
String color;//羊的颜色
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
}
这只羊非常简单,只有名字,年龄和颜色
来看看是如何克隆的~
Sheep sp1 = new Sheep("tom", 3, "red");
Sheep sp2 = new Sheep("tom", 3, "red");
Log.i("原型模式普通方法:",sp1.toString()+"\t hashCode:"+sp1.hashCode());
Log.i("原型模式普通方法:",sp2.toString()+"\t hashCode:"+sp2.hashCode());
这里非常好理解,就是new了2个羊达到’克隆’的目的
效果图(1.1)
:
可以看出hashCode是不一样的,说明2个内存指向不一样,那么就来验证一下.
验证方式非常简单,通过修改名字来判断会不会冲突
验证代码:
Sheep sp1 = new Sheep("tom", 3, "red");
Sheep sp2 = new Sheep("tom", 3, "red");
Log.i("原型模式普通方法:",sp1.toString()+"\t hashCode:"+sp1.hashCode());
Log.i("原型模式普通方法:",sp2.toString()+"\t hashCode:"+sp2.hashCode());
sp2.setName("jack");//修改'克隆'羊的名字为jack
Log.i("原型模式普通方法:",sp1.toString()+"\t hashCode:"+sp1.hashCode());
Log.i("原型模式普通方法:",sp2.toString()+"\t hashCode:"+sp2.hashCode());
修改’克隆’羊的名字为jack
效果图(1.2)
:
通过这张图可以看出:即使修改’克隆’羊也不会影响被克隆羊的参数(这里指名字).
用代码来解释就是:内存地址不一样,内存指向也不一样.这是2个完全没关系的东西
传统方式的优缺点
- 比较好理解,简单易操作
- 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象过多,效率不高。
- 总是需要重新初始化对象,而不是动态的获取对象运行时的状态。
浅克隆
对于基本数据类型是成员变量,浅拷贝会直接进行值拷贝,也就是将该属性值复制一份给新的对象。
原型模式使用:
- 实现Cloneable接口
- 实现clone()方法
浅拷贝代码实现:
public class Sheep implements Cloneable {
String name;//羊的名字
int age;//样的年龄
String color;//羊的颜色
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
@NonNull
@Override
public Object clone() {
try {
//引用对象拷贝 将对象的方法拷贝给当前的值
Sheep sheep = (Sheep) super.clone();
return sheep;
} catch (Exception e) {
Log.i("原型模式:", e.getMessage());
return null;
}
}
}
实现代码:
Sheep sheep1 = new Sheep("tom", 3, "red",lazinessSheep);
Sheep sheep2 = (Sheep) sheep1.clone();//'克隆羊'
Log.i("原型模式:", "============ 克隆之前 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
Log.i("原型模式:", "===========================");
sheep1.setName("jack");
Log.i("原型模式:", "============ 克隆之后 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
分析:
- sheep1 是本身的羊
- sheep2 是通过sheep1 .clone()克隆出来的羊
- 先输出结果,然后改变sheep1本身羊的name,来看看会不会s影响heep2( 克隆羊)
效果图(1.3)
:
可以看出这种克隆方式对于基本数据类型能够完成很好的克隆,
这就是浅克隆
如果说我的羊现在有一只朋友’懒洋洋’来看看有如何不同:
我的朋友类:
public class LazinessSheep{
String name = "懒洋洋";
int age = 2;
public LazinessSheep() {
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "LazinessSheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
克隆羊类:
public class Sheep implements Cloneable {
String name;//羊的名字
int age;//样的年龄
String color;//羊的颜色
public LazinessSheep lazinessSheep;//羊的朋友(懒洋洋)
public Sheep(String name, int age, String color, LazinessSheep lazinessSheep) {
this.name = name;
this.age = age;
this.color = color;
this.lazinessSheep = lazinessSheep;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", lazinessSheep=" + lazinessSheep +
'}';
}
@NonNull
@Override
public Object clone() {
try {
//引用对象拷贝 将对象的方法拷贝给当前的值
Sheep sheep = (Sheep) super.clone();
return sheep;
} catch (Exception e) {
Log.i("原型模式:", e.getMessage());
return null;
}
}
}
实现代码:
LazinessSheep lazinessSheep = new LazinessSheep();
Sheep sheep1 = new Sheep("tom", 3, "red",lazinessSheep);
//'克隆羊'
Sheep sheep2 = (Sheep) sheep1.clone();
Log.i("原型模式:", "============ 克隆之前 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
Log.i("原型模式:", "===========================");
sheep1.lazinessSheep.setName("灰太狼");
Log.i("原型模式:", "============ 克隆之后 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
分析:
- lazinessSheep是我的朋友
- sheep1是本身的羊
- sheep2是被克隆的羊
- 先输出第一次的结果,然后改变我朋友(lazinessSheep)的名字为灰太狼
来看看有什么不同:
效果图(1.4)
:
通过效果图(1.4)
可以看出我们只改变了
sheep1本身的羊朋友的值(名字),但是sheep2克隆羊朋友的值(名字)也改变了
现在的问题如下:
可以看出,浅拷贝无法复制引用类型,因为他没有复制引用类型,只是吧现在的状态指向了内存地址,不管你复制10个还是20个克隆羊,引用类型还是指向的原本的内存地址
这就是浅克隆为什么不能克隆引用类型,只能克隆基本数据类型了
深克隆
所谓深克隆,就是解决浅克隆不能复制引用数据类型的问题,达到克隆的每一个个羊,都是单独的,和原本并没有区别.
深克隆代码:
public class LazinessSheep implements Cloneable{
String name = "懒洋洋";
int age = 2;
public LazinessSheep() {
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "LazinessSheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@NonNull
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
将需要克隆的有引用类型也实现Cloneable接口实现clone()方法
public class Sheep implements Cloneable {
String name;//羊的名字
int age;//样的年龄
String color;//羊的颜色
public LazinessSheep lazinessSheep;//羊的朋友(懒洋洋)
public Sheep(String name, int age, String color, LazinessSheep lazinessSheep) {
this.name = name;
this.age = age;
this.color = color;
this.lazinessSheep = lazinessSheep;
}
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", lazinessSheep=" + lazinessSheep +
'}';
}
@NonNull
@Override
public Object clone() {
try {
//引用对象拷贝 将对象的方法拷贝给当前的值
Sheep sheep = (Sheep) super.clone();
lazinessSheep = (LazinessSheep) sheep.lazinessSheep.clone();
return sheep;
} catch (Exception e) {
Log.i("原型模式:", e.getMessage());
return null;
}
}
}
修改羊本身的类:
重点方法:
@NonNull
@Override
public Object clone() {
try {
//引用对象拷贝 将对象的方法拷贝给当前的值
Sheep sheep = (Sheep) super.clone();
lazinessSheep = (LazinessSheep) sheep.lazinessSheep.clone();
return sheep;
} catch (Exception e) {
Log.i("原型模式:", e.getMessage());
return null;
}
}
因为这里的引用类型也是由基本数据类型组成的,
所以在这里通过
lazinessSheep = (LazinessSheep) sheep.lazinessSheep.clone();
把LazinessSheep的clone();赋值给当前类的lazinessSheep即可
实现代码:
还是上边一样的代码:
/// 原型模式之深克隆 \\\\\\\\\\\\\\\\\\\\
LazinessSheep lazinessSheep = new LazinessSheep();
Sheep sheep1 = new Sheep("tom", 3, "red",lazinessSheep);
//'克隆羊'
Sheep sheep2 = (Sheep) sheep1.clone();
Log.i("原型模式:", "============ 克隆之前 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
Log.i("原型模式:", "===========================");
sheep1.lazinessSheep.setName("灰太狼");
Log.i("原型模式:", "============ 克隆之后 ===============");
Log.i("原型模式:", "sheep1=>" + sheep1.toString() + "\t hashCode=>" + sheep1.hashCode());
Log.i("原型模式:", "sheep2=>" + sheep2.toString() + "\t hashCode=>" + sheep2.hashCode());
效果图(1.5)
:
可以看到,改变sheep1的引用数据类型lazinessSheep为灰太狼之后并没有影响克隆之后的值,这样就达到了深克隆的效果
总结
- 浅克隆:只能克隆基本数据类型,因为克隆引用数据类型他不会创建新的内存地址,而是吧当前克隆的指向被克隆的地址
- 深克隆:所有数据类型都可以克隆,如果原始对象发生变化,其他克隆对象也会发生相应的变化,无需修改代码。
- 创建新的对象比较复杂时,可以使用原型模式简化对象的创建过程,同时也能提高效率。
- 不用重新初始化对象,而是动态地获取对象运行的状态。
最近文章:
原创不易,您的点赞就是对我最大的支持,点个赞支持一下哦~