JAVA

深拷贝浅拷贝,一个例子就明白

转载:深拷贝浅拷贝,一个例子就明白

今天分享一个Java开发中经常遇到的问题:拷贝对象时,有时候改了一个对象,另一个对象也跟着变了。这是怎么回事呢?

其实这涉及到拷贝的两种方式:浅拷贝深拷贝。别被名字吓到,一个例子就能搞懂!

什么是浅拷贝?


只复制对象的表面,内部引用还是共享的

我们先看一个简单的例子:

public classStudent {
    private String name;
    private List<String> hobbies;
    
    // 构造方法、getter、setter省略...
    
    // 浅拷贝方法
    public Student shallowCopy() {
        Studentcopy=newStudent();
        copy.name = this.name;
        copy.hobbies = this.hobbies; // 直接赋值引用
        return copy;
    }
}

测试一下:

public classTest {
    publicstaticvoidmain(String[] args) {
        Studentstudent1=newStudent();
        student1.setName("小明");
        student1.setHobbies(Arrays.asList("篮球", "游戏"));
        
        // 浅拷贝
        Studentstudent2= student1.shallowCopy();
        
        // 修改student2的爱好
        student2.getHobbies().add("编程");
        
        System.out.println("student1的爱好:" + student1.getHobbies());
        System.out.println("student2的爱好:" + student2.getHobbies());
    }
}

输出结果:

student1的爱好:[篮球, 游戏, 编程]
student2的爱好:[篮球, 游戏, 编程]

看到了吗?我们只修改了student2,但student1也跟着变了!这就是浅拷贝的特点。

 

什么是深拷贝?


不仅复制对象表面,连内部的引用对象也重新创建

再看深拷贝的实现:

public classStudent {
    private String name;
    private List<String> hobbies;
    
    // 深拷贝方法
    public Student deepCopy() {
        Studentcopy=newStudent();
        copy.name = this.name;
        copy.hobbies = newArrayList<>(this.hobbies); // 创建新的List
        return copy;
    }
}

测试深拷贝:

public classTest {
    publicstaticvoidmain(String[] args) {
        Studentstudent1=newStudent();
        student1.setName("小明");
        student1.setHobbies(Arrays.asList("篮球", "游戏"));
        
        // 深拷贝
        Studentstudent3= student1.deepCopy();
        
        // 修改student3的爱好
        student3.getHobbies().add("编程");
        
        System.out.println("student1的爱好:" + student1.getHobbies());
        System.out.println("student3的爱好:" + student3.getHobbies());
    }
}

输出结果:

student1的爱好:[篮球, 游戏]
student3的爱好:[篮球, 游戏, 编程]

这次student1没有受到影响,这就是深拷贝的效果!

 

两者区别一目了然


通过内存结构图理解本质差异

让我们用图来看看内存中发生了什么:

浅拷贝的问题:两个对象的hobbies引用指向同一个List,修改任何一个都会影响另一个。

深拷贝的优势:每个对象都有自己独立的List,互不影响。

 

开发中的实际应用


知道什么时候该用哪种拷贝

1. 配置对象拷贝

// 系统配置需要深拷贝,避免误修改影响全局
SystemConfig backupConfig = originalConfig.deepCopy();

2. 缓存数据拷贝

// 从缓存取出的对象要深拷贝,避免业务修改污染缓存
User userFromCache = cache.get(userId).deepCopy();

3. 什么时候用浅拷贝

// 只是想复制基本属性,内部对象本身就是不可变的
Student student = original.shallowCopy(); // String是不可变的,可以放心浅拷贝

 

实现深拷贝的常用方法


三种方式,各有特点

方法一:手动实现

public Student deepCopy() {
    Student copy = new Student();
    copy.name = this.name;
    copy.hobbies = new ArrayList<>(this.hobbies);
    return copy;
}

方法二:使用序列化

// 需要实现Serializable接口
public Student deepCopy()throws Exception {
    ByteArrayOutputStreambos=newByteArrayOutputStream();
    ObjectOutputStreamoos=newObjectOutputStream(bos);
    oos.writeObject(this);
    
    ByteArrayInputStreambis=newByteArrayInputStream(bos.toByteArray());
    ObjectInputStreamois=newObjectInputStream(bis);
    return (Student) ois.readObject();
}

方法三:使用工具库

// 使用Apache Commons Lang
Student copy = SerializationUtils.clone(original);

// 使用JSON转换(需要Gson等库)
String json = gson.toJson(original);
Student copy = gson.fromJson(json, Student.class);

 

总结


记住这个口诀:

  • • 浅拷贝:表面兄弟,内心相通
  • • 深拷贝:独立个体,各自精彩

在实际开发中,大部分情况下我们需要的是深拷贝,特别是涉及到集合、数组等引用类型的时候。