在讲述这个模式之前,我们先看一个案例:复制简历
先创建一个简历类
public class Resume { private String name; private String sex; private String age; private String timeArea; private String company; public Resume(String name) { super(); this.name = name; } public void setPersonalInfo(String sex,String age){ this.sex = sex; this.age = age; } public void setWorkExperience(String timeArea, String company) { this.timeArea = timeArea; this.company = company; } public void display() { System.out.println("Resume [name=" + name + ", sex=" + sex + ", age=" + age + ", timeArea=" + timeArea + ", company=" + company + "]"); } }
测试方法
public class Test { public static void main(String[] args) { Resume a = new Resume("张三"); a.setPersonalInfo("男", "30"); a.setWorkExperience("1999-2000", "XX公司"); Resume b = new Resume("张三"); b.setPersonalInfo("男", "30"); b.setWorkExperience("1999-2000", "XX公司"); Resume c = new Resume("张三"); c.setPersonalInfo("男", "30"); c.setWorkExperience("1999-2000", "XX公司"); a.display(); b.display(); c.display(); } }
输出结果
Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司] Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司] Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司]
上面的实现非常简单,如果需要20分简历,就需要实例化20次,如果想把1999改成1998,那么也需要修改20次。
看到这肯定觉得这样的操作非常笨,只要把不变的信息定义成全局变量,定义一个List集合,利用循环直接实例化20次,每循环一次,把全局变量的信息赋值给新对象,再把新对象放入List集合中,最后List集合里就有了20个相同的简历对象。
这么做当然和上面的代码产生了相同的效果,但这是真正的复制吗?
如果已知一个对象有200个成员变量,我们要复制这个对象,那么就需要把这个对象的200个成员变量先取出来,用全局变量存储这200个成员变量的值,然后再创建一个新对象,把全局变量存储的值赋值给新对象的200个成员变量。期间还要小心谨慎,不能赋错了。这样做不是一样的傻么。
那怎样复制一个对象呢?
原型模式介绍:http://www.runoob.com/design-pattern/prototype-pattern.html
简历类实现 Cloneable,重写 clone()
public class Resume implements Cloneable { private String name; private String sex; private String age; private String timeArea; private String company; public Resume(String name) { super(); this.name = name; } public void setPersonalInfo(String sex,String age){ this.sex = sex; this.age = age; } public void setWorkExperience(String timeArea, String company) { this.timeArea = timeArea; this.company = company; } public void display() { System.out.println("Resume [name=" + name + ", sex=" + sex + ", age=" + age + ", timeArea=" + timeArea + ", company=" + company + "]"); } public Resume clone() throws CloneNotSupportedException{ Resume o = (Resume) super.clone(); return o; } }
测试方法
public class Test { public static void main(String[] args) throws CloneNotSupportedException { Resume a = new Resume("张三"); a.setPersonalInfo("男", "30"); a.setWorkExperience("1999-2000", "XX公司"); Resume b = a.clone(); a.display(); b.display(); } }
输出结果
Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司] Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司]
这样就实现了对象的复制,调用对象的clone()就能复制出一个新的对象。
克隆相比之前的new对象更快吗?
每new一次,都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行这个初始化操作就实在是太低效了。一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大对的提高。
如果复制的对象中包含其他自定义的类,那么这些自定义的类也会被复制吗?
创建一个地址类
public class Address { private String country; private String province; public Address(String country, String province) { super(); this.country = country; this.province = province; } @Override public String toString() { return "Address [country=" + country + ", province=" + province + "]"; } //省略getter和setter方法 }
在简历类中增加一个地址类成员变量
public class Resume implements Cloneable { private String name; private String sex; private String age; private String timeArea; private String company; private Address address; public Resume(String name) { super(); this.name = name; } public void setPersonalInfo(String sex,String age){ this.sex = sex; this.age = age; } public void setWorkExperience(String timeArea, String company) { this.timeArea = timeArea; this.company = company; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public void display() { System.out.println("Resume [name=" + name + ", sex=" + sex + ", age=" + age + ", timeArea=" + timeArea + ", company=" + company + ", address=" + address + "]"); } public Resume clone() throws CloneNotSupportedException{ Resume o = (Resume) super.clone(); return o; } }
测试方法
public class Test { public static void main(String[] args) throws CloneNotSupportedException { Address address = new Address("中国", "山东"); Resume a = new Resume("张三"); a.setPersonalInfo("男", "30"); a.setWorkExperience("1999-2000", "XX公司"); a.setAddress(address); Resume b = a.clone(); b.setPersonalInfo("男", "31"); a.getAddress().setProvince("江苏"); b.getAddress().setProvince("湖南"); a.display(); b.display(); } }
输出结果
Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司, address=Address [country=中国, province=湖南]] Resume [name=张三, sex=男, age=31, timeArea=1999-2000, company=XX公司, address=Address [country=中国, province=湖南]]
这和我们期望的不同。我们想要简历a的省份是江苏,简历b的省份是湖南,结果却是两份简历的省份都是湖南。
为什么是这样?
如果复制的字段能通过他们的内容判断是否相等,那么复制时就会逐位复制。如:基本数据类型和String类。否则只会复制引用但不复制引用的对象。这就叫做“浅复制”,被复制对象的所有变量都含有与原来的对象相同的值,而所有的引用变量都仍然指向原来的对象。
那如果想把要引用变量所引用的对象也复制,该怎么办?
这种方式叫做“深复制”。
让地址类也实现 Cloneable,重写 clone()
public class Address implements Cloneable{ private String country; private String province; public Address(String country, String province) { super(); this.country = country; this.province = province; } public Address clone() throws CloneNotSupportedException{ Address o = (Address) super.clone(); return o; } @Override public String toString() { return "Address [country=" + country + ", province=" + province + "]"; } //省略getter和setter方法 }
简历类在克隆时也将地址类克隆
public class Resume implements Cloneable { private String name; private String sex; private String age; private String timeArea; private String company; private Address address; public Resume(String name) { super(); this.name = name; } public void setPersonalInfo(String sex,String age){ this.sex = sex; this.age = age; } public void setWorkExperience(String timeArea, String company) { this.timeArea = timeArea; this.company = company; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public void display() { System.out.println("Resume [name=" + name + ", sex=" + sex + ", age=" + age + ", timeArea=" + timeArea + ", company=" + company + ", address=" + address + "]"); } public Resume clone() throws CloneNotSupportedException{ Resume o = (Resume) super.clone(); this.address = this.address.clone(); return o; } }
测试方法
public class Test { public static void main(String[] args) throws CloneNotSupportedException { Address address = new Address("中国", "山东"); Resume a = new Resume("张三"); a.setPersonalInfo("男", "30"); a.setWorkExperience("1999-2000", "XX公司"); a.setAddress(address); Resume b = a.clone(); b.setPersonalInfo("男", "31"); a.getAddress().setProvince("江苏"); b.getAddress().setProvince("湖南"); a.display(); b.display(); } }
输出结果
Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司, address=Address [country=中国, province=江苏]] Resume [name=张三, sex=男, age=31, timeArea=1999-2000, company=XX公司, address=Address [country=中国, province=湖南]]
这样就实现了复制引用变量时,把引用变量指向的对象也复制一遍。