`

clone() 方法

    博客分类:
  • Java
 
阅读更多

在应用开发过程中,我们可能会需要拷贝(copy)一个现有的对象,即得到一个新对象并希望其与现有对象封装完全相同的信息(属性值),主要是为了此后两者互不相干,修改其中的一个对象不会影响到另外一个。简单地引用变量间的赋值是不能解决问题的,因为并没有创建新对象;而自己编写代码先创建一个新对象,再将原始对象的属性一一复制过来也比较繁琐,且存在“浅度拷贝”问题;这种情况下,利用clone() 方法来实现对象拷贝不失为一种明智的选择。

 

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 {@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;
	}

	@Override
	public Object clone()
	{
		Person p = null;

		try
		{
			p = (Person) super.clone();
		}
		catch (CloneNotSupportedException e)
		{
			e.printStackTrace();
		}

		return p;
	}

	@Override
	public String toString()
	{

		return ToStringBuilder.reflectionToString(this,
				ToStringStyle.SHORT_PREFIX_STYLE);
	}
}

 

TestClone.java

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);
	}
}

 

 程序运行结果:

false
Person[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;
	}

	@Override
	public Object clone()
	{
		Book b = null;

		try
		{
			b = (Book) super.clone();
		}
		catch (CloneNotSupportedException e)
		{
			e.printStackTrace();
		}

		// b.author = (Person) b.getAuthor().clone();

		return b;
	}

	@Override
	public 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后“副本”对象与原始对象引用类型属性指向同样的对象,为了更加直观,读者可以自行画个内存分析图分析一下。

 

这种不够彻底的拷贝也称为浅度拷贝,浅度拷贝可能造成“原件”和“副本”对象之间的“藕断丝连”,往往导致我们所不希望的结果。与之对应的彻底拷贝操作被称为深度拷贝(Deep Copy),实现起来也不算困难,只需要在拷贝目标对象时对与其有关联的对象,比如拷贝的Book对象通过其属性author所引用的Person对象,也同时进行显式拷贝处理。

 

这里我们只需要将之前Book.java中代码的注释去掉,就可以实现深度拷贝。

b.author = (Person) b.getAuthor().clone();
@Override
public 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]]

 

 
 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    Java中clone方法共6页.pdf.zip

    本资料"Java中clone方法共6页.pdf.zip"可能包含了关于如何理解和使用`clone()`方法的详细解释,以及它在实际开发中的应用示例。 `clone()`方法的主要用途是创建一个现有对象的副本,这个副本与原始对象具有相同的...

    Java clone方法使用

    详细的描述了Java中 clone方法使用

    Java中的clone方法详解_动力节点Java学院

    Java中的clone方法详解_动力节点Java学院,动力节点口口相传的Java黄埔军校

    bigcoder84#study-notes#_4Object中的clone方法1

    Object类中的clone方法clone()方法的作用克隆方法用于创建对象的拷贝,为了使用clone方法,类必须实现java.lang.Cloneable接口

    Visual studio 2015 标准课程第五章 数组数组的Clone方法

    Visual studio 2015 标准课程第五章 数组数组的Clone方法和数组的CopyTo方法

    jquery的clone方法应用于textarea和select的bug修复

    具体来说,重写的clone方法会首先调用原始的clone方法来创建元素的副本,然后寻找当前元素集中所有的textarea元素,并将其与结果集中找到的所有textarea元素配对。之后,对于每对textarea元素,新元素会获取旧元素的...

    Java中的clone方法详解_动力节点Java学院整理

    Java中的clone方法详解 在Java语言中,clone方法是一个非常重要的概念,它允许对象被复制,从而创造出一个新的对象。下面我们将详细介绍Java中的clone方法,并讨论它的实现机制和应用场景。 什么是clone方法 ...

    Java的clone方法[归类].pdf

    Java的`clone()`方法在软件开发中扮演着重要的角色,特别是在需要复制对象的场景下。在Java中,对象的复制并非像C++等语言中的指针复制那样简单,因为Java中没有指针的概念,而是使用引用。这导致了在默认情况下,...

    JAVA对象clone方法代码实例解析

    JAVA对象clone方法代码实例解析 JAVA对象clone方法是JAVA编程语言中的一种复制对象的方法,通过实现Cloneable接口和重写clone方法,可以实现对象的浅复制或深复制。在本文中,我们将通过示例代码详细介绍JAVA对象...

    JQuery中clone方法复制节点

    JQuery库中的clone方法是其强大的功能之一,它允许开发者复制文档中的元素节点,进而可以将这些副本插入到DOM的其他位置或进行其他操作。在本知识点中,我们将详细介绍JQuery中clone方法复制节点的使用方法,重点...

    DataTable类Clone方法与Copy方法的区别分析

    DataTable.Clone 方法:克隆 DataTable 的结构,包括所有 DataTable 架构和约束。 DataTable.Copy 方法:复制该 DataTable 的结构和数据。 我们可以编写如下的程序,进行验证: 代码如下: static string connStr ...

    C#中Clone一个对象的值到另一个对象案例 c#经典案例.pdf

    在这篇文章中,我们将学习如何使用 C# 中的 Clone 方法来复制一个对象的值到另一个对象。 什么是 Clone? Clone 是一种复制对象的方法,它可以将一个对象的值复制到另一个对象中。 Clone 方法可以分为浅复制和深...

    Java 数组复制clone方法实现详解

    本文将深入讲解如何使用`clone()`方法来实现数组复制,并通过实例代码分析其工作原理和注意事项。 首先,`clone()`方法是Java中的一个内置功能,它允许我们创建一个对象的浅拷贝。对于基本类型的数组,`clone()`会...

    数组Clear,Clone方法.zip

    免费资源,让你玩转数组的属性,它将不会是你的障碍,如果有疑难问题加我QQ 912019136

    Java clone方法详解及简单实例

    Java中的`clone`方法是Java语言提供的一种复制对象的机制,它允许创建一个现有对象的副本,这个副本具有与原始对象相同的状态,但它们是独立的实体,对其中一个对象的修改不会影响另一个。`clone`方法是Java `Object...

    java object 之clone方法全面解析

    Java中的`clone()`方法是Object类的一个成员方法,它提供了创建一个对象副本的功能。这篇文章将深入探讨`clone()`方法的工作原理、使用场景以及浅拷贝(shallow clone)与深拷贝(deep clone)的区别。 首先,让...

Global site tag (gtag.js) - Google Analytics