标签:副本 状态 tostring 项目 生产 pre 撤销 对象内存 list()
指原型实例指定创建对象的种类,并通过克隆这些原型创建新的对象
原型模式就是让类实现Cloneable接口,达到克隆原型类的方式。
1、在创建对象的时候,我们不只是希望被创建的对象继承其基类的基本结构,还希望继承原型对象的数据。
2、希望对目标对象的修改不影响既有的原型对象(深度克隆的时候可以完全互不影响)。
3、隐藏克隆操作的细节,很多时候,对对象本身的克隆需要涉及到类本身的数据细节。
4、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等;
5、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式;
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式先产生出一个包含大量共有信息的类,然后可以拷贝出副本,修正细节信息,建立了一个完整的个性对象。
7、循环体中生产大量对象时
优点:
缺点:
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
总之深浅克隆都会在堆中新分配一块区域,区别在于对象属性引用的对象是否需要进行克隆(递归性的)。
1.4.1、浅克隆
public class PersonShallow implements Cloneable { private String name; private Integer age; private Date birthday; public PersonShallow() { System.out.println("ctor PersonShallow"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "PersonShallow{" + "name=‘" + name + ‘\‘‘ + ", age=" + age + ", birthday=" + birthday + ‘}‘; } }
测试
@Test public void test() throws Exception { //原型A对象 PersonShallow a = new PersonShallow(); a.setName("李宏旭"); a.setAge(1); a.setBirthday(new Date()); System.out.println("a:" + a); System.out.println("*************克隆**************"); //克隆B对象 PersonShallow b = (PersonShallow) a.clone(); System.out.println("b:" + b); System.out.println("***************比较***************"); /* * 测试A==B? * 对任何的对象x,都有x.clone() !=x,即克隆对象与原对象不是同一个对象 */ System.out.print("比较:A==B?"); System.out.println(a == b?"是一个对象":"不是一个对象"); System.out.println("a.hashCode:"+a.hashCode()); System.out.println("b.hashCode:"+b.hashCode()); b.getBirthday().setTime(666666666666L); System.out.println("a Birthday为:" + a.getBirthday()); System.out.println("b Birthday为:" + b.getBirthday()); a.setName("李四"); System.out.println("a name 为:" + a.getName()); System.out.println("b name 为:" + b.getName()); /* * 比较Date对象 */ System.out.print("比较:A.Date==B.Date?"); System.out.println(a.getBirthday() == b.getBirthday()?"是一个对象":"不是一个"); }
输出
ctor PersonShallow a:PersonShallow{name=‘李宏旭‘, age=1, birthday=Tue Jul 09 17:25:43 CST 2019} *************克隆************** b:PersonShallow{name=‘李宏旭‘, age=1, birthday=Tue Jul 09 17:25:43 CST 2019} ***************比较*************** 比较:A==B?不是一个对象 a.hashCode:1811075214 b.hashCode:1588970020 a Birthday为:Sat Feb 16 09:11:06 CST 1991 b Birthday为:Sat Feb 16 09:11:06 CST 1991 a name 为:李四 b name 为:李宏旭 比较:A.Date==B.Date?是一个对象
可以发现B在克隆A的birthday时,是直接克隆的引用。这种是浅克隆。
说明点:
1、(PersonShallow) a.clone();这样子克隆并不等同于Person p2 = p1;像Person p2 = p1;指的是在栈中创建一个变量p2,将p1的内存地址赋给p2,其实指的是同一个对象。而克隆是复制出一份一模一样的对象,两个对象内存地址不同,但对象中的结构与属性值一模一样。
2、对象克隆拷贝时,类的构造函数是不会被执行的。
3、当被克隆的类中有引用对象(String或Integer等包装类型除外)时,克隆出来的类中的引用变量存储的还是之前的内存地址如Date,也就是说克隆与被克隆的对象是同一个。这样的话两个对象共享了一个私有变量,所有人都可以改,是一个种非常不安全的方式,在实际项目中使用还是比较少的。
示例
public class PersonDeep implements Cloneable { private String name; private Date birthday; private List<String> list; public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } @Override protected Object clone() throws CloneNotSupportedException { PersonDeep deep = null; try { deep = (PersonDeep) super.clone(); // 需要特殊处理引用类型的变量 deep.birthday = (Date) deep.birthday.clone(); List<String> newList = new ArrayList(); if(this.list!=null) for (String str : this.list) { newList.add(str); } deep.setList(newList); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return deep; } @Override public String toString() { return "PersonDeep{" + "name=‘" + name + ‘\‘‘ + ", birthday=" + birthday + ", list=" + list + ‘}‘; } }
测试
@Test public void test() throws Exception { //原型A对象 PersonDeep a = new PersonDeep(); a.setName("李宏旭"); a.setBirthday(new Date()); System.out.println("a:" + a); System.out.println("*************克隆**************"); //克隆B对象 PersonDeep b = (PersonDeep) a.clone(); System.out.println("b:" + b); System.out.println("***************比较***************"); /* * 测试A==B? * 对任何的对象x,都有x.clone() !=x,即克隆对象与原对象不是同一个对象 */ System.out.print("比较:A==B?"); System.out.println(a == b?"是一个对象":"不是一个对象"); System.out.println("a.hashCode:"+a.hashCode()); System.out.println("b.hashCode:"+b.hashCode()); b.getBirthday().setTime(666666666666L); System.out.println("a Birthday为:" + a.getBirthday()); System.out.println("b Birthday为:" + b.getBirthday()); a.setName("李四"); System.out.println("a name 为:" + a.getName()); System.out.println("b name 为:" + b.getName()); /* * 比较Date对象 */ System.out.print("比较:A.Date==B.Date?"); System.out.println(a.getBirthday() == b.getBirthday()?"是一个对象":"不是一个"); }
输出
a:PersonDeep{name=‘李宏旭‘, birthday=Tue Jul 09 18:03:23 CST 2019, list=null} *************克隆************** b:PersonDeep{name=‘李宏旭‘, birthday=Tue Jul 09 18:03:23 CST 2019, list=[]} ***************比较*************** 比较:A==B?不是一个对象 a.hashCode:1811075214 b.hashCode:1588970020 a Birthday为:Tue Jul 09 18:03:23 CST 2019 b Birthday为:Sat Feb 16 09:11:06 CST 1991 a name 为:李四 b name 为:李宏旭 比较:A.Date==B.Date?不是一个
这样就完成了深度拷贝,两种对象互为独立,属于单独对象。
注意:final 类型修饰的成员变量不能进行深度拷贝
ArrayList的clone()
HashMap的clone()
CacheKey的clone()
防守打法
标签:副本 状态 tostring 项目 生产 pre 撤销 对象内存 list()
原文地址:https://www.cnblogs.com/bjlhx/p/11159194.html