1.Object.clone()方法
在学习原型模式以前,我们先学习下Object对象的一个clone()方法,JDK1.8源码如下:
/**
* Creates and returns a copy of this object. The precise meaning
* of "copy" may depend on the class of the object. The general
* intent is that, for any object {@code x}, the expression:
* <blockquote>
* <pre>
* x.clone() != x</pre></blockquote>
* will be true, and that the expression:
* <blockquote>
* <pre>
* x.clone().getClass() == x.getClass()</pre></blockquote>
* will be {@code true}, but these are not absolute requirements.
* While it is typically the case that:
* <blockquote>
* <pre>
* x.clone().equals(x)</pre></blockquote>
* will be {@code true}, this is not an absolute requirement.
* <p>
* By convention, the returned object should be obtained by calling
* {@code super.clone}. If a class and all of its superclasses (except
* {@code Object}) obey this convention, it will be the case that
* {@code x.clone().getClass() == x.getClass()}.
* <p>
* By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by {@code super.clone} before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by {@code super.clone}
* need to be modified.
* <p>
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
* <p>
* The class {@code Object} does not itself implement the interface
* {@code Cloneable}, so calling the {@code clone} method on an object
* whose class is {@code Object} will result in throwing an
* exception at run time.
*
* @return a clone of this instance.
* @throws CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
该方法的作用就创建并返回一个对象的复制品,复制品与当前对象是同一个类型,但是用==判断为false,以上通常是正确的,但也不是绝对的,至于equals方法判断是不是相等还是要看该eauals方法的实现了。通常一个类和其所有的父类(除了Object)都遵守调用super.clone()获取重写的clone()的结果,那么相同类型比较为true是成立的。如果一个对象需要重写该方法,那么它也同时需要实现Cloneable接口,如果未实现,这执行clone()方法时会导致CloneNotSupportedException被抛出。
public class User implements Cloneable{
private int age;
private String name;
@Override
public String toString() {
return "User [age=" + age + ", name=" + name + "]";
}
public User(int age, String name) {
super();
this.age = age;
this.name = name;
}
public User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}
**************************************************************************
public class PrototypeDemo {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User(23, "rose");
System.out.println("user:" + user);
User copy = user.clone();
System.out.println("copy:" + copy);
boolean classFlag = user.getClass() == copy.getClass();
System.out.println("user.getClass()==copy.getClass()" + classFlag);
System.out.println("user == copy" + (user == copy));
System.out.println("user.equals(copy)" + user.equals(copy));
}
}
********************************************************************************
user:User [age=23, name=rose]
copy:User [age=23, name=rose]
user.getClass()==copy.getClass()true
user == copyfalse
user.equals(copy)false
2.深拷贝和浅拷贝
对以上测试用例中的user的年龄和姓名分别修改后再进行输出对象,查看结果如下:
public class PrototypeDemo {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User(23, "rose");
System.out.println("user:" + user);
User copy = user.clone();
System.out.println("copy:" + copy);
user.setAge(16);
System.out.println("update user age from 23 to 16");
System.out.println("user:" + user);
System.out.println("copy:" + copy);
user.setName("juli");
System.out.println("update user name from rose to juli");
System.out.println("user:" + user);
System.out.println("copy:" + copy);
}
}
****************************************************************************************
user:User [age=23, name=rose]
copy:User [age=23, name=rose]
update user age from 23 to 16
user:User [age=16, name=rose]
copy:User [age=23, name=rose]
update user name from rose to juli
user:User [age=16, name=juli]
copy:User [age=23, name=rose]
可以看出对于user对象的姓名和年龄的更改并不影响拷贝的姓名和年龄,那么我们再看一个例子,我们在User类中添加一个Department类变量,然后再做类似上面的修改并输出,
public class User implements Cloneable{
private int age;
private String name;
private Department deparment;
public User(int age, String name, Department deparment) {
this.age = age;
this.name = name;
this.deparment = deparment;
}
public Department getDeparment() {
return deparment;
}
public void setDeparment(Department deparment) {
this.deparment = deparment;
}
@Override
public String toString() {
return "User [age=" + age + ", name=" + name + ", deparment=" + deparment + "]";
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}
**********************************************************************************
public class Department {
private String name;
private String location;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public Department(String name, String location) {
super();
this.name = name;
this.location = location;
}
@Override
public String toString() {
return "Department [name=" + name + ", location=" + location + "]";
}
}
************************************************************************************
public class PrototypeDemo {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User(23, "rose",new Department("dev", "北京雍和宫"));
System.out.println("user:" + user);
User copy = user.clone();
System.out.println("copy:" + copy);
user.getDeparment().setLocation("上海东方明珠");
System.out.println("update user Department location from 北京雍和宫 to 上海东方明珠");
System.out.println("user:" + user);
System.out.println("copy:" + copy);
}
}
***********************************************************************************
user:User [age=23, name=rose, deparment=Department [name=dev, location=北京雍和宫]]
copy:User [age=23, name=rose, deparment=Department [name=dev, location=北京雍和宫]]
update user Department location from 北京雍和宫 to 上海东方明珠
user:User [age=23, name=rose, deparment=Department [name=dev, location=上海东方明珠]]
copy:User [age=23, name=rose, deparment=Department [name=dev, location=上海东方明珠]]
如上我们可以看出,Department的变化导致了复制品的 Department变化,原因就在于clone()方法默认是浅拷贝,也就是API注释中的shallow copy,那么我们怎么做才能保证复制品和源对象的相对独立呢?文档中如是说:
By convention, the object returned by this method should be independent
* of this object (which is being cloned). To achieve this independence,
* it may be necessary to modify one or more fields of the object returned
* by {@code super.clone} before returning it. Typically, this means
* copying any mutable objects that comprise the internal "deep structure"
* of the object being cloned and replacing the references to these
* objects with references to the copies. If a class contains only
* primitive fields or references to immutable objects, then it is usually
* the case that no fields in the object returned by {@code super.clone}
* need to be modified.
在clone()方法中返回copy之前那些既非基本类型又非指向不可变对象的引用类型属性进行修改,这就是深度拷贝。对User的clone()方法修改后如下:
public User clone() throws CloneNotSupportedException {
User copy= (User) super.clone();
copy.setDeparment(new Department(this.deparment.getName(), this.deparment.getLocation()));
return copy;
}
运行结果呢?
user:User [age=23, name=rose, deparment=Department [name=dev, location=北京雍和宫]]
copy:User [age=23, name=rose, deparment=Department [name=dev, location=北京雍和宫]]
update user Department location from 北京雍和宫 to 上海东方明珠
user:User [age=23, name=rose, deparment=Department [name=dev, location=上海东方明珠]]
copy:User [age=23, name=rose, deparment=Department [name=dev, location=北京雍和宫]]
复制品不因为源对象的变化而变化。但是这种实现深度拷贝(deep copy)较为麻烦,当系统要拷贝的类较为多的时候,那么我们可以使用序列化和反序列化的方式简化该操作,因为序列化后再反序列化会生成一个新的对象。
/*******************
** 有些流的关闭不规范,此处只是演示序列化与反序列化
/
public User clone() throws CloneNotSupportedException {
// User copy= (User) super.clone();
// copy.setDeparment(new Department(this.deparment.getName(), this.deparment.getLocation()));
// return copy;
try {
FileOutputStream fileOutputStream = new FileOutputStream("objSer.file");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(this);
objectOutputStream.flush();
objectOutputStream.close();
fileOutputStream.close();
FileInputStream fis = new FileInputStream("objSer.file");
ObjectInputStream ois = new ObjectInputStream(fis);
User copy = (User) ois.readObject();
ois.close();
ois.close();
return copy;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
3.原型模式
假如我们有一些对象的创建成本是很高的,那么我们可以将这些对象创建之后,缓存起来,每次要使用的时候返回一个该对象的深度拷贝对象,这样子就能减少系统的压力,提高运行效率。大概的演示如下(仅仅提供了思路,具体的代码省略)
ublic class SystemBigObj {
//字段以及getter setter方法等等
private Integer id;
//重写clone()方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Override
public SystemBigObj clone() throws CloneNotSupportedException {
// User copy= (User) super.clone();
// copy.setDeparment(new Department(this.deparment.getName(), this.deparment.getLocation()));
// return copy;
try {
FileOutputStream fileOutputStream = new FileOutputStream("objSer.file");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(this);
objectOutputStream.flush();
objectOutputStream.close();
fileOutputStream.close();
FileInputStream fis = new FileInputStream("objSer.file");
ObjectInputStream ois = new ObjectInputStream(fis);
SystemBigObj copy = (SystemBigObj) ois.readObject();
ois.close();
ois.close();
return copy;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
class SystemBigObjCache{
private static final ConcurrentHashMap<Integer, SystemBigObj> cahce
= new ConcurrentHashMap<Integer, SystemBigObj>();
public static void loadCache() {
//创建过程复杂,查询数据库,redis,各种计算等等
SystemBigObj systemBigObj1 = new SystemBigObj();
SystemBigObj systemBigObj2 = new SystemBigObj();
cahce.put(systemBigObj1.getId(), systemBigObj1);
cahce.put(systemBigObj2.getId(), systemBigObj2);
}
public static SystemBigObj getById(Integer id) throws CloneNotSupportedException {
return cahce.get(id).clone();
}
}
以后我们就可以在SystemBigObjCache中通过getById()方法传入id参数获取对象的拷贝。