Java克隆(Clone)是
Java语言的特性之一,但在实际中应用比较少见。但有时候用克隆会更方便更有效率。
对于克隆(Clone),Java有一些限制:
1、被克隆的类必须自己实现Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。Cloneable 接口实际上是个标识接口,没有任何接口方法。
2、实现Cloneable接口的类应该使用公共方法重写 Object.clone(它是受保护的)。某个对象实现了此接口就克隆它是不可能的。即使 clone 方法是反射性调用的,也无法保证它将获得成功。
3、在Java.lang.Object类中克隆方法是这么定义的:
protected Object clone()
throws CloneNotSupportedException
创建并返回此对象的一个副本。表明是一个受保护的方法,同一个包中可见。
按照惯例,返回的对象应该通过调用 super.clone 获得。
引题:
举个例子说吧,现在有一个对象比如叫foo,你需要在创建当前对象的一个副本作为存根你能怎么做?
假如你不用Clone,那么你可以先new一个对象foo1:Foo foo1=new Foo(),然后用foo给foo1对象set值,这样就得到foo的副本foo1;除此之外,别无选择。
这样说,也许有人会觉得说的过于绝对了,不过事实如此啊。
要产生一个副本,那副本要不要内存?----当然要了,那就对了!既然需要内存,(不克隆的情况下)你不new还有什么办法呢?请大家时刻铭记对象是Java运行时产生的,驻留在计算机内存中。
常见错误:
下面我澄清几个初学者容易犯迷糊的错误,同样的问题,产生foo对象的副本:
1、Foo foo1=new Foo();
foo1=foo;
然后就想当然的认为副本foo1生成了!
错误原因:foo1没错是申请了内存,但是执行foo1=foo后,foo1就不在指向刚申请的内存区域了,转而指向foo对象的内存区域,这时候,foo1、foo指向了同一内存区域。刚才new的操作制造一堆垃圾等着JVM回收。
2、Foo foo1=foo;
错误原因:还是两个变量都指向了同一块内存。
3、有些老鸟更厉害一些:在Foo中定义一个返回自身的方法:
public Foo getInstance(){
return this;
}
然后,Foo foo1=foo.getInstance();
错误原因:同上,主要还是没有重新开辟内存,this在对象里是什么?----就是对象自己的引用!那么getInstance()自然返回的就是对象自己,反正又是两个对象穿了一条裤子----***,哈哈。错得心服口服吧。为了节省篇幅,我在最后写个例子,留给那些对此有异议的人看。
引入克隆
看了这么多方法都不行,还很麻烦!干脆用克隆吧,简单明了。
废话不说了,看例子:
定义两个类CloneFooA、CloneFooB,然后写个测试类CloneDemo分别克隆这两个类的对象,然后打印测试结果到控制台。
1
/** */
/**
2
* 简单类克隆实现
3
* 要实现克隆,必须实现Cloneable接口,这是一个标识接口,没有接口方法
4
* 实现了 Cloneable 接口,以指示 Object.clone() 方法可以合法地对该类实例进行按字段复制。
5
* 按照惯例,实现此接口的类应该使用公共方法重写 Object.clone(它是受保护的)。
6
*/
7
![](https://i-blog.csdnimg.cn/blog_migrate/353c1fa9cd42287d30310af820b5430d.gif)
public
class
CloneFooA
implements
Cloneable
{
8
private String strA;
9
private int intA;
10![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
11![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public CloneFooA(String strA, int intA)
{
12
this.strA = strA;
13
this.intA = intA;
14
}
15![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
16![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public String getStrA()
{
17
return strA;
18
}
19![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
20![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public void setStrA(String strA)
{
21
this.strA = strA;
22
}
23![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
24![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public int getIntA()
{
25
return intA;
26
}
27![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
28![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public void setIntA(int intA)
{
29
this.intA = intA;
30
}
31![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
32![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
/** *//**
33
* @return 创建并返回此对象的一个副本。
34
* @throws CloneNotSupportedException
35
*/
36![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public Object clone() throws CloneNotSupportedException
{
37
//直接调用父类的clone()方法,返回克隆副本
38
return super.clone();
39
}
40
}
41
![](https://i-blog.csdnimg.cn/blog_migrate/17c135a2763225eb253f4ff92e88a664.gif)
42
1
/** */
/**
2
* 深度克隆对象,当类存在聚合关系的时候,克隆就必须考虑聚合对象的克隆
3
*/
4
![](https://i-blog.csdnimg.cn/blog_migrate/353c1fa9cd42287d30310af820b5430d.gif)
public
class
CloneFooB
implements
Cloneable
{
5
private CloneFooA fooA;
6
private Double douB;
7![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
8![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public CloneFooB(Double douB)
{
9
this.douB = douB;
10
}
11![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
12![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public CloneFooB(CloneFooA fooA, Double douB)
{
13
this.fooA = fooA;
14
this.douB = douB;
15
}
16![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
17![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public CloneFooA getFooA()
{
18
return fooA;
19
}
20![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
21![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public void setFooA(CloneFooA fooA)
{
22
this.fooA = fooA;
23
}
24![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
25![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public Double getDouB()
{
26
return douB;
27
}
28![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
29![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public void setDouB(Double douB)
{
30
this.douB = douB;
31
}
32![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
33![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
/** *//**
34
* 克隆操作
35
*
36
* @return 自身对象的一个副本
37
* @throws CloneNotSupportedException
38
*/
39![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public Object clone() throws CloneNotSupportedException
{
40
//先调用父类的克隆方法进行克隆操作
41
CloneFooB cloneFooB = (CloneFooB) super.clone();
42
//对于克隆后出的对象cloneFooB,如果其成员fooA为null,则不能调用clone(),否则出空指针异常
43
if (this.fooA != null)
44
cloneFooB.fooA = (CloneFooA) this.fooA.clone();
45![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
46
return cloneFooB;
47
}
48
}
49
![](https://i-blog.csdnimg.cn/blog_migrate/17c135a2763225eb253f4ff92e88a664.gif)
50
1
/** */
/**
2
* 测试类:分别克隆CloneFooA和CloneFooB类,并打印克隆前后的结果.
3
*/
4
![](https://i-blog.csdnimg.cn/blog_migrate/353c1fa9cd42287d30310af820b5430d.gif)
public
class
CloneDemo
{
5![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public static void main(String args[]) throws CloneNotSupportedException
{
6
//CloneFooA克隆前
7
CloneFooA fooA1 = new CloneFooA("FooA", 11);
8
System.out.println("CloneFooA的对象克隆前对象fooA1值为: " + fooA1.getStrA() + "," + fooA1.getIntA());
9
//CloneFooA克隆后
10
CloneFooA fooA2 = (CloneFooA) fooA1.clone();
11
System.out.println("CloneFooA的对象克隆后对象fooA2值为: " + fooA2.getStrA() + "," + fooA2.getIntA());
12
//比较fooA1和fooA2内存地址
13
if (fooA1 == fooA2) System.out.println("比较fooA1和fooA2内存地址:相等!");
14
else System.out.println("比较fooA1和fooA2内存地址:不相等!");
15![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
16
System.out.println("-------------------------");
17![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
18
//CloneFooB克隆前
19
CloneFooB fooB1 = new CloneFooB(fooA1, new Double("33"));
20
System.out.println("CloneFooB的对象克隆前对象fooB1值为: " + fooB1.getFooA().getStrA() + "," + fooB1.getFooA().getIntA() + " | " + fooB1.getDouB());
21
//CloneFooB克隆后
22
CloneFooB fooB2 = (CloneFooB) fooB1.clone();
23
System.out.println("CloneFooB的对象克隆前对象fooB2值为: " + fooB2.getFooA().getStrA() + "," + fooB2.getFooA().getIntA() + " | " + fooB2.getDouB());
24![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
25
if (fooA1 == fooA2) System.out.println("比较fooB1和fooB2内存地址:相等!");
26
else System.out.println("比较fooB1和fooB2内存地址:不相等!");
27
}
28
}
29
![](https://i-blog.csdnimg.cn/blog_migrate/17c135a2763225eb253f4ff92e88a664.gif)
30
运行结果:
CloneFooA的对象克隆前对象fooA1值为: FooA,11
CloneFooA的对象克隆后对象fooA2值为: FooA,11
比较fooA1和fooA2内存地址:不相等!
-------------------------
CloneFooB的对象克隆前对象fooB1值为: FooA,11 | 33.0
CloneFooB的对象克隆前对象fooB2值为: FooA,11 | 33.0
比较fooB1和fooB2内存地址:不相等!
Process finished with exit code 0
反面教材:
最后,我给出我上面提出到最后要给出的反面例子。
随便写一个,在CloneFooA 的基础上做了少许改动,内容如下:
1
public
class
CloneFooA
implements
Cloneable
{
2
private String strA;
3
private int intA;
4![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
5![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public CloneFooA(String strA, int intA)
{
6
this.strA = strA;
7
this.intA = intA;
8
}
9![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
10![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public String getStrA()
{
11
return strA;
12
}
13![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
14![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public void setStrA(String strA)
{
15
this.strA = strA;
16
}
17![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
18![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public int getIntA()
{
19
return intA;
20
}
21![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
22![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public void setIntA(int intA)
{
23
this.intA = intA;
24
}
25![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
26![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
/** *//**
27
* @return 创建并返回此对象的一个副本。
28
* @throws CloneNotSupportedException
29
*/
30![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public Object clone() throws CloneNotSupportedException
{
31
//直接调用父类的clone()方法,返回克隆副本
32
return super.clone();
33
}
34![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
35![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
/** *//**
36
* @return 返回运行时的对象
37
*/
38![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public CloneFooA getInstance()
{
39
return this;
40
}
41![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
42![](https://i-blog.csdnimg.cn/blog_migrate/0263e0c3f9f3faecf5b4177968c5d64c.gif)
public static void main(String args[])
{
43
CloneFooA fooA=new CloneFooA("aa",11);
44
System.out.println(fooA.getStrA()+" "+fooA.getIntA());
45![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
46
CloneFooA fooA1=fooA.getInstance();
47
System.out.println(fooA1.getStrA()+" "+fooA1.getIntA());
48
if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等!");
49![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
50
System.out.println("-------------------------");
51![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
52
//改变后fooA或者fooA1中任何一个,看看另外一个是否会改变
53
fooA1.setStrA("bb");
54
System.out.println(fooA.getStrA()+" "+fooA.getIntA());
55
System.out.println(fooA1.getStrA()+" "+fooA1.getIntA());
56![](https://i-blog.csdnimg.cn/blog_migrate/258e3e26538340eb1c68b7657f2a2de8.gif)
57
if(fooA==fooA1) System.out.println("fooA和fooA1内存地址相等,改变fooA1后,fooA的值也跟着变化了");
58
}
59
}
60
![](https://i-blog.csdnimg.cn/blog_migrate/17c135a2763225eb253f4ff92e88a664.gif)
61
运行结果:
aa 11
aa 11
fooA和fooA1内存地址相等!
-------------------------
bb 11
bb 11
fooA和fooA1内存地址相等,改变fooA1后,fooA的值也跟着变化了
Process finished with exit code 0
来自:http://www.cnitblog.com/lzzzing/archive/2008/05/14/43711.html