java常用设计模式三:原型模式
在说原型模式之前先说一下浅拷贝和深拷贝的概念
一、浅拷贝和深拷贝
1、浅拷贝
在java中,对象创建后需要有一个引用变量来指向该对象实际的地址空间,也就是说引用变量与对象实体是两个不同的数据体。在Object类的clone()方法中。
对对象字段进行复制时,如果字段是String 和8种基本数据类型(int,boolean,char,byte,short,float,double.long)包括对应的封装类,则会复制字段的值到一个新的变量中;而字段是引用类型,则仅会将引用值复制给新对象中的相应字段中,也就是说,两个字段指向了同一个对象实例。看以下的例子来理解浅拷贝。
1)用户类
public class User{ private String name; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
2)学生类:如果要让Student类支持clone方法,必须实现Cloneable接口
public class Student implements Cloneable { private User user; private String message; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } /** * 重写clone()方法为public类型,实现拷贝功能 */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); }
3)测试类
public class TestCopy { public static void main(String[] args) throws CloneNotSupportedException { User user = new User(); user.setName("张三"); user.setSex("男"); Student oldStudent = new Student(); oldStudent.setUser(user); oldStudent.setMessage("old message"); System.out.println("oldStudent:"+oldStudent.getUser().getName()+" :"+oldStudent.getUser().getSex()+" :"+oldStudent.getMessage()); Student copyStudent = (Student) oldStudent.clone(); System.out.println("copyStudent:"+copyStudent.getUser().getName()+" :"+copyStudent.getUser().getSex()+" :"+copyStudent.getMessage()); System.out.println("oldStudent == copyStudent ? " +(oldStudent == copyStudent)); copyStudent.getUser().setName("李丽"); copyStudent.getUser().setSex("女"); copyStudent.setMessage("new message"); System.out.println("修改copyStudent的内容后==========="); System.out.println("oldStudent:"+oldStudent.getUser().getName()+" :"+oldStudent.getUser().getSex()+" :"+oldStudent.getMessage()); System.out.println("copyStudent:"+copyStudent.getUser().getName()+" :"+copyStudent.getUser().getSex()+" :"+copyStudent.getMessage()); } }
oldStudent:张三 :男 :old message copyStudent:张三 :男 :old message oldStudent == copyStudent ? false 修改copyStudent的内容后=========== oldStudent:李丽 :女 :old message copyStudent:李丽 :女 :new message
由以上结果可知道,oldStudent和copyStudent并不是同一个对象,当将copyStudent的user信息和message信息修改后,oldStudent的user信息相应也改变了,oldStudent的message没有改变。
得出结论:在进行用Object类对Student的User属性进行拷贝时是浅拷贝,也就是说只是将oldStudent里面user引用变量复制到了copyStudent里面,2个变量实际上指向的是同一块地址。
2、深拷贝
那就是对于引用型变量,深拷贝会开辟一块新的内存空间,将被复制引用所指向的对象实例的各个属性复制到新的内存空间中,然后将新的引用指向块内存(也就是一个新的实例)。以后对新引用指向的实例属性进行修改的时候就不会影响到老引用指向的实例属性。
我们将浅拷贝的例子修改下:
1)用户类:也实现了Cloneable接口
public class User implements Cloneable{ private String name; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } /** * 由于User类里面只有基本数据类型的封装类,所以调用Object.clone()方法得到的是深拷贝内容 * @return * @throws CloneNotSupportedException */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
2)学生类也修改如下:主要是clone方法
public class Student implements Cloneable { private User user; private String message; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } /** * 重写clone()方法为public类型,然后实现深拷贝功能。 */ @Override public Object clone() throws CloneNotSupportedException { Student student = (Student)super.clone(); student.user = (User) user.clone(); return student; } }
3)测试类不变
public class TestCopy { public static void main(String[] args) throws CloneNotSupportedException { User user = new User(); user.setName("张三"); user.setSex("男"); Student oldStudent = new Student(); oldStudent.setUser(user); oldStudent.setMessage("old message"); System.out.println("oldStudent:"+oldStudent.getUser().getName()+" :"+oldStudent.getUser().getSex()+" :"+oldStudent.getMessage()); Student copyStudent = (Student) oldStudent.clone(); System.out.println("copyStudent:"+copyStudent.getUser().getName()+" :"+copyStudent.getUser().getSex()+" :"+copyStudent.getMessage()); System.out.println("oldStudent == copyStudent ? " +(oldStudent == copyStudent)); copyStudent.getUser().setName("李丽"); copyStudent.getUser().setSex("女"); copyStudent.setMessage("new message"); System.out.println("修改copyStudent的内容后==========="); System.out.println("oldStudent:"+oldStudent.getUser().getName()+" :"+oldStudent.getUser().getSex()+" :"+oldStudent.getMessage()); System.out.println("copyStudent:"+copyStudent.getUser().getName()+" :"+copyStudent.getUser().getSex()+" :"+copyStudent.getMessage()); } }
oldStudent:张三 :男 :old message copyStudent:张三 :男 :old message oldStudent == copyStudent ? false 修改copyStudent的内容后=========== oldStudent:张三 :男 :old message copyStudent:李丽 :女 :new message
由以上执行结果可知:对copyStudent的修改并不会影响到oldStudent,因此实现了深拷贝。
还可以通过序列化和反序列化实现 深拷贝
User类:要实现序列化
public class User implements Serializable{ private String name; private String sex; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
Student类:要实现序列化
public class Student implements Serializable { private User user; private String message; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } /** * 注意,这个clone()方法已不再是Object类的clone方法了 */ public Student clone(){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayInputStream bais = null; ObjectOutputStream oos = null; ObjectInputStream ois = null; Student student = null; try { oos = new ObjectOutputStream(baos); //将本身实例写入流中 oos.writeObject(this); bais=new ByteArrayInputStream(baos.toByteArray()); ois = new ObjectInputStream(bais); student = (Student)ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return student; }
测试类:
public class TestCopy { public static void main(String[] args) { User user = new User(); user.setName("张三"); user.setSex("男"); Student oldStudent = new Student(); oldStudent.setUser(user); oldStudent.setMessage("old message"); System.out.println("oldStudent:"+oldStudent.getUser().getName()+" :"+oldStudent.getUser().getSex()+" :"+oldStudent.getMessage()); Student copyStudent = oldStudent.clone(); System.out.println("copyStudent:"+copyStudent.getUser().getName()+" :"+copyStudent.getUser().getSex()+" :"+copyStudent.getMessage()); System.out.println("oldStudent == copyStudent ? " +(oldStudent == copyStudent)); copyStudent.getUser().setName("李丽"); copyStudent.getUser().setSex("女"); copyStudent.setMessage("new message"); System.out.println("修改copyStudent的内容后==========="); System.out.println("oldStudent:"+oldStudent.getUser().getName()+" :"+oldStudent.getUser().getSex()+" :"+oldStudent.getMessage()); System.out.println("copyStudent:"+copyStudent.getUser().getName()+" :"+copyStudent.getUser().getSex()+" :"+copyStudent.getMessage()); } }
oldStudent:张三 :男 :old message copyStudent:张三 :男 :old message oldStudent == copyStudent ? false 修改copyStudent的内容后=========== oldStudent:张三 :男 :old message copyStudent:李丽 :女 :new message
3、原型模式
定义:使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
设计思路:原型模式要求对象实现一个可以“克隆”自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例。这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建。
上面浅拷贝和深拷贝的2个例子,其实就是原型模式的表现形式之一。