Prototype(原型)
Cloneable
接口来扮演此角色。ConcreteProrotype(具体原型)
User
类扮演此角色。Client(使用者)
1.用java实现原型模式比较简单,只需要将目标类实现Cloneable
接口即可
@Getter @Setter @AllArgsConstructor public class User implements Cloneable { private String name; private Date birthday; public User() { System.out.println("User constructor被调用"); } @Override public Object clone() throws CloneNotSupportedException { System.out.println("User clone被调用"); // 浅克隆写法 return super.clone(); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", birthday=" + birthday + '}'+super.toString(); } }
public abstract class AbstractUser implements Cloneable { @Override protected Object clone() throws CloneNotSupportedException { System.out.println("抽象父类clone方法被调用"); return super.clone(); } }
@Getter @Setter public class User extends AbstractUser { private String name; private Date birthday; public User() { System.out.println("User constructor被调用"); } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", birthday=" + birthday + '}'+super.toString(); } }
@Test public void testClone() throws CloneNotSupportedException { Date birthday = new Date(0L); // 1. 创建用户对象 User user1 = new User(); user1.setName("小林"); user1.setBirthday(birthday); // 2. 克隆用户对象 User user2 = (User) user1.clone(); // 3. user1,user2对比 System.out.println(user1); System.out.println(user2); System.out.println(user1 == user2); System.out.println("<==============我是分割线============>"); // 4. 赋值新的birthday并打印 user1.getBirthday().setTime(666666666666L); System.out.println(user1); System.out.println(user2); System.out.println(user1 == user2); }
根据user1和user2的hash值不同可以看出是不同的类,但user1的birthday修改后,user2的birthday也同样被修改,说明user1和user2指向了同一个birthday,这里就引发深拷贝与浅拷贝的问题。
使用clone()
,之后,由于birthday
是一个引用对象,由于是浅拷贝,user中的引用成员变量依然指向的是同一个。
什么是深拷贝,我们clone
user对象的同时,也将其内部引用的对象进行拷贝,使得每个引用对象无关联,都是单独的对象。
clone()
方法的实现@Getter @Setter public class User implements Cloneable { private String name; private Date birthday; public User() { System.out.println("User constructor被调用"); } @Override public Object clone() throws CloneNotSupportedException { System.out.println("User clone被调用"); // 浅拷贝写法 // return super.clone(); // 深拷贝写法 User user = (User) super.clone(); user.birthday=(Date) user.getBirthday().clone(); return user; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", birthday=" + birthday + '}'+super.toString(); } }
可以看到改造完成之后user1的birthday与user2的birthday指向不是同一个birthday了。
使用原型模式时要十分注意深拷贝、浅拷贝的问题,即使了解了深拷贝和浅拷贝,在写代码的过程中一个疏忽就可能产生BUG,如果对深拷贝和浅拷贝不了解同学,需要慎用原型模式。