原型模式:添加一个自我克隆的方法,可以调用object的clone()(需要实现Cloneable接口,不会调用构造器),也可以自己写个clone()方法进行克隆。
这里涉及到一个深复制和浅复制的东西,也就是对象中包含其他对象的时候需要通过深复制来复制被包含对象,否则被包含对象只是克隆出来了一个引用而已(理解了java中所谓的“引用”就明白 了,java堆内存和栈内存的处理)。
先看示例:使用Cloneable接口通过Object的clone实现
package com.zpj.designMode.prototypePattern; /** * @author PerKins Zhu * @date:2016年9月5日 下午8:37:31 * @version :1.1 * */ public class Person implements Cloneable { String name; int age; public Person() { System.out.println("----无参 构造器Person------"); } public Person(String name, int age) { super(); this.name = name; this.age = age; System.out.println("----带参 构造器Person------"); } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
测试代码:
package com.zpj.designMode.prototypePattern; import org.junit.Test; /** * @author PerKins Zhu * @date:2016年9月5日 下午8:40:51 * @version :1.1 * */ public class TestUnit { @Test public void test01() throws CloneNotSupportedException{ Person p1= new Person("li",10); Person p2 =(Person)p1.clone(); System.out.println(p2.age); System.out.println(p2.name); System.out.println(p1.equals(p2)); } }
运行结果:
可见进行clone的时候并没有调用构造器进行。克隆出来的对象空间地址是不一样的。
自己实现克隆方法:
package com.zpj.designMode.prototypePattern; /** * @author PerKins Zhu * @date:2016年9月5日 下午8:37:31 * @version :1.1 * */ public class Person { String name; int age; public Person() { System.out.println("----无参 构造器Person------"); } public Person(String name, int age) { super(); this.name = name; this.age = age; System.out.println("----带参 构造器Person------"); } public Person clone() { Person per = new Person(); per.age = this.age; per.name = this.name; return per; } }
进行测试:
package com.zpj.designMode.prototypePattern; import org.junit.Test; /** * @author PerKins Zhu * @date:2016年9月5日 下午8:40:51 * @version :1.1 * */ public class TestUnit { @Test public void test01() throws CloneNotSupportedException{ Person p1= new Person("li",10); Person p2 =(Person)p1.clone(); System.out.println(p2.age); System.out.println(p2.name); System.out.println(p1.equals(p2)); } }
运行结果:
可见:通过new出来的对象调用了无参构造方法。两个对象的物理地址是不同的。
第二种clone实现很明了,不在赘述,那么看一次啊Cloneable是怎么使用的。
@Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }
这里调用Object的clone,看一下源码
/** * 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 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.*/ protected native Object clone() throws CloneNotSupportedException;
看几个关键点:
x.clone() != x will be true: 两个对象的堆内存地址不相同。也即是在堆内存中存在两个内容相同的该对象。
x.clone().getClass() == x.getClass() will be true: 同属于一个模板类。
x.clone().equals(x)will be true: 这两个对象的内容相同。
protected native Object clone() :native ,该方法的实现由非java语言实现,比如C。说明对象的创建不是调用java的对象构造器,而是直接操作的内存。
看来这才是Object.clone()的真面目。
那么玩个好玩的,看代码:
来个单例的对象
package com.zpj.designMode.prototypePattern; /** * @author PerKins Zhu * @date:2016年9月5日 下午8:37:31 * @version :1.1 * */ public class Person implements Cloneable { // 注意:static private static Person person = new Person("li", 10); private String name; private int age; // 注意:private private Person() { } // 注意:private private Person(String name, int age) { super(); this.name = name; this.age = age; } public static Person getInstance() { return person; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
那么clone一下看看:
package com.zpj.designMode.prototypePattern; import org.junit.Test; /** * @author PerKins Zhu * @date:2016年9月5日 下午8:40:51 * @version :1.1 * */ public class TestUnit { @Test public void test01() throws CloneNotSupportedException{ Person p1= Person.getInstance(); Person p2 =(Person)p1.clone(); System.out.println(p2.getAge()); System.out.println(p2.getName()); System.out.println(p1.equals(p2)); System.out.println(p1); System.out.println(p2); } }
神奇的事情发生了:
一个单例对象变为两个了!!!
因此,代码要想安全,单例对象不能实现 Cloneable接口。
再说所谓的深复制,浅复制:
添加一个Book
package com.zpj.designMode.prototypePattern; /** * @author PerKins Zhu * @date:2016年9月5日 下午9:57:54 * @version :1.1 * */ public class Book { private String name; public Book(String name) { super(); this.name = name; } }
Person 多了一本书:更新person
package com.zpj.designMode.prototypePattern; /** * @author PerKins Zhu * @date:2016年9月5日 下午8:37:31 * @version :1.1 * */ public class Person implements Cloneable { String name; int age; Book book; public Person(String name, int age, Book book) { super(); this.name = name; this.age = age; this.book = book; System.out.println("----带参 构造器Person------"); } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
下面进行测试:
package com.zpj.designMode.prototypePattern; import org.junit.Test; /** * @author PerKins Zhu * @date:2016年9月5日 下午8:40:51 * @version :1.1 * */ public class TestUnit { @Test public void test01() throws CloneNotSupportedException { Book book = new Book("资本论"); Person p1 = new Person("li", 10, book); Person p2 = (Person) p1.clone(); System.out.println(p1.equals(p2)); System.out.println(p1); System.out.println(p2); System.out.println("----------book---"); System.out.println(p1.book.equals(p2.book)); System.out.println(p1.book); System.out.println(p2.book); } }
看看结果:
人不是同一个人,书却是同一本书!!!!
这就是所谓的浅复制:如果被复制对象(person)中包含引用对象(book),则当引用对象没有实现Cloneable接口,实现Object的clone方法时,对被引用对象的复制就是浅复制,即:只复制了该对象的引用,在堆内存中并未实际开辟新的堆内存空间。
这里需要明白什么是引用,见:java堆内存和栈内存的处理,弄明白引用之后,就明白浅复制是怎么玩的了,明白该如何实现深复制。不再详述。
----------------------------------------------------------------------