1、对象的拷贝 深拷贝和浅拷贝 :i.关于 clone对象拷贝在实际编程过程,有时候我们会遇到一种情况: 当你有一个对象 A,在某一个时刻, A 已经保存了对应的属性值,而且这些值本身是有效的,这个时候可能需要一个和 A 完全相同的对象 B,并且当 B里面的属性值发生变化的时候, A 中的属性值不受影响,可以理解为 A 和 B 独立,但是 B 的初始化不是按照我们平时创建该对象的时候的初始化操作, B 的初始化数据完全来自 A。对 Java 存储模型了解的人都明白,在 Java 里面如果针对两个对象引用采取赋值操作的时候,仅仅是让两个引用指向了同一对象,如果其中一个引用里面的对象属性改变的时候会影
2、响另外一个对象属性跟着改变,所以 Java 语言本身的对象赋值语句是不能完成上边的需求的。在这种时候,就需要用到 Object 类里面的通用方法 clone(),这里需要说明的是:通过 clone()方法创建的对象是一个新对象,它可以认为是源对象的一个拷贝,但是在内存堆中,JVM 会为这个拷贝分配新的对象存储空间来存放该对象的所有状态该拷贝和普通对象使用 new 操作符创建的对象唯一的区别在于初始值,这个拷贝的初始值不是对象里面成员的默认值,而是和源对象此刻状态的成员的值是一样的下边这段代码是 clone 方法的运用:public class testpublic static void ma
3、in(String args)AClass class1 = new AClass();class1.a = 12;AClass class2 = (AClass)class1.clone();System.out.println(class2.a);System.out.println(class1=class2);class AClass implements Cloneablepublic int a = 0;public Object clone()AClass o = null;tryo = (AClass)super.clone();catch(CloneNotSupportedE
4、xception ex)ex.printStackTrace();return o; 上边这段代码运行结果输出为:12false可以知道的就是成功复制了一个 AClass 的对象,该对象的引用为class1,而拷贝对象的引用为 class2,这两个引用通过 =比较输出为false,证明这两个引用不是指向了同一个对象,而且拷贝对象里面的 a 的值和class1 引用的对象里面的 a 的值是一样的,都是 12,这样就成功完成了对象的拷贝过程。若你对上边这段代码还有不了解的地方可以尝试将下边的代码修改掉:AClass class2 = (AClass)class1.clone() 修改为:AClas
5、s class2 = class1;改了过后,输出结果应该为:12true所以在对象的 clone 过程,需要注意的几点有:1希望能够提供对象 clone 功能的类必须实现 Cloneable 接口,这个接口位于 java.lang 包里面2希望提供对象 clone 功能的类必须重载 clone()方法,在重载过程可以看到这句话:super .clone();也就是说,不论 clone 类的继承结构如何,我们在对象拷贝的时候都直接或间接调用了 Object 的 clone()方法。而且细心留意可以看到 Object 的 clone 方法是 protected 域的,也就是说这个方法只有 Obj
6、ect 的子类可以调用,而在重载的时候将 clone 方法修饰符改为 public3还有一点很重要就是 Object 源代码里面的 clone()方法是 native 方法,一般而言,对 JVM 来说,native 方法的效率远比 java 中的普通方法高,这就是为什么我们在复制一个对象的时候使用 Object 的 clone()方法,而不是使用 new 的方式。4Cloneable 接口和我们在编写 IO 程序的时候序列化接口一样,只是一个标志,这个接口是不包含任何方法的,这个标志主要是为了检测 Object 类中的 clone 方法,若我们定义的类想要实现拷贝功能,但是没有实现该接口而调用
7、 Object 的 clone 方法,那么就会出现语句中 catch 块里面的异常错误,抛出 CloneNotSupportedException。ii 浅拷贝在对象 clone 的过程中,浅拷贝又称为“影子 clone”,先看一段代码:/这里先定义一个类class AClasspublic int a;public AClass(int a) this.a = a;public void change() a += 12;public String toString() return “A Value is “ + this.a;/定义一个 clone 类,里面包含了 AClass 的对象引
8、用class BClass implements Cloneablepublic int a = 12;public AClass obj = new AClass(11);public Object clone()BClass object = null;tryobject = (BClass)super.clone();catch(CloneNotSupportedException ex)ex.printStackTrace();return object;public class TestClone public static void main(String args)BClass
9、class1 = new BClass();class1.a = 15;System.out.println(class1.a);System.out.println(class1.obj);BClass class2 = (BClass)class1.clone();class2.a = 22;class2.obj.change();System.out.println(class1.a);System.out.println(class1.obj);System.out.println(class2.a);System.out.println(class2.obj);运行上边这段代码会有以
10、下输出:15A Value is 1115 /这里拷贝成功了A Value is 23 /!不对,根本没有调用 class1 里面的 obj 的 change方法,所以不应该修改 class1 里面的 obj 里面的变量 a 的值【初衷】22A Value is 23不知细心的读者有没有发现输出和我们预期的拷贝不一样,虽然 class2 引用的对象是从 class1 拷贝过来的, class2 里面的引用 obj 和 class1 里面的引用 obj 实际上还是指向了同一个对象,其含义在于,拷贝的初衷是要复制一个一模一样的对象,包括对象里面的对象也应该实现的是复制操作,它最终的目的是保证 cl
11、ass1 和 class2 本身的属性以及 class1 和 class2 里面的对象引用的属性在拷贝过后的各种相关操作里面相互独立,上边输出证明了 class1 和class2 里面的变量 a 确实已经拷贝成功,但是 class1 和 class2 里面的AClass 对象的引用 obj 在拷贝过后还是指向了同一个对象,所以拷贝结束过后,调用 class2 的 obj 的 change 方法的时候,也修改了 class1 里面的 obj指向的对象里面的值。所以在 Java 里面我们把上边的拷贝过程称为“浅拷贝”,同样又称为“影子 clone”。从这里可以知道,在 JVM 的对象复制里面,实际
12、上基本数据类型可以直接通过这种方式来进行拷贝工作,而非原始类型这样操作了过后拷贝的对象仅仅拷贝了对象里面的基本数据类型的成员变量,而比较复杂的类型的成员变量并没有像预期一样产生拷贝效果,这种拷贝我们就称之为“浅拷贝”ii.深拷贝如果要实现我们预期的对象拷贝效果,就需要使用深拷贝操作,其实在浅拷贝基础上实现深拷贝有两个步骤,以上边的代码为例:1第一步:让 AClass 实现同样的 clone 功能2第二步:在 BClass 的 clone 操作中多写入一句话:object.obj = (AClass)obj.clone();修改后程序如下:/这里先定义一个类class AClass implem
13、ents Cloneablepublic int a;public AClass(int a) this.a = a;public void change() a += 12;public String toString() return “A Value is “ + this.a;public Object clone()AClass object = null;tryobject = (AClass)super.clone();catch(CloneNotSupportedException ex)ex.printStackTrace();return object;/定义一个clone
14、 类,里面包含了 AClass的对象引用class BClass implements Cloneablepublic int a = 12;public AClass obj = new AClass(11);public Object clone()BClass object = null;tryobject = (BClass)super.clone();object.obj = (AClass)obj.clone();catch(CloneNotSupportedException ex)ex.printStackTrace();return object;public class t
15、est public static void main(String args)BClass class1 = new BClass();class1.a = 15;System.out.println(class1.a);System.out.println(class1.obj);BClass class2 = (BClass)class1.clone();class2.a = 22;class2.obj.change();System.out.println(class1.a);System.out.println(class1.obj);System.out.println(class2.a);System.out.println(class2.obj);