这里我们就可以引入两个专业的术语:浅克隆(shallow clone)和深克隆(deep clone)。
所谓的浅克隆,顾名思义就是很表面的很表层的克隆,如果我们要克隆Administrator对象,只克隆他自身以及他包含的所有对象的引用地址。而深克隆,就是非浅克隆。克隆除自身以外所有的对象,包括自身所包含的所有对象实例。至于深克隆的层次,由具体的需求决定,也有“N层克隆”一说。但是,所有的基本(primitive)类型数据,无论是浅克隆还是深克隆,都会进行原值克隆。毕竟他们都不是对象,不是存储在堆中。(Java中所有的对象都是保存在堆中,而堆是供全局共享的。也就是说,如果同一个Java程序的不同方法,只要能拿到某个对象的引用,引用者就可以随意的修改对象的内部数据。)
要让一个对象进行克隆,其实就是两个步骤:1. 让该类实现java.lang.Cloneable接口;2. 重写(override)Object类的clone()方法。(并且在方法内部调用持有对象的clone()方法;如果有N多个持有的对象,那就要写N多的方法,突然改变了类的结构,还要重新修改clone()方法。)
public class CloneBean implements Serializable,Cloneable{
public class ShallowClone implements Cloneable {
// 基本类型 每个对象一份
private int a;
private String b;
// 非基本类型 数组 对象, 复制的对象保持一个引用,相关的修改都会影响
private int[] c;
private CloneBean clonebean;
// 重写Object.clone()方法,并把protected改为public 《只是浅克隆, 如果要实现真正的深克隆,用到 序列化及反序列化》
@Override
public Object clone() {
ShallowClone sc = null;
try {
sc = (ShallowClone) super.clone();//要调用 父类的 clone方法
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
//sc.clonebean=(CloneBean)clonebean.clone(); 显示的调用 对象的克隆,也可以保持对象的新值,因为 本身对象又是一个新的对象
return sc;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
public int[] getC() {
return c;
}
public void setC(int[] c) {
this.c = c;
}
public CloneBean getClonebean() {
return clonebean;
}
public void setClonebean(CloneBean clonebean) {
this.clonebean = clonebean;
}
}
public class ShallowCloneTest {
public static void main(String[] args) throws CloneNotSupportedException {
ShallowClone c1 = new ShallowClone();
// 对c1赋值
c1.setA(100);
c1.setB("clone1");
c1.setC(new int[] { 1000 });
CloneBean cb = new CloneBean();
cb.setI(10);
cb.setSt1("str1");
c1.setClonebean(cb);
System.out.println("克隆前: c1.a=" + c1.getA());
System.out.println("克隆前: c1.b=" + c1.getB());
System.out.println("克隆前: c1.c[0]=" + c1.getC()[0]);
System.out.println("克隆前: c1.cb.str1=" + c1.getClonebean().getSt1());
System.out.println("-----------");
// 克隆出对象c2,并对c2的属性A,B,C进行修改
ShallowClone c2 = (ShallowClone) c1.clone();
// 对c2进行修改
c2.setA(50);
c2.setB("clone2");
int[] a = c2.getC();
a[0] = 500;
c2.setC(a);
CloneBean cb1 = c2.getClonebean();
cb1.setSt1("tewtstewt");
c2.setClonebean(cb1);
System.out.println("克隆后: c1.a=" + c1.getA());
System.out.println("克隆后: c1.b=" + c1.getB());
System.out.println("克隆后: c1.c[0]=" + c1.getC()[0]);
System.out.println("克隆后: c1.cb.str1=" + c1.getClonebean().getSt1());
System.out.println("---------------");
System.out.println("克隆后: c2.a=" + c2.getA());
System.out.println("克隆后: c2.b=" + c2.getB());
System.out.println("克隆后: c2.c[0]=" + c2.getC()[0]);
System.out.println("克隆后: c2.cb.str1=" + c2.getClonebean().getSt1());
}
}
×××××××××××××××××××××用serializable来实现×××××××××××××××××××××××××××××××××××
//前提是对象以及对象内部 所有引用到的对象 都是可串行化的,否则,就需要仔细考察那些不可串行化的对象可否设成transient,从而将之排除在复制过程之外。而类变量 是全局的,也不作序列化操作。
public class DeepClone implements Serializable
{
private static final long serialVersionUID = -4371342386287259388L;
private transient String transi;//修饰为不可瞬时的 不可序列化对象
public static int k;
private int a;
private String b;
private int[] c;
private CloneBean cb;//必须也是可串行化的
public int getA()
{
return a;
}
public void setA(int a)
{
this.a = a;
}
public String getB()
{
return b;
}
public void setB(String b)
{
this.b = b;
}
public int[] getC()
{
return c;
}
public void setC(int[] c)
{
this.c = c;
}
public CloneBean getCb() {
return cb;
}
public void setCb(CloneBean cb) {
this.cb = cb;
}
public String getTransi() {
return transi;
}
public void setTransi(String transi) {
this.transi = transi;
}
public static int getK() {
return k;
}
public static void setK(int k) {
DeepClone.k = k;
}
// 用序列化与反序列化实现深克隆
public Object deepClone() {
Object o = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos
.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
o = ois.readObject();
ois.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return o;
}
}