文章目录
- 1、原型设计模式
- 2、深克隆和浅克隆
1、原型设计模式
说明:
- 用一个原型对象,创建和原型对象相同的对象,以能够保证创建对象的性能
- 是创建大量相同对象的最佳方式
使用场景:
- 对象的创建非常复杂,可以使用原型模式快捷的创建对象
- 性能和安全要求比较高
@Data
public class Pig{
private String name; //名字
private String doSomething; //喜欢做的事
}
现在要表示佩奇一家,正常创建流程如下:
public class Test{
public static void mian(STring[] args){
Pig peki = new Pig();
peki.setName("佩琪");
peki.setDoSomething("喜欢吃蛋糕");
System.out.println(peki);
Pig george = new Pig();
george.setName("乔治");
george.setDoSomething("喜欢睡觉");
System.out.println(george);
Pig pigDad = new Pig();
pigDad.setName("猪爸爸");
pigDad.setDoSomething("喜欢开车");
System.out.println(pigDad);
Pig pigMum = new Pig();
pigMum.setName("猪妈妈");
pigMum.setDoSomething("喜欢做饭");
System.out.println(pigMum);
}
}
运行:
采用原型设计模式后:实体类实现Cloneable接口
@Data
public class Pig implements Cloneable{
public Pig() {
System.out.println("小猪被初始化了...");
}
private String name; //名字
private String doSomething; //喜欢做的事
@Override //重写Object 的clone方法
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
再次创建佩奇一家:
public class Test{
public static void mian(STring[] args){
Pig peki = new Pig(); //先new一个
peki.setName("佩琪");
peki.setDoSomething("喜欢吃蛋糕");
System.out.println(peki);
Pig george = (Pig) peki.clone(); //后面就克隆
george.setName("乔治"); //如果这里不赋值,那克隆出来的属性和克隆样本一样
george.setDoSomething("喜欢睡觉");
System.out.println(george);
Pig pigDad = (Pig) peki.clone() ;
pigDad.setName("猪爸爸");
pigDad.setDoSomething("喜欢开车");
System.out.println(pigDad);
Pig pigMum = (Pig) peki.clone() ;
pigMum.setName("猪妈妈");
pigMum.setDoSomething("喜欢做饭");
System.out.println(pigMum);
}
}
运行:
发现构造方法只被调用了一次,且出来的也照样是不同的对象
。因此,当对象属性很多,而又要创建大量这种对象时,就可以用原型设计模式。该模式产生的对象,虽然都是不同的对象,但如果不重新赋值,属性却是与克隆样本保持一致的,即使是一个新的对象。
到这儿有个想法:既然主要是为了方便批量创建对象,不用重复set一些属性。那你把一样的属性定义成static不是更好吗?那相比克隆就只是多调了一次无参构造。但其实克隆有自己的场景,比如一批一批的对象的一些属性是相同的:全市竞赛,同一学校的三好学生,其属性只有名字不一样,但各个学校来看属性就都不一样,此时你Student类的属性不能定义成static吧。
2、深克隆和浅克隆
主要针对对象中有引用类型属性,如:
public class Person{
private String name;
private Parent parent; //父母亲
}
- 浅克隆:克隆的新对象,其引用属性依然指向原来对象的对应属性的内存地址,即Parent属性的值指向同一个内存地址
- 深克隆:引用属性的对象也会被克隆,不再指向原有的地址
Java中的Object类中提供了 clone()
方法来实现浅克隆。想要深克隆,可以采用对象流
(序列化和反序列化)。案例:奖状类,有一个引用属性Student:
//奖状类
public class Citation implements Cloneable {
private Student stu;
public Student getStu() {
return stu;
}
public void setStu(Student stu) {
this.stu = stu;
}
void show() {
System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
学生类:
//学生类
@Data
@AllArgsConstructor
public class Student {
private String name;
private String address;
}
模拟了一个类有引用属性Student时的克隆:
//测试类
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
Citation c1 = new Citation();
Student stu = new Student("张三", "西安");
c1.setStu(stu);
//复制奖状
Citation c2 = c1.clone();
//获取c2奖状所属学生对象
Student stu1 = c2.getStu();
stu1.setName("李四");
//判断stu对象和stu1对象是否是同一个对象
System.out.println("stu和stu1是同一个对象?" + (stu == stu1));
c1.show();
c2.show();
}
}
运行:
注意,默认的浅克隆下,引用属性指向的对象是同一个,后面set的引用属性的值,自然是会覆盖前面set克隆对象的引用属性值。使用对象流进行深克隆:
public class CitationTest1 {
public static void main(String[] args) throws Exception {
Citation c1 = new Citation();
Student stu = new Student("张三", "西安");
c1.setStu(stu);
//创建对象输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Desktop\\b.txt"));
//将c1对象写出到文件中
oos.writeObject(c1);
oos.close();
//创建对象出入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Desktop\\b.txt"));
//读取对象
Citation c2 = (Citation) ois.readObject();
//获取c2奖状所属学生对象
Student stu1 = c2.getStu();
stu1.setName("李四");
//判断stu对象和stu1对象是否是同一个对象
System.out.println("stu和stu1是同一个对象?" + (stu == stu1)); //false
c1.show(); //张三
c2.show(); //李四
}
}