原型模式
原型模式:
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
java中的克隆技术,以某个对象为原型,复制出新的对象。新的对象具有原型对象的特性。
优势有:效率高(直接克隆、避免了重新执行构造过程步骤)
克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性
值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象(深克隆不会,浅克隆可能会)。然后,在修改克隆对象的值。
原型模式实现:
Cloneable接口的clone方法
Prototype模式中实现起来最困难的地方就是内存的复制操作,所幸在Java中为我们提供了clone()方法
替我们做了绝大部分事情。
java提供了对于克隆的支持,想要实现克隆,必须实现Cloneable接口,该接口是个空接口,里面没有方法,此外还需要重写Object类的本地clone()方法。Cloneable接口的代码如下:
/* * Copyright (c) 1995, 2004, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package java.lang; /** * A class implements the <code>Cloneable</code> interface to * indicate to the {@link java.lang.Object#clone()} method that it * is legal for that method to make a * field-for-field copy of instances of that class. * <p> * Invoking Object's clone method on an instance that does not implement the * <code>Cloneable</code> interface results in the exception * <code>CloneNotSupportedException</code> being thrown. * <p> * By convention, classes that implement this interface should override * <tt>Object.clone</tt> (which is protected) with a public method. * See {@link java.lang.Object#clone()} for details on overriding this * method. * <p> * Note that this interface does <i>not</i> contain the <tt>clone</tt> method. * Therefore, it is not possible to clone an object merely by virtue of the * fact that it implements this interface. Even if the clone method is invoked * reflectively, there is no guarantee that it will succeed. * * @author unascribed * @see java.lang.CloneNotSupportedException * @see java.lang.Object#clone() * @since JDK1.0 */ public interface Cloneable { }
一个克隆的实现:
package com.oj; import java.util.Date; /** * @author We.lxk * */ public class Sheep implements Cloneable{ private String sname; private Date date; public Sheep() { // TODO Auto-generated constructor stub } public Sheep(String sname, Date date) { super(); this.sname = sname; this.date = date; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); //直接调用object对象的clone()方法 return obj; } }
测试代码:
package com.oj; import java.util.Date; public class Client { public static void main(String[] args) throws Exception { Date d = new Date(123123123123123L); Sheep s = new Sheep("少利",d); System.out.println(s); Sheep s2 = (Sheep) s.clone(); System.out.println(s2); System.out.println(s==s2); Sheep s3 = s; System.out.println(s==s3); s2.setSname("呵呵"); System.out.println("s: "+s.getSname()+" , "+s.getDate()+" s2: "+s2.getSname()+" , "+s2.getDate()); d.setTime(123123123123L); System.out.println("s: "+s.getSname()+" , "+s.getDate()+" s2: "+s2.getSname()+" , "+s2.getDate()); } }
测试输出:
com.oj.Sheep@16bd8ea
com.oj.Sheep@16e1fb1
false
true
s: 少利 , Mon Aug 14 11:32:03 CST 5871 s2: 呵呵 , Mon Aug 14 11:32:03 CST 5871
s: 少利 , Mon Nov 26 08:52:03 CST 1973 s2: 呵呵 , Mon Nov 26 08:52:03 CST 1973
通过输出可以发现,这个实现是浅克隆,s2克隆了s,克隆时只是把s中属性的值(引用类型,指向了一个地址)在堆中另外复制了一份,即把地址复制了一份,所以这个时候s和s2指向了堆中不同的位置,运用==比较时才会false,因为地址不一样了,这个地址所存储的值并没有复制,其中的Date d;仍然等于说是两个对象共用了一份,因此当你直接改变d的值得时候表现为s和s2两个输出的日期同时发生了变化,当你采用s.setDate(new Date(123L))时,这是在堆中新建立了一个Date对象,相当于同d已经脱离了,这是d已经只有s2中的date指向,s中的date指向了这个在堆中新建立的对象了,这时两者才算是脱离了;把s2直接赋值给s3时,两个地址引用指向了同一个堆中对象的位置,这个比较是相等的。
下面进行深克隆
package com.oj; import java.util.Date; /** * @author We.lxk * */ public class Sheep2 implements Cloneable{ private String sname; private Date date; public Sheep2() { // TODO Auto-generated constructor stub } public Sheep2(String sname, Date date) { super(); this.sname = sname; this.date = date; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } @Override protected Object clone() throws CloneNotSupportedException { Object obj = super.clone(); //直接调用object对象的clone()方法 Sheep2 s = (Sheep2) obj; s.date = (Date) this.date.clone(); return obj; } }
测试
package com.oj; import java.util.Date; public class Client2 { public static void main(String[] args) throws Exception { Date d = new Date(123123123123123L); Sheep2 s = new Sheep2("少利",d); System.out.println(s); Sheep2 s2 = (Sheep2) s.clone(); System.out.println(s2); System.out.println(s==s2); Sheep2 s3 = s; System.out.println(s==s3); s2.setSname("呵呵"); System.out.println("s: "+s.getSname()+" , "+s.getDate()+" s2: "+s2.getSname()+" , "+s2.getDate()); d.setTime(123123L); System.out.println("s: "+s.getSname()+" , "+s.getDate()+" s2: "+s2.getSname()+" , "+s2.getDate()); s2.setDate(new Date(123123123123L)); System.out.println("s: "+s.getSname()+" , "+s.getDate()+" s2: "+s2.getSname()+" , "+s2.getDate()); } }
输出:
com.oj.Sheep2@16bd8ea
com.oj.Sheep2@16e1fb1
false
true
s: 少利 , Mon Aug 14 11:32:03 CST 5871 s2: 呵呵 , Mon Aug 14 11:32:03 CST 5871
s: 少利 , Thu Jan 01 08:02:03 CST 1970 s2: 呵呵 , Mon Aug 14 11:32:03 CST 5871
s: 少利 , Thu Jan 01 08:02:03 CST 1970 s2: 呵呵 , Mon Nov 26 08:52:03 CST 1973
此时,已经完全将s里的date和s2的date完全分离了,因为在克隆代码里连同date对象一同进行了克隆。输出请遵循上述方法分析理解。
端: