吐槽
今天带猫猫去打针,然后她各种皮,差点从袋子里面跑出去了emmmmm,早上上课时候,编译原理上上课居然就听不懂了,很尴尬,赶紧回去补。
什么叫原型模式
就是类似鸣人的影分身之术,可以克隆对象
定义:用原型实例指向创建对象的种类,并通过复制这些原型创建新的对象
原型模式使用的场景
- 当初始化类对象需要消耗非常多资源,或者说要进繁琐的数据准备或者权限,如果想简化创建,可以使用原型模式
- 一个对象要提供给其他对象访问的时候,而且个个调用者都可能改变其值的时候,可以用原型模式复制个个对象提供调用者使用,即保护性拷贝
原型模式下的各种角色
- Client —— 客户端用户,调用类
- ConcretePrototype —— 实现Prototype接口的类,这些类真正实现克隆自身的相关代码
- Prototype —— 声明一个克隆自身的接口,用于约束想要克隆自己的类,要求实现定义的克隆方法。
对象的拷贝
在Java中,如果我们将原始对象的值赋给另一个对象,就是值的传递,如
int a = 1;
int b = a;
如果将引用类型的值赋给另一个对象,则是引用的传递,如
String[] a = new String[5];
String[] b = a;//这里b只是指向了a的引用
在java中
==,如果是对比的基本数据类型(int,long等),比较存储的值是否相等,
如果对比的是引用型的变量,比较的是所指向的对象地址是否相等
equals,不能用于比较基本数据类型,如果没对equals()方法进行
重写,比较的是指向的对象地址,如果想要比较对象内容,需要自行重写
方法,做相应的判断
java中的克隆方法及其使用过程
- 对任何的对象x,都有:x.clone()!=x ,即不是同一对象
- 对任何的对象x,都有:x.clone().getClass==x.getClass(),即对象类型一致
- 如果对象obj的equals()方法定义恰当的话,那么obj.clone().equals(obj)
应当是成立的
使用clone()方法的步骤:
1.实现clone的类首先需要实现Cloneable接口。Cloneable接口实质上是一个标识接口
2.在类中重写Object类中的clone方法
3.在clone方法中调用super.clone()。无论clone类的继承结构是什么,super.clone会直接或间接调用java.lang.Object类的clone()方法。
4.把浅复制的引用指向原型对象新的克隆体。
java在处理基本数据类型(例如int,double,char等),都采用按值传递(传递的是输入参数的复制),除此之外的其他类型都采用按引用传递(传递的是对象的一个引用)。对象除了在函数调用时是引用传递,在使用“=”赋值时也采用引用传递
实用流程
1 先写个引用类:Student.java
public class Student {
private String name; //名字
Student(String name){
this.name = name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "name:"+name;
}
}
2实现Cloneable接口的类:CloneStudent.java,核心就是重写clone方法而已
public class CloneStudent implements Cloneable {
private int age; //年龄
private Student student;//名字
private String king;//学生的类型
CloneStudent(int age,Student student,String king){
System.out.println("执行了构造方法");
this.age = age;
this.student = student;
this.king = king;
}
@Override
protected Object clone() throws CloneNotSupportedException {
CloneStudent cloneStudent = null;
try {
cloneStudent = (CloneStudent)super.clone();
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return cloneStudent;
}
public int getAge() {
return age;
}
public String getKing() {
return king;
}
public Student getStudent() {
return student;
}
public void setAge(int age) {
this.age = age;
}
public void setKing(String king) {
this.king = king;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "CloneStudent["+"age = "+age+"Student"+student.toString()+"kind"+king;
}
}
3 调用类
CloneStudent cloneStudent1 = new CloneStudent(10, new Student("笑笑"),"哈哈");
try {
CloneStudent cloneStudent2 = (CloneStudent)cloneStudent1.clone();
System.out.println("Student1.equals(Student) "+cloneStudent1.equals(cloneStudent2));
System.out.println("student1==student2"+(cloneStudent1==cloneStudent2));
System.out.println(cloneStudent1.getClass()==cloneStudent2.getClass());
System.out.println(cloneStudent1.toString());
System.out.println(cloneStudent2.toString());
cloneStudent1.setAge(200);
System.out.println(cloneStudent1.toString());
System.out.println(cloneStudent2.toString());
cloneStudent2.getStudent().setName("啦啦");
System.out.println(cloneStudent1.toString());
System.out.println(cloneStudent2.toString());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
运行结果:
以上结果发现:
- cloneStudent2是cloneStudent1克隆出来的
- 通过克隆的时候不会执行构造函数
- 克隆会生成的新的对象变量,指向的却是同一个内存地址
- 克隆前后数据类型一致
- 克隆的时候,类中基本数据类型的属性会新建,但是引用类型的
只会生成个新的引用变量,引用变量的地址依旧指向同一个内存地址
深拷贝和浅拷贝
浅拷贝:只新建基本类型数据,不新建引用类型数据
深拷贝:引用类型数据也新建
如何将浅拷贝转换成深拷贝?
有两种方法
1引用类型也实现Cloneable接口,然后实现clone方法
先把Student.java这个类实现接口,然后写Clone方法
public class Student implements Cloneable{
private String name; //名字
Student(String name){
this.name = name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "name:"+name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Student student = null;
student = (Student)super.clone();
return student;
}
}
然后再在接口实现类CloneStudent修改clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
CloneStudent cloneStudent = null;
try {
cloneStudent = (CloneStudent)super.clone();
cloneStudent.setStudent((Student)this.getStudent().clone());
}catch (CloneNotSupportedException e){
e.printStackTrace();
}
return cloneStudent;
}
然后重新运行发行结果:
然后结果发现我们cloneStudent2对象修改了name字符串,cloneStudent1对象的name字符串并没有变化
总结
原型模式就是clone方法的使用,推荐用深拷贝的方式
浅拷贝适用于 对象只包含原始数据域或者不可变对象域的时候,提高效率
该模式的优点是:
1.简化对象创建过程,当对象创建比较烦琐时,可提高创建效率
2.深拷贝可保存对象状态,可将对象拷贝后保存起来,需要的时候恢复
缺点是:
克隆时候不会执行构造方法,所以很尴尬