模式说明

需要获取一个类的多个实例的场景下,若该类的构造器需要做较多的初始化工作,则创建多个实例会耗时耗资源。可以在创建一个实例后,后续实例都直接拷贝该实例可以避免每次获取实例时的初始化开销,对于需要修改的字段针对性修改即可,这就是原型模式。根据对引用类型字段的拷贝程度分为浅拷贝和深拷贝。

浅拷贝

对原型P的实例p1执行实例p1.clone()方法拷贝给p2时,对p1内的引用类型字段(准确地说是可变引用类型)q(类型Q),只拷贝其引用。Java中重写clone方法时若只调用super.clone(),则该clone方法在使用时即为浅拷贝,即上述p1.q == p2.q。注意clone方法在Cloneable接口中是native方法,返回类型是Object,赋值给p2时要向下转型。

  1. P p1 = new P();
  2. P p2 = (P)p1.clone();

可以这样描述以上两条语句。p1.clone()在堆中生成一个新的实例,p2是这个新实例的引用(地址),新实例中有p1的基本数据和不变类型(final修饰的引用类型如String)的完整拷贝,但对于可变的引用类型,只拷贝其引用。即如果p1中有一可变引用字段Q,那么p1.q == p2.q,p1p2中的q指向同一个实例,即原本p1中的q实例。

深拷贝

以浅拷贝中的描述为例,深拷贝对p中的可变引用类型q,也拷贝一个完整的q到新的地址中(与q并列的Q类型新实例),此时p1.q !=p2.q。深拷贝要求Q自身也要实现Cloneable接口并重写clone方法。即在原型P的重写clone方法内针对q要有类似如下的语句。

本示例定义一个Person类,使其实现Cloneable接口并通过重写clone方法使其支持浅拷贝。在Person类中定义一个不变引用类型Country和一个非不变引用类型WorkExperience,展示浅拷贝Person时,对不变类型Country拷贝出一个新实例,对非不变引用类型WorkExperience拷贝的是它的引用(地址)。作为对比,本示例定义一个PersonDeep类,该类内有一个非不变引用类型WorkExperienceDeep,使WorkExperienceDeep也实现Cloneable接口并重写clone方法,且在PersonDeep类的clone方法中,针对非不变引用l类型WorkExperienceDeep,执行一条对它的clone方法来得到它的一个实例,从而实现对PersonDeep的深拷贝。

结构

原型接口(通常就是Cloneable)
具体原型类
实现Cloneable接口并重写clone方法,分为深拷贝和浅拷贝。

