如果希望一个类能够克隆,那么:
(1) 实现Cloneable 接口
(2) 覆盖clone() 需要申明为public;只覆盖方法而不实现接口,则会抛出异常CloneNotSupportedException
(3) 在自己的clone()中调用super.clone()
(4) 在自己的clone()中捕获违例
这一系列步骤能达到最理想的效果。
java浅复制:
package com.soft.clone;
public class CloneTest implements Cloneable
{
private Entity entity;
private int tmp;
public CloneTest(Entity entity,int tmp)
{
this.entity = entity;
this.tmp = tmp;
}
public Object clone()
{
Object o = null;
try
{
o = super.clone();
} catch (Exception e)
{
e.printStackTrace();
}
return o;
}
public void increment()
{
tmp++;
}
public int getTmp() {
return tmp;
}
public Entity getEntity() {
return entity;
}
public static void main(String[] args)
{
Entity entity = new Entity(6);
CloneTest test1 = new CloneTest(entity, 1);
CloneTest test2 = (CloneTest)test1.clone();
test2.increment();
test2.getEntity().increment();
System.out.println("test1.tmp = " + test1.getTmp());
System.out.println("test1.Entity.i = " + test1.getEntity().getI());
System.out.println("test2.tmp = " + test2.getTmp());
System.out.println("test2.Entity.i = " + test2.getEntity().getI());
}
}
class Entity
{
private int i;
public Entity(int i)
{
this.i = i;
}
public void increment()
{
i++;
}
public int getI() {
return i;
}
}
输出结果:
test1.tmp = 1
test1.Entity.i = 7
test2.tmp = 2
test2.Entity.i = 7
浅复制只会复制对象的基本类型成员。若对象中包含其他对象作为成员变量(上例中的entity),则只复制entity的引用,实际指向的是同一个实例。
thinking in java 4th 12章
最初,Java 只是作为一种用于控制硬件的语言而设计,与因特网并没有丝毫联系。象这样一类面向大众的语言一样,其意义在于程序员可以对任意一个对象进行克隆。这样一来,clone()就放置在根类Object 里面,但因为它是一种公用方式,因而我们通常能够对任意一个对象进行克隆。看来这是最灵活的方式了,毕竟它不会带来任何害处。
正当Java 看起来象一种终级因特网程序设计语言的时候,情况却发生了变化。突然地,人们提出了安全问题,而且理所当然,这些问题与使用对象有关,我们不愿望任何人克隆自己的保密对象。所以我们最后看到的是为原来那个简单、直观的方案添加的大量补丁:clone()在Object 里被设置成“protected”。必须将其覆盖,并使用“implement Cloneable”,同时解决违例的问题。只有在准备调用Object 的clone()方法时,才没有必要使用Cloneable 接口,因为那个方法会在运行期间得到检查,以确保我们的类实现了Cloneable。但为了保持连贯性(而且由于Cloneable 无论如何都是空的),最好还是由自己实现Cloneable。
java深层复制:
试图深层复制合成对象时会遇到一个问题。必须假定成员对象中的clone()方法也能依次对自己的句柄进行深层复制,以此类推。
为了能正常实现深层复制,必须对所有类中的代码进行控制,或者至少全面掌握深层复制中需要涉及的类,确保它们自己的深层复制能正确进行。
java深层复制方法有两种:
1、使用序列化深复制
2、克隆合成对象时候,将合成对象对象所拥有的句柄也进行深层复制
import java.io.Serializable;
//方式一:序列化
public class Thing2 implements Serializable
{
Thing1 o1 = new Thing1();
}
class Thing1 implements Serializable
{
}
public class Thing4 implements Cloneable
{
Thing3 o3 = new Thing3();
//方式二:clone方法中对成员变量(成员对象是一个句柄)也进行克隆
public Object clone()
{
Thing4 o = null;
try
{
//此处明确克隆后的对象为Thing4,为了能够访问成员变量,对成员变量也进行复制
o = (Thing4)super.clone();
} catch (Exception e)
{
e.printStackTrace();
}
o.o3 = (Thing3)o3.clone();
return o;
}
}
class Thing3 implements Cloneable
{
public Object clone()
{
Object o = null;
try
{
o = super.clone();
} catch (Exception e)
{
e.printStackTrace();
}
return o;
}
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Compete
{
static final int SIZE = 5000;
public static void main(String[] args)
{
Thing2[] a = new Thing2[SIZE];
for (int i = 0; i < a.length; i++)
{
a[i] = new Thing2();
}
Thing4[] b = new Thing4[SIZE];
for (int i = 0; i < b.length; i++)
{
b[i] = new Thing4();
}
try
{
long t1 = System.currentTimeMillis();
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(buf);
for (int i = 0; i < a.length; i++)
{
o.writeObject(a[i]);
}
//now get copies
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));
Thing2[] c = new Thing2[SIZE];
for (int i = 0; i < c.length; i++)
{
c[i] = (Thing2)in.readObject();
}
long t2 = System.currentTimeMillis();
System.out.println("Duplication via serialization:"
+ (t2 - t1) + " Milliseconds");
//clone
t1 = System.currentTimeMillis();
Thing4[] d = new Thing4[SIZE];
for (int i = 0; i < d.length; i++)
{
d[i] = (Thing4)b[i].clone();
}
t2 = System.currentTimeMillis();
System.out.println("Duplication via cloning:"
+ (t2 - t1) + " Milliseconds");
} catch (Exception e)
{
}
}
}
输出结果:
运行第一次结果:
Duplication via serialization:81 Milliseconds
Duplication via cloning:3 Milliseconds
运行第二次结果:
Duplication via serialization:76 Milliseconds
Duplication via cloning:3 Milliseconds
Serializable 类很容易设置,但在复制它们时却要做多得多的工作。克隆涉及到大量的类设置工作,但实际的对象复制是相当简单的。
除了序列化和克隆之间巨大的时间差异以外,我们也注意到序列化技术的运行结果并不稳定,而克隆每一次花费的时间都是相同的。