深拷贝、浅拷贝和clone、new

Java 中存在基础类型和引用类型。Java 的赋值都是传值的。
  1. 对于基础类型,会拷贝具体的内容。

  2. 对于引用类型,存储的这个值只是指向实际对象的地址,拷贝也只会拷贝引用地址。

  • 浅拷贝
  1. 对基本数据类型进行值传递
  2. 对引用数据类型进行引用地址的拷贝
  • 深拷贝
  1. 对基本数据类型进行值传递
  2. 对引用数据类型,创建一个新的对象,并复制其内容

拷贝相关API

Object#clone

Java 中所有的对象都是继承自 java.lang.ObjectObject 对象中提供了一个 protected 类型的 clone 方法。

protected native Object clone() 
    throws CloneNotSupportedException;

Object#clone() 方法是 native 的,所以不需要我们来实现。需要注意的是,clone 方法是 protected 的,这意味着 clone 方法只能在 java.lang 包或者其子类可见。如果我们想要在一个程序中调用某个对象的 clone 方法则是不可以的。因为 clone 方法是定义在 Object 中的,该对象并没有对外可见的 clone 方法。

Cloneable接口

在上文中提到,Object#clone() 方法是 protected 的,我们不能直接在程序中对一个对象调用 clone 方法。JDK 推荐「实现 Cloneable 接口并重写 clone 方法(可使用 public 修饰符)来实现属性的拷贝」。

package java.lang;

/**
 * 此处给出 Cloneable 的部分注释
 * A class implements the Cloneable interface to
 * indicate to the java.lang.Object#clone() method that it
 * is legal for that method to make a
 * field-for-field copy of instances of that class.
 * 
 * Invoking Object's clone method on an instance that does not implement the
 * Cloneable interface results in the exception
 * CloneNotSupportedException being thrown.
 * 
 * By convention, classes that implement this interface should override
 * Object.clone (which is protected) with a public method.
 */
public interface Cloneable {

}

阅读 Cloneable 源码,有如下结论:

  1. 对于实现 Cloneable 接口的对象,是可以调用 Object#clone() 方法来进行属性的拷贝。
  2. 若一个对象没有实现 Cloneable 接口,直接调用 Object#clone() 方法,会抛出 CloneNotSupportedException 异常。
  3. Cloneable 是一个空接口,并不包含 clone 方法。但是按照惯例(by convention),实现 Cloneable 接口时,应该以 public 修饰符重写 Object#clone() 方法(该方法在 Object 中是被 protected 修饰的)。

 

浅拷贝

参考 Cloneable 接口的源码注释部分,如果一个类实现了 Cloneable 接口,那么 Objectclone 方法将返回该对象的逐个属性(field-by-field)拷贝

  • 对基本数据类型进行值传递
  • 对引用数据类型进行引用地址的拷贝
  • 定义两个对象,AddressCustomerUser
@Data
class Address implements Cloneable{
    private String name;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

@Data
class CustomerUser implements Cloneable{
    private String firstName;
    private String lastName;
    private Address address;
    private String[] cars;

    @Override
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        testShallowCopy();
    }

    public static void testShallowCopy() throws CloneNotSupportedException {
        Address address= new Address();
        address.setName("北京天安门");
        CustomerUser customerUser = new CustomerUser();
        customerUser.setAddress(address);
        customerUser.setLastName("李");
        customerUser.setFirstName("雷");
        String[] cars = new String[]{"别克","路虎"};
        customerUser.setCars(cars);

        //浅拷贝
        CustomerUser customerUserCopy =(CustomerUser) customerUser.clone();

        customerUserCopy.setFirstName("梅梅");
        customerUserCopy.setLastName("韩");
        customerUserCopy.getAddress().setName("北京颐和园");
        customerUserCopy.getCars()[0]="奥迪";

        System.out.println("customerUser: " + JSONUtil.toJsonStr(customerUser));
        System.out.println("customerUserCopy: " + JSONUtil.toJsonStr(customerUserCopy));
    }
}
  • 程序运行结果如下。
customerUser: {"lastName":"李","address":{"name":"北京颐和园"},"firstName":"雷","cars":["奥迪","路虎"]}
customerUserCopy: {"lastName":"韩","address":{"name":"北京颐和园"},"firstName":"梅梅","cars":["奥迪","路虎"]}

深拷贝

实现深拷贝有两种方式,「序列化对象方式」和「二次调用 clone 方式」
  1. 序列化(serialization)方式
    • 先对对象进行序列化,再进行反序列化,得到一个新的深拷贝的对象
  2. 二次调用 clone 方式
    • 先调用对象的 clone() 方法
    • 对对象的引用类型的属性值,继续调用 clone() 方法进行拷贝

修改 CustomerUserclone() 方法,对 CustomerUser 对象的引用类型的属性值,即 Address 属性值和数组(String[])属性值 cars,二次调用 clone 方法。

 

@Data
class CustomerUser implements Cloneable{
    private String firstName;
    private String lastName;
    private Address address;
    private String[] cars;

    @Override
    public Object clone() throws CloneNotSupportedException{
        CustomerUser customerUserDeepCopy = (CustomerUser) super.clone();
        //二次调用clone方法
        customerUserDeepCopy.address = (Address) address.clone();
        customerUserDeepCopy.cars = cars.clone();
        return customerUserDeepCopy;
    }
}

public class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        testShallowCopy();
    }

    public static void testShallowCopy() throws CloneNotSupportedException {
        Address address= new Address();
        address.setName("北京天安门");
        CustomerUser customerUser = new CustomerUser();
        customerUser.setAddress(address);
        customerUser.setLastName("李");
        customerUser.setFirstName("雷");
        String[] cars = new String[]{"别克","路虎"};
        customerUser.setCars(cars);

        //浅拷贝
        CustomerUser customerUserCopy =(CustomerUser) customerUser.clone();

        customerUserCopy.setFirstName("梅梅");
        customerUserCopy.setLastName("韩");
        customerUserCopy.getAddress().setName("北京颐和园");
        customerUserCopy.getCars()[0]="奥迪";

        System.out.println("customerUser: " + JSONUtil.toJsonStr(customerUser));
        System.out.println("customerUserCopy: " + JSONUtil.toJsonStr(customerUserCopy));
    }
}
  • 运行程序,输出结果如下。
customerUser: {"lastName":"李","address":{"name":"北京天安门"},"firstName":"雷","cars":["别克","路虎"]}
customerUserCopy: {"lastName":"韩","address":{"name":"北京颐和园"},"firstName":"梅梅","cars":["奥迪","路虎"]}

posted @   KLAPT  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示