Java中的 深拷贝 与 浅拷贝


Java中的 深拷贝 与 浅拷贝

​ 深入深拷贝与浅拷贝之前,先复习一下Java中的访问修饰符,特别是protected,再去理解深/浅拷贝的知识。

Java中的访问修饰符

  1. public

    用public修饰的类、类属变量及方法:包内及包外的任何类(包括子类和普通类)均可以访问;

  2. protected

    用protected修饰的类、类属变量及方法,包内的任何类及包外那些继承了该类的子类才能访问,protected重点突出继承

    protected修饰符的修饰的成员变量和方法也称为受保护的成员变量和方法

    受保护的成员变量和方法:

    1. 可以在本类或同一个包中的其它类(包括子类)中通过类的实例进行访问

    2. 也可以被同一个包中的类或不同包中的类继承,但是不能在不同包中的其它类(包括子类)中通过该类的实例进行访问。

  3. default

    如果一个类、类属变量及方法没有用任何修饰符(即没有用public、protected及private中任何一种修饰),则其访问权限为default(默认访问权限)。默认访问权限的类、类属变量及方法,包内的任何类(包括继承了此类的子类)都可以访问它,而对于包外的任何类都不能访问它(包括包外继承了此类的子类)。default重点突出

  4. private

    用private修饰的类、类属变量及方法,只有本类可以访问而包内包外的任何类均不能访问它

对象克隆

Object类中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝。

实际上,clone() 方法是 Object类中的一个protected方法,这说明你的代码不能直接调用,只有Employee类可以克隆Employee对象。

这个限制是有原因的,看看Object类如何实现clone() 。它对这个对象一无所知,所以只能逐个域进行拷贝。

如果含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样一来,原对象和克隆的对象仍然会共享一些信息。

这就是浅拷贝

对于子对象属于不可变类,如String,浅拷贝是安全的。

浅拷贝

public class Address{
    private String city;

    public Address(String city) {
        this.city = city;
    }
	
    // ... getter and setter

    // ... toString
}

public class Employee implements Cloneable{
    private String name;
    private int age;
    private Address address;

    public Employee(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // ... getter and setter

    // ... toString

    @Override
    protected Employee clone(){
        Employee employee = null;
        try {
            employee = (Employee)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return employee;
    }
}

@Test
public void testClone(){
    System.out.println("克隆之前");
    Address address = new Address("深圳市");
    Employee employee_before = new Employee("been", 18, address);
    System.out.println("employee_before: "+ employee_before);

    System.out.println("克隆之后");
    Employee employee_after = employee.clone();

    employee1.getAddress().setCity("广州市");
    employee1.setName("mad");
    employee1.setAge(23);
    
    System.out.println("employee_after:  " + employee_after);

    System.out.println("employee_before: " + employee_before);
}

输出结果会发现,第一个员工对象的名字和年龄和第二个员工对象的名字和年龄不一致,说明name(基本类型)和age(基本类型)克隆成功,即修改第二个员工的名字和年龄不会影响到第一个员工的名字和年龄。

但是,修改完第二个员工的地址后发现第一个员工的地址也发生了变化,说明 Address(引用类型)克隆不成功,即修改的是同一个对象,如果想把Address(引用类型)克隆成功,需要深拷贝

深拷贝

要想实现 Address(引用类型)的深克隆,首先让Address类实现 Cloneable 接口,重写clone方法

public class Address implements Cloneable{
    private String city;

    public Address(String city) {
        this.city = city;
    }

    // ... getter and setter

    // ... toString

    @Override
    protected Address clone(){
        Address address = null;
        
        try {
            address = (Address)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return address;
    }
}

在Employee类的clone方法里添加一行代码:employee.address = address.clone();

public class Employee implements Cloneable{
    private String name;
    private int age;
    private Address address;

    public Employee(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }

    // ... getter and setter

    // ... toString

    @Override
    protected Employee clone(){
        Employee employee = null;
        try {
            employee = (Employee)super.clone();
            employee.address = address.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return employee;
    }
}

这次修改第二个员工的地址不会影响第一个员工的address,说明深克隆成功。

总结:

如果需要克隆的类中只有基本类型的话,那么只在该类中实现Cloneable接口即可,如果该类中除了包含基本类型之外,还有引用类型的话,需要将该引用对象也要实现Cloneable接口,并重写clone方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值