原型模式是用于创建重复的对象
,同时又能保证性能。这种类型的设计模式属于创建型模式
,它提供了一种创建对象的最佳方式。
传统创建重复对象的方法:
package originModel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor class Teacher implements Cloneable{ private String name; private Student student; }
package originModel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Student { private int age; private String name; }
package originModel; public class test1 { public static void main(String[] args) { Teacher teacher1=new Teacher(); teacher1.setName("张三"); teacher1.setStudent(new Student(19,"小明")); Teacher teacher2=new Teacher(teacher1.getName(),teacher1.getStudent()); System.out.println(teacher1); System.out.println(teacher2); } }
输出如下所示:
Teacher(name=张三, student=Student(age=19, name=小明)) Teacher(name=张三, student=Student(age=19, name=小明))
传统方式虽然比较好理解而且也容易操作,但是在创建新的对象时,总是需要获取原始对象的属性
,如果创建的对象比较复杂,这种方式效率很低。
通过原型模式创建重复对象的方法:本质“克隆”
浅克隆:
实现步骤:
原型对象实现 1:实现Cloneable接口 2:实现clone方法
package originModel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor class Teacher implements Cloneable { private String name; //student类为Teacher类的成员变量 private Student student; @Override public Object clone() throws CloneNotSupportedException { //浅克隆 return super.clone(); }
测试类:
package originModel; public class test1 { public static void main(String[] args) throws CloneNotSupportedException { Teacher teacher1=new Teacher(); teacher1.setName("张三"); teacher1.setStudent(new Student(19,"小明")); Teacher teacher2= (Teacher) teacher1.clone(); System.out.println(teacher1); System.out.println(teacher2); } }
输出如下所示:
Teacher(name=张三, student=Student(age=19, name=小明)) Teacher(name=张三, student=Student(age=19, name=小明))
通过克隆我们得到了和原型对象值完全相同的克隆对象,如果我们修改克隆对象的值是否会影响到原型对象呢?
验证如下所示:
package originModel; public class test1 { public static void main(String[] args) throws CloneNotSupportedException { Teacher teacher1=new Teacher(); teacher1.setName("张三"); teacher1.setStudent(new Student(19,"小明")); Teacher teacher2= (Teacher) teacher1.clone(); System.out.println(teacher1.getStudent()); System.out.println(teacher2.getStudent()); System.out.println(teacher1.getStudent()==teacher2.getStudent()); // 验证1:克隆对象中引用数据类型值的改变是否会影响到原型对象? teacher2.getStudent().setName("李华"); teacher2.getStudent().setAge(20); System.out.println(teacher1.getStudent()); System.out.println(teacher2.getStudent()); System.out.println(teacher1.getStudent()==teacher2.getStudent()); // 验证2:克隆对象中基本数据类型值的改变是否会影响到原型对象? teacher2.setName("李红"); System.out.println(teacher1.getName()); System.out.println(teacher2.getName()); System.out.println(teacher1.getName()==teacher2.getName()); } }
输出如下所示:
修改克隆对象中引用数据类型的值影响到了原型对象,而修改克隆对象中基本数据类型的值并没有影响到原型对象
Student(age=19, name=小明) Student(age=19, name=小明) true Student(age=20, name=李华) Student(age=20, name=李华) true 张三 李红 false
出现上述结果的原因如下所示:
克隆对象中的基本数据类型对象是直接存储在克隆对象自身的内存空间中,而不是引用原型对象的内存空间。当修改克隆对象中的基本数据类型对象的值时,只是修改了克隆对象自身的值,不会影响原型对象的值。
而克隆对象中的引用数据类型对象则是引用了原型对象的内存地址,它们指向同一个对象。当修改克隆对象中的引用数据类型对象的值时,实际上是修改了原型对象的值,因为它们共享同一块内存空间。所以修改克隆对象中引用数据类型对象的值会导致原型对象的值发生改变。
如果我们不希望对克隆对象的修改影响到原始对象,那么我们可以使用深克隆实现:
深克隆:
是指创建一个新的对象
,这个新对象与原对象是完全独立的,它们在内存中的地址是不同的。因此,修改克隆对象中引用类型的值不会影响原型对象。
方式 1:重写clone方法,依次调用成员变量的clone方法
如下所示:
package originModel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor class Teacher implements Cloneable{ private String name; private Student student; @Override protected Object clone() throws CloneNotSupportedException { Teacher teacher= (Teacher) super.clone();//该行实现的依然是浅克隆 //克隆引用对象 Student student1= (Student) teacher.getStudent().clone(); teacher.setStudent(student1); return teacher; } }
既然要调用引用对象Student的克隆方法,那么Student类也要实现对应的Cloneable接口和clone方法
package originModel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor public class Student implements Cloneable{ private int age; private String name; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
修改测试类如下所示:
package originModel; public class test1 { public static void main(String[] args) throws CloneNotSupportedException { Teacher teacher1=new Teacher(); teacher1.setName("张三"); teacher1.setStudent(new Student(19,"小明")); Teacher teacher2= (Teacher) teacher1.clone(); System.out.println(teacher1.getStudent()); System.out.println(teacher2.getStudent()); System.out.println(teacher1.getStudent()==teacher2.getStudent()); // 验证1:克隆对象中引用数据类型值的改变是否会影响到原型对象? teacher2.getStudent().setName("李华"); teacher2.getStudent().setAge(20); System.out.println(teacher1.getStudent()); System.out.println(teacher2.getStudent()); System.out.println(teacher1.getStudent()==teacher2.getStudent()); // 验证2:克隆对象中基本数据类型值的改变是否会影响到原型对象? System.out.println(teacher1.getName()); System.out.println(teacher2.getName()); System.out.println(teacher1.getName()==teacher2.getName()); teacher2.setName("李红"); System.out.println(teacher1.getName()); System.out.println(teacher2.getName()); System.out.println(teacher1.getName()==teacher2.getName()); } }
输出如下所示:
Student(age=19, name=小明) Student(age=19, name=小明) false Student(age=19, name=小明) Student(age=20, name=李华) false 张三 张三 true 张三 李红 false
但是如果一个类中包含的引用类型对象有很多,我们使用这种方式的效率就非常低
方式2:使用Json工具,比如GSON对象序列化和反序列化实现深克隆
第一步导入对应的依赖:
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10.1</version> </dependency>
如下所示:
package originModel; import com.google.gson.Gson; public class test1 { public static void main(String[] args) throws CloneNotSupportedException { Teacher teacher1=new Teacher(); teacher1.setName("张三"); teacher1.setStudent(new Student(19,"小明")); Gson gson=new Gson(); //对象的序列化 String json=gson.toJson(teacher1); //对象的反序列化 Teacher teacher2=gson.fromJson(json,Teacher.class); System.out.println(teacher1.getStudent()); System.out.println(teacher2.getStudent()); System.out.println(teacher1.getStudent()==teacher2.getStudent()); // 验证1:克隆对象中引用数据类型值的改变是否会影响到原型对象? teacher2.getStudent().setName("李华"); teacher2.getStudent().setAge(20); System.out.println(teacher1.getStudent()); System.out.println(teacher2.getStudent()); System.out.println(teacher1.getStudent()==teacher2.getStudent()); // 验证2:克隆对象中基本数据类型值的改变是否会影响到原型对象? System.out.println(teacher1.getName()); System.out.println(teacher2.getName()); System.out.println(teacher1.getName()==teacher2.getName()); teacher2.setName("李红"); System.out.println(teacher1.getName()); System.out.println(teacher2.getName()); System.out.println(teacher1.getName()==teacher2.getName()); } }
输出如下所示:
对克隆对象(teacher2)的修改没有影响到原始对象(teacher1)
Student(age=19, name=小明) Student(age=19, name=小明) false Student(age=19, name=小明) Student(age=20, name=李华) false 张三 张三 false 张三 李红 false