代码演示

  1. package com.yukiyama.pattern.creation;
  2. /**
  3. * 原型模式
  4. */
  5. public class ProtoTypeDemo {
  6. public static void main(String[] args) {
  7. System.out.println("====如下是深拷贝示例====");
  8. // 声明一个支持深拷贝的实例p1
  9. PersonDeep pd1 = new PersonDeep();
  10. // 通过原型类中的clone()方法拷贝p1
  11. PersonDeep pd2 = (PersonDeep) pd1.clone();
  12. // p1和p2是两个不同的实例,输出“false”
  13. System.out.println(pd1 == pd2);
  14. // 设置p1
  15. pd1.setName("张三");
  16. pd1.setOccupation("程序员");
  17. pd1.setWorkExperienceDeep("Alibaba");
  18. // 设置拷贝而来的p2
  19. pd2.setName("李四");
  20. pd2.setOccupation("老师");
  21. pd2.setWorkExperienceDeep("Tsinghua");
  22. // 以下3行分别输出“张三”, “程序员”, “Alibaba”
  23. // WorkExperienceDeep没有被pd2修改为"Tsinghua",说明PersonDeep是深拷贝类型
  24. System.out.println(pd1.getName());
  25. System.out.println(pd1.getOccupation());
  26. System.out.println(pd1.getWorkExperience().getCompany());
  27. // 以下3行分别输出“李四”, “老师”, “Tsinghua”
  28. System.out.println(pd2.getName());
  29. System.out.println(pd2.getOccupation());
  30. System.out.println(pd2.getWorkExperience().getCompany());
  31. // 如下输出“false”,说明p1和p2中的引用类型WorkExperience是不同的实例
  32. System.out.println(pd1.getWorkExperience() == pd2.getWorkExperience());
  33. System.out.println("====如下是浅拷贝示例====");
  34. Person p1 = new Person();
  35. Person p2 = (Person) p1.clone();
  36. // p1和p2是两个不同的实例,输出“false”
  37. System.out.println(p1 == p2);
  38. // 设置p1
  39. p1.setName("张三");
  40. p1.setOccupation("程序员");
  41. p1.setWorkExperience("Alibaba");
  42. p1.setCountry("CHINA");
  43. // 设置拷贝而来的p2
  44. p2.setName("李四");
  45. p2.setOccupation("老师");
  46. p2.setWorkExperience("Tsinghua");
  47. p2.setCountry("SINGAPORE");
  48. // 以下4行分别输出“张三”, “程序员”, “Tsinghua”,“CHINA”
  49. // WorkExperience被p2修改为"Tsinghua",说明Person是浅拷贝类型
  50. // 另外,p1中的CHINA并没有因为p2设置为了SINGAPORE而受到影响,
  51. // 说明自定义的不变类Country在浅拷贝中也拷贝到了新实例,而不是只拷贝其引用
  52. System.out.println(p1.getName());
  53. System.out.println(p1.getOccupation());
  54. System.out.println(p1.getWorkExperience().getCompany());
  55. System.out.println(p1.getCountry());
  56. // 以下4行分别输出“李四”, “老师”, “Tsinghua”,“SINGAPORE”
  57. System.out.println(p2.getName());
  58. System.out.println(p2.getOccupation());
  59. System.out.println(p2.getWorkExperience().getCompany());
  60. System.out.println(p2.getCountry());
  61. // 如下输出“true”,说明p1和p2中的引用类型WorkExperience是相同的实例
  62. System.out.println(p1.getWorkExperience() == p2.getWorkExperience());
  63. // 如下输出“false”,说明p1和p2中的引用类型WCountry是不同的实例
  64. System.out.println(p1.getCountry() == p2.getCountry());
  65. }
  66. }
  67. /**
  68. * 深拷贝版
  69. * 原型类
  70. * 实现Cloneable接口并重写clone方法
  71. * 本类中的非不变引用类型字段也通过该字段自身的clone()进行拷贝
  72. */
  73. class PersonDeep implements Cloneable{
  74. private String name;
  75. private String occupation;
  76. private int age;
  77. private WorkExperienceDeep workExperience;
  78. // 在构造器中实例化引用类型WorkExperience
  79. public PersonDeep() {
  80. workExperience = new WorkExperienceDeep();
  81. }
  82. public int getAge() {
  83. return age;
  84. }
  85. public void setAge(int age) {
  86. this.age = age;
  87. }
  88. public String getName() {
  89. return name;
  90. }
  91. public void setName(String name) {
  92. this.name = name;
  93. }
  94. public String getOccupation() {
  95. return occupation;
  96. }
  97. public void setOccupation(String occupation) {
  98. this.occupation = occupation;
  99. }
  100. public WorkExperienceDeep getWorkExperience() {
  101. return workExperience;
  102. }
  103. public void setWorkExperienceDeep(String company) {
  104. this.workExperience.setCompany(company);
  105. }
  106. // 重写clone()使得原型类Person能够执行clone()从而被拷贝
  107. @Override
  108. protected Object clone() {
  109. PersonDeep person = null;
  110. try {
  111. person = (PersonDeep) super.clone();
  112. person.workExperience = (WorkExperienceDeep) person.getWorkExperience().clone();
  113. } catch (CloneNotSupportedException e) {
  114. e.printStackTrace();
  115. }
  116. return person;
  117. }
  118. }
  119. /**
  120. * 深拷贝版
  121. * 原型类型中的引用类型字段
  122. */
  123. class WorkExperienceDeep implements Cloneable{
  124. private String company;
  125. public String getCompany() {
  126. return company;
  127. }
  128. public void setCompany(String company) {
  129. this.company = company;
  130. }
  131. @Override
  132. protected Object clone() {
  133. WorkExperienceDeep we = null;
  134. try {
  135. we = (WorkExperienceDeep) super.clone();
  136. } catch (CloneNotSupportedException e) {
  137. e.printStackTrace();
  138. }
  139. return we;
  140. }
  141. }
  142. /**
  143. * 浅拷贝版
  144. * 原型类,实现Cloneable接口并重写clone方法
  145. */
  146. class Person implements Cloneable{
  147. private String name;
  148. private String occupation;
  149. private int age;
  150. private WorkExperience workExperience;
  151. private Country country;
  152. // 在构造器中实例化引用类型WorkExperience
  153. public Person() {
  154. workExperience = new WorkExperience();
  155. }
  156. public int getAge() {
  157. return age;
  158. }
  159. public void setAge(int age) {
  160. this.age = age;
  161. }
  162. public String getName() {
  163. return name;
  164. }
  165. public void setName(String name) {
  166. this.name = name;
  167. }
  168. public String getOccupation() {
  169. return occupation;
  170. }
  171. public void setOccupation(String occupation) {
  172. this.occupation = occupation;
  173. }
  174. public WorkExperience getWorkExperience() {
  175. return workExperience;
  176. }
  177. public String getCountry() {
  178. return country.getCountryName();
  179. }
  180. public void setCountry(String countryName) {
  181. this.country = new Country(countryName);
  182. }
  183. public void setWorkExperience(String company) {
  184. this.workExperience.setCompany(company);
  185. }
  186. // 重写clone()使得原型类Person能够对外拷贝
  187. @Override
  188. protected Object clone() {
  189. Person person = null;
  190. try {
  191. person = (Person) super.clone();
  192. } catch (CloneNotSupportedException e) {
  193. e.printStackTrace();
  194. }
  195. return person;
  196. }
  197. }
  198. /**
  199. * 浅拷贝版
  200. * 原型类型中的引用类型字段
  201. */
  202. class WorkExperience{
  203. private String company;
  204. public String getCompany() {
  205. return company;
  206. }
  207. public void setCompany(String company) {
  208. this.company = company;
  209. }
  210. }
  211. /**
  212. * 浅拷贝版
  213. * 自定义不变类,用于演示在浅拷贝中对不变类的拷贝结果
  214. */
  215. class Country{
  216. private String countryName;
  217. public Country(String countryName) {
  218. this.countryName = countryName;
  219. }
  220. public String getCountryName() {
  221. return countryName;
  222. }
  223. }