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 method {@code clone} for class {@code Object} performs a * specific cloning operation. First, if the class of this object does * not implement the interface {@code Cloneable}, then a * {@code CloneNotSupportedException} is thrown. Note that all arrays * are considered to implement the interface {@code Cloneable} and that * the return type of the {@code clone} method of an array type {@code T[]} * is {@code T[]} where T is any reference or primitive type. * 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 {@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. * * @return a clone of this instance. * @exception CloneNotSupportedException if the object's class does not * support the {@code Cloneable} interface. Subclasses * that override the {@code clone} method can also * throw this exception to indicate that an instance cannot * be cloned. * @see java.lang.Cloneable */ protected native Object clone() throws CloneNotSupportedException;
?
其中的修饰符native标明此方法是一个本地方法,即调用了其运行时在平台/操作系统的底层功能。该方法能够创建并返回当前对象的一个副本,可以理解为将当前对象的所有信息(一连串的内存空间中存储的数据)直接复制一份并单独保存,因此其返回的是已经包含了原有对象信息的一个新对象,而不是原有对象的引用。
?
clone()方法在Object类中也被定义为protected的,因此只有在其子类中进行重写才能真正发挥作用,Java语言规定,所有要进行“克隆”的对象所属的类必须实现java.lang.Cloneable接口,这是一个安全性保护。
?
下面看一个具体的例子:
实现简单的克隆操作
Person.java
package org.fool.clone;import org.apache.commons.lang3.builder.ToStringBuilder;import org.apache.commons.lang3.builder.ToStringStyle;public class Person implements Cloneable{private String name;private int age;public Person(String name, int age){this.name = name;this.age = age;}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;}@Overridepublic Object clone(){Person p = null;try{p = (Person) super.clone();}catch (CloneNotSupportedException e){e.printStackTrace();}return p;}@Overridepublic String toString(){return ToStringBuilder.reflectionToString(this,ToStringStyle.SHORT_PREFIX_STYLE);}}
package org.fool.clone;public class TestClone{public static void main(String[] args){Person p1 = new Person("Spring", 18);Person p2 = (Person) p1.clone();System.out.println(p1 == p2);p2.setAge(30);System.out.println(p1);System.out.println(p2);}}
?
?程序运行结果:
falsePerson[name=Spring,age=18]Person[name=Spring,age=30]?
?
这里我们可以查看java.long.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 * @see java.lang.CloneNotSupportedException * @see java.lang.Object#clone() * @since JDK1.0 */public interface Cloneable {}这样的接口被称为空接口,实际上只是起到标记的作用——必须是该接口的实现类的实例才能进行克隆操作,因此这样的接口也称为“标记性接口”。
?
?
需要小心的是,使用上述的clone()方法进行对象拷贝可能出现浅度拷贝(Low Copy)的问题,我们来看一个直观的例子。
Book.java
package org.fool.clone;import org.apache.commons.lang3.builder.ToStringBuilder;import org.apache.commons.lang3.builder.ToStringStyle;public class Book implements Cloneable{private String bookName;private double price;private Person author;public Book(String bookName, double price, Person author){this.bookName = bookName;this.price = price;this.author = author;}public String getBookName(){return bookName;}public void setBookName(String bookName){this.bookName = bookName;}public double getPrice(){return price;}public void setPrice(double price){this.price = price;}public Person getAuthor(){return author;}public void setAuthor(Person author){this.author = author;}@Overridepublic Object clone(){Book b = null;try{b = (Book) super.clone();}catch (CloneNotSupportedException e){e.printStackTrace();}// b.author = (Person) b.getAuthor().clone();return b;}@Overridepublic String toString(){return ToStringBuilder.reflectionToString(this,ToStringStyle.SHORT_PREFIX_STYLE);}}?
?
TestLowCopy.java
package org.fool.clone;public class TestLowCopy{public static void main(String[] args){Book b1 = new Book("Spring", 99.5, new Person("Google", 18));Book b2 = (Book) b1.clone();b2.setBookName("Hibernate");b2.setPrice(89.5);b2.getAuthor().setName("Facebook");b2.getAuthor().setAge(30);System.out.println(b1);System.out.println(b2);}}
?
?
程序运行结果:
Book[bookName=Spring,price=99.5,author=Person[name=Facebook,age=30]]Book[bookName=Hibernate,price=89.5,author=Person[name=Facebook,age=30]]?
?
可以看出克隆后的b2对象对其属性author所引用的Person对象的改动影响到了b1,这是由clone()方法的实现机制决定的——clone()方法先在内存中开辟一块目标对象所需的存储空间(只要是同属于一个类型,则对象占有的存储空间大小也一定相同),然后直接将原始对象存储空间中的内容(包括各属性的值)原样拷贝过来。对基本类型的属性,其属性值就是真正要用到的信息,这样的操作当然没有问题,但对于引用类型的属性,其值只是所引用的其他对象的句柄,这就导致clone后“副本”对象与原始对象引用类型属性指向同样的对象,为了更加直观,读者可以自行画个内存分析图分析一下。
?
这种不够彻底的拷贝也称为浅度拷贝,浅度拷贝可能造成“原件”和“副本”对象之间的“藕断丝连”,往往导致我们所不希望的结果。与之对应的彻底拷贝操作被称为深度拷贝—eep Copy),实现起来也不算困难,只需要在拷贝目标对象时对与其有关联的对象,比如拷贝的Book对象通过其属性author所引用的Person对象,也同时进行显式拷贝处理。
?
这里我们只需要将之前Book.java中代码的注释去掉,就可以实现深度拷贝。
b.author = (Person) b.getAuthor().clone();
@Overridepublic Object clone(){Book b = null;try{b = (Book) super.clone();}catch (CloneNotSupportedException e){e.printStackTrace();}b.author = (Person) b.getAuthor().clone();return b;}?
?
TestDeepCopy.java
package org.fool.clone;public class TestDeepCopy{public static void main(String[] args){Book b1 = new Book("Spring", 99.5, new Person("Google", 18));Book b2 = (Book) b1.clone();b2.setBookName("Hibernate");b2.setPrice(89.5);b2.getAuthor().setName("Facebook");b2.getAuthor().setAge(30);System.out.println(b1);System.out.println(b2);}}
?
程序运行结果:
Book[bookName=Spring,price=99.5,author=Person[name=Google,age=18]]Book[bookName=Hibernate,price=89.5,author=Person[name=Facebook,age=30]]
?
?
?
?
?
?
?
?