本篇主要就java语言对原型模式扩展探讨理解,但是接触java的时间有限,所以不能保证没有谬误。主要内容就如下几个方面展开:
【java创建对象的方式】
java创建对象,最先我们肯定接触的是用new进行构建。之后我们会接触反射(反射有俩种方式直接使用class类和使用Constructor类创建),之后使用原型模式,肯定会了解到clone方式,之后因为clone存在浅复制的问题,所以了解到serializable方式创建。
1,new Object()。这种方式没有太多可说的
2,利用反射创建对象1 public classCreateT {2 //Class类创建 3 public static
3,利用clone进行创建。因为Object对象有clone的方法,该方法能够创建对象,所以可以调用clone方法进行创建。
//Object中clone方法是protect类型,只有重载成public之后才能在类外使用,这处设计的确实挺精妙的。
4,使用序列化,反序列化创建对象(后面介绍)
【传统的原型方式-clone】
Object类为什么有一个protect类型的clone方法,这就是java当初设计的时候,为原型模式保留的,这我们就管它叫传统的原型模式。public class Test3 implementsCloneable { @Overrideprotected Object clone() throwsCloneNotSupportedException {return super.clone(); }}
1 public interfaceCloneable {2 }
上面代码是一个标准的clone方法的标准实现,我们发现该方法可能会抛出异常,而且要实现空白接口Cloneable
那为什么要实现这个接口?什么情况下会抛出异常呢?下面我们看个例子1 //实现Cloneable并且调用super.clone() 2 public class Test6 implementsCloneable {3 @Override4 protected Object clone() throwsCloneNotSupportedException {5 return super.clone();6 }7 }8 //未实现Cloneable未调用super.clone() 9 public classTest5{10 @Override11 public Object clone() throwsCloneNotSupportedException {12 return newTest5();13 }14 }15 //未实现Cloneable并且调用super.clone() 16 public classTest4{17 @Override18 public Object clone() throwsCloneNotSupportedException {19 return super.clone();20 }21 }22 //测试类 23 public static voidmain(String[] args) {24 try{25 Test6 test6=newTest6();26 Test6 test6clone=(Test6) test6.clone();27 System.out.println(test6clone.getClass());28 Test5 test5=newTest5();29 Test5 test5clone=(Test5) test5.clone();30 System.out.println(test5clone.getClass());31 Test4 test4=newTest4();32 Test4 test4clone=(Test4) test4.clone();33 System.out.println(test4clone.getClass());34 }catch(Exception ex){35 System.out.println(ex.getClass());36 }37 }
运行结果:
通过运行结果我们可以得到一个结论:如果调用super.clone方法,必须实现空接口Cloneable,否则会抛出异常。那我们实现的时候,像Test5一样,使用构造函数构造,不调用super.clone()是不是就可以了。原则上应该没问题,不过还有一些细节的地方是不一致的,下面继续看测试代码:1 //直接new 2 public classTest5{3 @Override4 protected Object clone() throwsCloneNotSupportedException {5 return newTest5();6 }7 publicTest5(){8 System.out.println("Test5构造函数");9 }10 private static int index=0;11 public intgetIindex() {12 returniindex;13 }14 private int iindex=++index;15 @Override16 publicString toString() {17 returnString.valueOf(iindex);18 }19 }20 //使用super.clone() 21 public class Test6 implementsCloneable {22 @Override23 protected Object clone() throwsCloneNotSupportedException {24 return super.clone();25 }26 27 publicTest6(){28 System.out.println("Test6构造函数");29 }30 private static int index=0;31 public intgetIindex() {32 returniindex;33 }34 private int iindex=++index;35 @Override36 publicString toString() {37 returnString.valueOf(iindex);38 }39 }40 //测试代码 41 public static voidmain(String[] args) {42 try{43 Test6 test6=newTest6();44 Test6 test6clone=(Test6) test6.clone();45 System.out.println("test6:"+test6.toString());46 System.out.println("test6clone:"+test6clone.toString());47 Test5 test5=newTest5();48 Test5 test5clone=(Test5) test5.clone();49 System.out.println("test5:"+test5.toString());50 System.out.println("test5clone:"+test5clone.toString());51 }catch(Exception ex){52 System.out.println(ex.getClass());53 }54 }
输出结果:
我们明显可以看出:super.clone是不走构造函数,并且私有变量也能复制;而直接使用new创建,会走构造函数,并且内部私有变量也可能会有变化。我们之前说创建对象的几种方式有clone,而没把他算到new Object中,因为clone压根就不会走构造函数!!!!
我们看下Object类的clone方法签名:1 protected native Object clone() throws CloneNotSupportedException;
我们发现这有一个native的关键字,之前重来没见过的,百度之后才知道,native关键字代表与C语言(其他语言)交互,也就是说Object类的clone方法是用底层实现的,当然效率也会更高些。
【深复制与浅复制】
下面我们再看一个例子:1 public classTest7 {2 }3 public class Test8 implementsCloneable {4 publicTest7 getTest7() {5 returntest7;6 }7 public voidsetTest7(Test7 test7) {8 this.test7 =test7;9 }10 Test7 test7;11 @Override12 protected Object clone() throwsCloneNotSupportedException {13 return super.clone();14 }15 }16 public static voidmain(String[] args) {17 try{18 Test7 test7=newTest7();19 Test8 test8=newTest8();20 Test8 test8Copy=(Test8) test8.clone();21 System.out.println("test8==test8Copy:"+(test8==test8Copy));22 System.out.println("test8.getTest7==test8Copy.getTest7:"+(test8.getTest7()==test8Copy.getTest7()));23 }catch(Exception ex){24 System.out.println(ex.getClass());25 }26 }
运行结果:
重结果中我们可以看出test8和test8Copy对象不是同一个对象,但是test8和test8Copy内部引用的Test7对象是同一个对象,也就是说test8中的Test7被修改,test8Copy中的Test7也会被修改,这几乎就不是我们想要的。我们管这种复制叫浅复制。如果对象内所有属性都被复制我们叫做深复制。
【序列化】
如果我们想彻底深复制,可以使用序列化的方式,示例代码如下://需要实现Serializable public class Test7 implementsSerializable { }//需要实现Serializable public class Test8 implementsSerializable {publicTest7 getTest7() {returntest7; }public voidsetTest7(Test7 test7) {this.test7 =test7; } Test7 test7; @Overrideprotected Object clone() throwsCloneNotSupportedException {return super.clone(); } }//序列化帮助代码 public classCommonClone {public static
运行结果:
我们可以看见属性Test7也是不同的,实现了深复制。需要注意的是序列化也不会调用类的构造函数,可能与Object的clone方法机制类似,但不太明确
由于序列化和反序列化也是一个比较大的话题,这里就不展开讨论。这里只简单的介绍下,被序列化的对象以及其属性都要实现接口Serializable(和Cloneable一样,也不包含方法),如果不实现会抛出异常。
【总结】