Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去。举例说明:比如,对象A和对象B都属于类S,具有属性a和b。那么对对象A进行拷贝操作赋值给对象B就是:B.a=A.a; B.b=A.b;
在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用现有对象的部分或全部 数据。
Java中的对象拷贝主要分为:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。
先介绍一点铺垫知识:Java中的数据类型分为基本数据类型和引用数据类型。对于这两种数据类型,在进行赋值操作、用作方法参数或返回值时,会有值传递和引用(地址)传递的差别。
浅拷贝(Shallow Copy):①对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。②对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
具体模型如图所示:可以看到基本数据类型的成员变量,对其值创建了新的拷贝。而引用数据类型的成员变量的实例仍然是只有一份,两个对象的该成员变量都指向同一个实例。
浅拷贝的实现方式主要有三种:
一、通过拷贝构造方法就行浅拷贝:
拷贝构造方法指的是该类的构造方法参数为该类的对象。使用拷贝构造方法可以很好地完成浅拷贝,直接通过一个现有的对象创建出与该对象属性相同的新的对象。
代码参考如下:
/* 拷贝构造方法实现浅拷贝 */ public class CopyConstructor { public static void main(String[] args) { Person p1=new Person(20,"摇头耶稣"); Person p2=new Person(p1); System.out.println(p2); } } class Person{ private int age; private String name; public Person(int age,String name) { this.age=age; this.name=name; } public Person(Person p) { this.name=p.name; this.age=p.age; } //声明拷贝构造方法,方法体中将所有属性都进行赋值 public String toString() { return this.name+this.age; } }
运行结果为:
摇头耶稣20
要注意:拷贝构造方法有且只能实现浅拷贝!
二、通过重写clone()方法进行浅拷贝:
Object类是类结构的根类,其中有一个方法为protected Object clone() throws CloneNotSupportedException,这个方法就是进行的浅拷贝。有了这个浅拷贝模板,我们可以通过调用clone()方法来实现对象的浅拷贝。但是需要注意:1、Object类虽然有这个方法,但是这个方法是受保护的(被protected修饰),所以我们无法直接使用。2、使用clone方法的类必须实现Cloneable接口,否则会抛出异常CloneNotSupportedException。对于这两点,我们的解决方法是,在要使用clone方法的类中重写clone()方法,通过super.clone()调用Object类中的原clone方法。
参考代码如下:对Student类的对象进行拷贝,直接重写clone()方法,通过调用clone方法即可完成浅拷贝。
/* clone方法实现浅拷贝 */ public class ShallowCopy { public static void main(String[] args) { Age a=new Age(20); Student stu1=new Student("摇头耶稣",a,175); //通过调用重写后的clone方法进行浅拷贝 Student stu2=(Student)stu1.clone(); System.out.println(stu1.toString()); System.out.println(stu2.toString()); //尝试修改stu1中的各属性,观察stu2的属性有没有变化 stu1.setName("大傻子"); //改变age这个引用类型的成员变量的值 a.setAge(99); //stu1.setaAge(new Age(99)); 使用这种方式修改age属性值的话,stu2是不会跟着改变的。因为创建了一个新的Age类对象而不是改变原对象的实例值 stu1.setLength(216); System.out.println(stu1.toString()); System.out.println(stu2.toString()); } } /* * 创建年龄类 */ class Age{ //年龄类的成员变量(属性) private int age; //构造方法 public Age(int age) { this.age=age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String toString() {