zoukankan      html  css  js  c++  java
  • Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

    Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨

    Part I

    没啥好说的,直接开始Part II吧。

    Part II

    谈到了对象的克隆,就不得不说为什么要对对象进行克隆。Java中所有的对象都是保存在堆中,而堆是供全局共享的。也就是说,如果同一个Java程序的不同方法,只要能拿到某个对象的引用,引用者就可以随意的修改对象的内部数据(前提是这个对象的内部数据通过get/set方法曝露出来)。有的时候,我们编写的代码想让调用者只获得该对象的一个拷贝(也就是一个内容完全相同的对象,但是在内存中存在两个这样的对象),有什么办法可以做到呢?当然是克隆咯。

    Part III

    首先,我们是程序员,当然是用我们程序员的语言来交流。

    import java.util.Date;
    
    
    
    public class User implements Cloneable {
    
    
    
    	private String username;
    
    	private String password;
    
    	private Date birthdate;
    
    
    
    	public User(String username, String password, Date birthdate) {
    
    		this.username = username;
    
    		this.password = password;
    
    		this.birthdate = birthdate;
    
    	}
    
    
    
    	@Override
    
    	protected Object clone() throws CloneNotSupportedException {
    
    		return super.clone();
    
    	}
    
    
    
    	@Override
    
    	public int hashCode() {
    
    		// 省略equals的实现(可用eclipse自动生成)
    
    	}
    
    
    
    	@Override
    
    	public boolean equals(Object obj) {
    
    		// 省略equals的实现(可用eclipse自动生成)
    
    	}
    
    
    
    	// 省略一大堆get/set方法
    
    
    
    }
    

    上述代码构建了一个User类,并且实现了java.lang.Cloneable接口。顾名思义,Cloneable的意思就是说明这个类可以被克隆的意思。

    而我们先去看看java.lang.Cloneable这个接口有些什么。

    /*
     * @(#)Cloneable.java	1.17 05/11/17
     *
     * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
     * SUN 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
     * @version 1.17, 11/17/05
     * @see     java.lang.CloneNotSupportedException
     * @see     java.lang.Object#clone()
     * @since   JDK1.0
     */
    public interface Cloneable { 
    }

    不要惊讶,没错,除了一大堆的鸡肠以外,这个接口没有定义任何的方法签名。也就是说,我们要克隆一个对象,但是他又不给我提供一个方法。那该怎么办呢?不怕,我们还有全能的Object类,别忘记他可是所有类的始祖啊(神一般的存在着),所以,有事没事都该去问候一下他老人家。

    /*
     * @(#)Object.java	1.73 06/03/30
     *
     * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
     * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     */
    
    
    package java.lang;
    
    
    /**
     * Class <code>Object</code> is the root of the class hierarchy. 
     * Every class has <code>Object</code> as a superclass. All objects, 
     * including arrays, implement the methods of this class. 
     *
     * @author  unascribed
     * @version 1.73, 03/30/06
     * @see     java.lang.Class
     * @since   JDK1.0
     */
    public class Object {
    
        
    
       // 省略N多的代码
    
    
    
        /**
    
         * 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;
    
    
    
    }

    呵呵,又是一大串的鸡肠,别以为我是来凑字数的,这些都是Sun公司Java开发人员写的技术文章,多看看少说话吧。

    没错,又是个native方法,果然是个高深的东西,不过我们还是要占一下他的便宜。而且他这个方法是protected的,分明就是叫我们去占便宜的。

    再继续看看下面测试代码。

    import java.util.Date;
    
    
    
    import org.junit.Test;
    
    
    
    
    
    public class TestCase {
    
    	
    
    	@Test
    
    	public void testUserClone() throws CloneNotSupportedException {
    
    		User u1 = new User("Kent", "123456", new Date());
    
    		User u2 = u1;
    
    		User u3 = (User) u1.clone();
    
    		
    
    		System.out.println(u1 == u2);		// true
    
    		System.out.println(u1.equals(u2));	// true
    
    		
    
    		System.out.println(u1 == u3);		// false
    
    		System.out.println(u1.equals(u3));	// true
    
    	}
    
    	
    
    }

    这个clone()方法果然牛,一下子就把我们的对象克隆了一份,执行结果也符合我们的预期,u1和u3的地址不同但是内容相同。

    Part IV

    通过上述的例子,我们可以看出,要让一个对象进行克隆,其实就是两个步骤:

    1. 让该类实现java.lang.Cloneable接口;

    2. 重写(override)Object类的clone()方法。

    但是,事实上真的是如此简单吗?再看下面的代码。

    public class Administrator implements Cloneable {
    
    
    
    	private User user;
    
    	private Boolean editable;
    
    
    
    	public Administrator(User user, Boolean editable) {
    
    		this.user = user;
    
    		this.editable = editable;
    
    	}
    
    	
    
    	@Override
    
    	protected Object clone() throws CloneNotSupportedException {
    
    		return super.clone();
    
    	}
    
    
    
    	@Override
    
    	public int hashCode() {
    
    		// 老规矩
    
    	}
    
    
    
    	@Override
    
    	public boolean equals(Object obj) {
    
    		// 老规矩
    
    	}
    
    
    
    	// 老规矩
    
    
    
    }

    上面定义了一个Administrator类,这个类持有一个User类的对象。接下来我们看看对Administrator对象进行克隆会有什么效果。

    import java.util.Date;
    
    
    
    import org.junit.Test;
    
    
    
    
    
    public class TestCase {
    
    
    
    	@Test
    
    	public void testAdministratorClone() throws CloneNotSupportedException {
    
    		Administrator a1 = new Administrator(new User("Kent", "123456", new Date()), true);
    
    		Administrator a2 = a1;
    
    		Administrator a3 = (Administrator) a1.clone();
    
    		
    
    		System.out.println(a1 == a2);			// true
    
    		System.out.println(a1.equals(a2));		// true
    
    		
    
    		System.out.println(a1 == a3);			// false
    
    		System.out.println(a1.equals(a3));		// true
    
    		
    
    		System.out.println(a1.getUser() == a3.getUser());		//true ! It's not our expected!!!!!
    
    		System.out.println(a1.getUser().equals(a3.getUser()));	//true
    
    	}
    
    }

    呵呵呵!出问题了吧。Java哪是那么容易就能驾驭的说!

    这里我们就可以引入两个专业的术语:浅克隆(shallow clone)和深克隆(deep clone)。

    所谓的浅克隆,顾名思义就是很表面的很表层的克隆,如果我们要克隆Administrator对象,只克隆他自身以及他包含的所有对象的引用地址

    而深克隆,就是非浅克隆。克隆除自身以外所有的对象,包括自身所包含的所有对象实例。至于深克隆的层次,由具体的需求决定,也有“N层克隆”一说。

    但是,所有的基本(primitive)类型数据,无论是浅克隆还是深克隆,都会进行原值克隆。毕竟他们都不是对象,不是存储在堆中。注意:基本数据类型并不包括他们对应的包装类。

    如果我们想让对象进行深度克隆,我们可以这样修改Administrator类。

    @Override
    
    protected Object clone() throws CloneNotSupportedException {
    
    	Administrator admin = (Administrator) super.clone();
    
    	admin.user = (User) admin.user.clone();
    
    	return admin;
    
    }

    由于Boolean会对值进行缓存处理,所以我们没必要对Boolean的对象进行克隆。并且Boolean类也没有实现java.lang.Cloneable接口。

    Part V

    1. 让该类实现java.lang.Cloneable接口;

    2. 确认持有的对象是否实现java.lang.Cloneable接口并提供clone()方法;

    3. 重写(override)Object类的clone()方法,并且在方法内部调用持有对象的clone()方法;

    4. ……

    5. 多麻烦啊,调来调去的,如果有N多个持有的对象,那就要写N多的方法,突然改变了类的结构,还要重新修改clone()方法。

    难道就没有更好的办法吗?

    Part VI

    接下来要重点介绍一下使用java.lang.Serializable来实现对象的深度克隆。

    首先,我们编写一个工具类并提供cloneTo()方法。

    import java.io.ByteArrayInputStream;
    
    import java.io.ByteArrayOutputStream;
    
    import java.io.IOException;
    
    import java.io.ObjectInputStream;
    
    import java.io.ObjectOutputStream;
    
    
    
    public abstract class BeanUtil {
    
    
    
    	@SuppressWarnings("unchecked")
    
    	public static <T> T cloneTo(T src) throws RuntimeException {
    
    		ByteArrayOutputStream memoryBuffer = new ByteArrayOutputStream();
    
    		ObjectOutputStream out = null;
    
    		ObjectInputStream in = null;
    
    		T dist = null;
    
    
    
    		try {
    
    			out = new ObjectOutputStream(memoryBuffer);
    
    			out.writeObject(src);
    
    			out.flush();
    
    			in = new ObjectInputStream(new ByteArrayInputStream(memoryBuffer.toByteArray()));
    
    			dist = (T) in.readObject();
    
    
    
    		} catch (Exception e) {
    
    			throw new RuntimeException(e);
    
    		} finally {
    
    			if (out != null)
    
    				try {
    
    					out.close();
    
    					out = null;
    
    				} catch (IOException e) {
    
    					throw new RuntimeException(e);
    
    				}
    
    			if (in != null)
    
    				try {
    
    					in.close();
    
    					in = null;
    
    				} catch (IOException e) {
    
    					throw new RuntimeException(e);
    
    				}
    
    		}
    
    
    
    		return dist;
    
    	}
    
    
    
    }

    看不懂,没关系,直接拿去用就可以了。嘻嘻。

    接下来我们测试一下是否能通过这个工具来实现深度克隆。

    又是这个可爱的TestCase,可怜的每次都要动他……

    import java.util.Date;
    
    
    
    import org.junit.Test;
    
    
    
    
    
    public class TestCase {
    
    	
    
    	@Test
    
    	public void testCloneTo() {
    
    		Administrator src = new Administrator(new User("Kent", "123456", new Date()), true);
    
    		Administrator dist = BeanUtil.cloneTo(src);
    
    		
    
    		System.out.println(src == dist);			// false
    
    		System.out.println(src.equals(dist));		// true
    
    		
    
    		System.out.println(src.getUser() == dist.getUser());		//false ! Well done!
    
    		System.out.println(src.getUser().equals(dist.getUser()));	//true
    
    	}
    
    	
    
    }

    好了,无论你的对象有多么的复杂,只要这些对象都能够实现java.lang.Serializable接口,就可以进行克隆,而且这种克隆的机制是JVM完成的,不需要修改实体类的代码,方便多了。

    为什么这么简单就可以实现对象的克隆呢?java.lang.Serializable接口又是干嘛用的呢?如果想知道这些问题的答案,

  • 相关阅读:
    BestCoder17 1001.Chessboard(hdu 5100) 解题报告
    codeforces 485A.Factory 解题报告
    codeforces 485B Valuable Resources 解题报告
    BestCoder16 1002.Revenge of LIS II(hdu 5087) 解题报告
    codeforces 374A Inna and Pink Pony 解题报告
    codeforces 483B Friends and Presents 解题报告
    BestCoder15 1002.Instruction(hdu 5083) 解题报告
    codeforces 483C.Diverse Permutation 解题报告
    codeforces 483A. Counterexample 解题报告
    NSArray中地内存管理 理解
  • 原文地址:https://www.cnblogs.com/handsome1013/p/5359281.html
Copyright © 2011-2022 走看看