clone()是java.lang.Object类下面的一个很难处理的方法,clone()的作用很简单,提供一种克隆机制创建对象的副本,对于如何实现它已成为一个棘手的事,同时还长期被广受批评。不管怎样,我们不去争论历史,现在我们将尝试学习clone方法是怎样工作的。说实在的,想理解克隆机制并不简单,甚至有经验的java程序员也很难解释可变对象的克隆是如何运作的、深克隆(deep copy)与浅克隆(shallow copy)的区别。这文章分为三部分,我们首先看clone方法是如何工作的,第二部分将学习如何重写(override)clone方法,最后我们讨论深度克隆与浅克隆。之所以选择把它作为三部分,是为了一次专注在一件事上,clone()本身就很让人困惑的,因此最好是一个一个理解概念。在这篇文章中将学习到什么是clone方法,以及怎么工作的,顺便说一句,clone是定义一个Object类下基本方法之一,与之类似的还有hashcode(),toString(),wait和notify。 什么是克隆对象 clone()方法返回的对象叫做原始对象的克隆体。一个克隆对象的基本特性必须是:a.clone()!=a,这也就意味着克隆对象和原始对象在java 堆(heap)中是两个独立的对象。a.clone().getClass == a.getClass() 以及 clone.equals(a),也就是说克隆对象完完全全是原始对象的一个拷贝。这些特征来自于一种良好的行为—正确地重写(overriedden)clone方法。但是这些并不是克隆机制强制要求的。意味着clone()返回的对象可能会违反这些约定(通过调用super.clone()方法返回的对象),当重写clone()方法时,你可以遵循前面两条(a.clone()!=a和a.clone().getClass()==a.getClass())。为了遵循第三个特性(clone.equals(a)),你必须重写equals方法。例如:Rectangle类的克隆对象就有这三个特性,但是如果你注释equals()再来运行相同的程序,你将看到第三个约束条件clone.equlas(a)返回false,顺便说一下,在《Effective Java》中有条目提到如何有效使用clone方法的知识点,我强烈建议你在读完本文后去翻一翻这本书。 clone方法是如何工作的 java.lang.Object提供了默认的clone方法实现,它声明为protected和native。因此它的实现是取决于本地代码,因为它约定返回对象是通过调用super.clone()方法,任何克隆的过程最终都将到达java.lang.Object 的clone()方法,它首先检查这个相关的类是否实现了Cloneable接口,这个接口是一个标记接口,如果这个实例没有实现cloneable接口,那么就会抛出CloneNotSupported异常,这个异常是一个checked异常,也就是说他在克隆对象的时候总需要被处理。如果没有异常抛出,然后java.lang.Object的clone()方法将创建一个拷贝返回给调用者。因为Object类的clone()方法通过创建新对象来生成这个副本的,然后逐个域拷贝(field-by-filed),类似于赋值操作,这种操作对于原始类型(primitives)和不可变类型(immutable)来说是没问题的,但是如果你的类包含一些可变的数据结构如:ArrayList或数组就不合适了,这种情况原始对象和副本对象将指向相同的堆,你可以通过一种叫深度克隆的技术防止这种事情发生。他的每一个可变的域被独立的克隆,简而言之,下面总结就是一个clone方法是如何工作的。 1. 任何类在实例上调用clone(),他将实现cloneable重写clone方法,创建副本。
1 2 3 4 5 6 7 8 9 10 11 |
Rectangle
rec = new Rectangle( 30 , 60 );
logger.info(rec);
try {
logger.info( "Creating
Copy of this object using Clone method" );
Rectangle
copy = rec.clone();
logger.info( "Copy
" +
copy);
} catch (CloneNotSupportedException
ex) {
logger.debug( "Cloning
is not supported for this object" );
}
|
1 2 3 4 |
@Override
protected Rectangle
clone() throws CloneNotSupportedException
{
return (Rectangle) super .clone();
}
|
1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
import org.apache.log4j.Logger;
/**
*
Simple example of overriding clone() method in Java to understand How Cloning of
*
Object works in Java.
*
*
@author
*/
public class JavaCloneTest
{
private static final Logger
logger = Logger.getLogger(JavaCloneTest. class );
public static void main(String
args[]) {
Rectangle
rec = new Rectangle( 30 , 60 );
logger.info(rec);
Rectangle
copy = null ;
try {
logger.info( "Creating
Copy of this object using Clone method" );
copy
= rec.clone();
logger.info( "Copy
" +
copy);
} catch (CloneNotSupportedException
ex) {
logger.debug( "Cloning
is not supported for this object" );
}
//testing
properties of object returned by clone method in Java
logger.info( "copy
!= rec : " +
(copy != rec));
logger.info( "copy.getClass()
== rec.getClass() : " +
(copy.getClass() == rec.getClass()));
logger.info( "copy.equals(rec)
: " +
copy.equals(rec));
//Updating
fields in original object
rec.setHeight( 100 );
rec.setWidth( 45 );
logger.info( "Original
object :" +
rec);
logger.info( "Clonned
object :" +
copy);
}
}
public class Rectangle implements Cloneable{
private int width;
private int height;
public Rectangle( int w, int h){
width
= w;
height
= h;
}
public void setHeight( int height)
{
this .height
= height;
}
public void setWidth( int width)
{
this .width
= width;
}
public int area(){
return widthheight;
}
@Override
public String
toString(){
return String.format( "Rectangle
[width: %d, height: %d, area: %d]" ,
width, height, area());
}
@Override
protected Rectangle
clone() throws CloneNotSupportedException
{
return (Rectangle) super .clone();
}
@Override
public boolean equals(Object
obj) {
if (obj
== null )
{
return false ;
}
if (getClass()
!= obj.getClass()) {
return false ;
}
final Rectangle
other = (Rectangle) obj;
if ( this .width
!= other.width) {
return false ;
}
if ( this .height
!= other.height) {
return false ;
}
return true ;
}
@Override
public int hashCode()
{
int hash
= 7 ;
hash
= 47 hash
+ this .width;
hash
= 47 hash
+ this .height;
return hash;
}
}
Output:
2013 - 05 - 20 23 : 46 : 58 , 882 0 [main]
INFO JavaCloneTest - Rectangle [width: 30 ,
height: 60 ,
area: 1800 ]
2013 - 05 - 20 23 : 46 : 58 , 882 0 [main]
INFO JavaCloneTest - Creating Copy of this object
using Clone method
2013 - 05 - 20 23 : 46 : 58 , 882 0 [main]
INFO JavaCloneTest - Copy Rectangle [width: 30 ,
height: 60 ,
area: 1800 ]
2013 - 05 - 20 23 : 46 : 58 , 882 0 [main]
INFO JavaCloneTest - copy != rec : true
2013 - 05 - 20 23 : 46 : 58 , 882 0 [main]
INFO JavaCloneTest - copy.getClass() == rec.getClass() : true
2013 - 05 - 20 23 : 46 : 58 , 882 0 [main]
INFO JavaCloneTest - copy.equals(rec) : true
2013 - 05 - 20 23 : 46 : 58 , 882 0 [main]
INFO JavaCloneTest - Original object :Rectangle [width: 45 ,
height: 100 ,
area: 4500 ]
2013 - 05 - 20 23 : 46 : 58 , 882 0 [main]
INFO JavaCloneTest - Cloned object :Rectangle [width: 30 ,
height: 60 ,
area: 1800 ]
|
- 克隆方法用于创建对象的拷贝,为了使用clone方法,类必须实现java.lang.Cloneable接口重写protected方法clone,如果没有实现Clonebale接口会抛出CloneNotSupportedException.
- 在克隆java对象的时候不会调用构造器
- java提供一种叫浅拷贝(shallow copy)的默认方式实现clone,创建好对象的副本后然后通过赋值拷贝内容,意味着如果你的类包含可变对象,那么原始对象和克隆都将指向相同的内部对象,这是很危险的,因为发生在可变的字段上任何改变将反应到原始对象和副本对象上。为了避免这种情况,重写clone()方法。
- 按照约定,实例的克隆应该通过调用super.clone()获取,这样有助克隆对象的不变性建如:clone!=original和clone.getClass()==original.getClass(),尽管这些不是必须的
时间: 2024-10-31 15:32:15