Java基础-克隆

        Java中怎么拷贝一个对象呢?可以通过调用这个对象类型的构造器构造一个新对象,然后将要拷贝对象的属性设置到新对象里面。Java中也有另一种不通过构造器来拷贝对象的方式,这种方式称为 克隆
        Java提供了java.lang.Cloneable和java.lang.Object中的clone()方法来支持克隆。Cloneable接口表示一种可以克隆的行为或特性,但这个接口中却没有提供clone方法(Cloneable接口中没有任何方法,看起来更像是一个标记接口)。
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
 * @version %I%, %G%
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable { 
}

        相反在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 <tt>x</tt>, 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 <tt>true</tt>, but these are not absolute requirements. 
     * While it is typically the case that:
     * <blockquote>
     * <pre>
     * x.clone().equals(x)</pre></blockquote>
     * will be <tt>true</tt>, this is not an absolute requirement. 
     * <p>
     * By convention, the returned object should be obtained by calling
     * <tt>super.clone</tt>.  If a class and all of its superclasses (except
     * <tt>Object</tt>) obey this convention, it will be the case that
     * <tt>x.clone().getClass() == x.getClass()</tt>.
     * <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 <tt>super.clone</tt> 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 <tt>super.clone</tt>
     * need to be modified.
     * <p>
     * The method <tt>clone</tt> for class <tt>Object</tt> performs a 
     * specific cloning operation. First, if the class of this object does 
     * not implement the interface <tt>Cloneable</tt>, then a 
     * <tt>CloneNotSupportedException</tt> is thrown. Note that all arrays 
     * are considered to implement the interface <tt>Cloneable</tt>. 
     * 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 <tt>Object</tt> does not itself implement the interface 
     * <tt>Cloneable</tt>, so calling the <tt>clone</tt> method on an object 
     * whose class is <tt>Object</tt> will result in throwing an
     * exception at run time.
     *
     * @return     a clone of this instance.
     * @exception  CloneNotSupportedException  if the object's class does not
     *               support the <code>Cloneable</code> interface. Subclasses
     *               that override the <code>clone</code> method can also
     *               throw this exception to indicate that an instance cannot
     *               be cloned.
     * @see java.lang.Cloneable
     */
    protected native Object clone() throws CloneNotSupportedException;

        Object中的clone方法为受保护方法。既然所有的对象都存在一个clone方法,那是不是都可以调用(可通过反射)这个方法来得到克隆对象呢?clone方法有如下规定: 如果一个对象没有实现Cloneable接口,调用它的clone方法会抛出CloneNotSupportedException异常。但数组例外,可以认为数组实现了Cloneable,所以可以直接调用其clone方法获取克隆对象。

        我们开发中应该怎样使用克隆呢?一般认为,如果希望一个类拥有克隆的行为,要做一下几步:
        ①该类实现java.lang.Cloneable接口。
        ②覆盖Object的clone方法,将访问修饰符改为public,修改返回类型。
        ③clone方法内部调用父类的clone方法。

        示例如下:
public class CloneableObj implements Cloneable{

	//...
	
	@Override
	public CloneableObj clone(){
		try {
			return (CloneableObj) super.clone();
		} catch (CloneNotSupportedException e) {
			throw new AssertionError();
		}
	}
	
}


        按照Object克隆方法描述中的规范,克隆得到对象应该独立于被克隆对象,从根本上说,它们的内存地址(不严格的说)应该不同。但是如果有这种情况,一个可克隆类中包含其他的引用类型属性,那么克隆后会是什么情况呢?举个例子说明一下:
public class MyObj implements Cloneable{

	private MyPro myPro;

	public MyPro getMyPro() {
		return myPro;
	}

	public void setMyPro(MyPro myPro) {
		this.myPro = myPro;
	}

	@Override
	public MyObj clone(){
		try {
			return (MyObj) super.clone();
		} catch (CloneNotSupportedException e) {
			throw new AssertionError();
		}
	}
	
}

public class MyPro {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

public class Test {

	public static void main(String[] args) {
		MyObj myObj = new MyObj();
		MyPro myPro = new MyPro();
		myPro.setName("aa");
		myObj.setMyPro(myPro);
		
		MyObj clone = myObj.clone();
		//myObj和clone是否指向相同的内存地址
		System.out.println(clone == myObj);
		//myObj的myPro和clone的myPro是否指向相同的内存地址
		System.out.println(clone.getMyPro() == myObj.getMyPro());
	}
	
}

        运行结果:
false
true

        可见,虽然克隆对象与被克隆对象内存地址不同,但他们的引用类型属性却指向相同的内存地址。这便涉及到了 浅克隆深克隆的问题。
        上面的情况就是浅克隆,可见clone方法默认的行为就是浅克隆。那么也很容易想到,如果克隆一个对象,并递归的克隆其所有的引用类型属性,这样的方式就是深克隆了。
        对于一个复杂的类,如果需要提供克隆方法的话,一般会先调用父类的clone方法,然后将相应的域(属性)设置为相应的值(有些域需要保存状态,有些域需要清空。)或者将域清空然后调用内部方法来初始化状态,并且要在方法注释上说明该方法的行为是深克隆还是浅克隆。如HashMap的克隆方法:
    /**
     * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
     * values themselves are not cloned.
     *
     * @return a shallow copy of this map
     */
    public Object clone() {
        HashMap<K,V> result = null;
	try {
	    result = (HashMap<K,V>)super.clone();
	} catch (CloneNotSupportedException e) {
	    // assert false;
	}
        result.table = new Entry[table.length];
        result.entrySet = null;
        result.modCount = 0;
        result.size = 0;
        result.init();
        result.putAllForCreate(this);

        return result;
    }


       

你可能感兴趣的:(java基础)