Java 浅拷贝和深拷贝的理解和实现方式

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() {

你可能感兴趣的:(Java 浅拷贝和深拷贝的理解和实现方式)