【问题标题】:Java: Why String is special in Deep Copy and Shallow copy?Java:为什么字符串在深拷贝和浅拷贝中很特别?
【发布时间】:2025-11-21 22:30:01
【问题描述】:

这几天我在学习Java的clone方法。我了解到clone 使用浅拷贝。如果我想实现一个对象的深拷贝,那么我应该遵循this website中的以下原则

无需单独复制原语。 原类中的所有成员类都应支持克隆,并且在上下文中原类的clone方法中应在所有成员类上调用super.clone()。

在我的理解中,String在Java中是一种引用,为了更好的表达我的观点,代码如下:

// User.java
public class User implements Cloneable{
    private String name;

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

    // Getter() ,setter() and toString() are omitted here
}

以下代码为测试代码:

User user = new User();
user.setName("one");

User clone = (User)user.clone();
clone.setName("two");

System.out.println(user.getName());     // I think it should print "two", but actually "one"

所以在我的示例中,我似乎创建了User 的深层副本(是吗?)

以下是我对Java内存的理解:

①表示我创建一个副本,②是字符串的变化。

所以如果我的图片是正确的,它应该打印“二”,对吧?

我知道java中的String是不可变的的,我想这大概就是为什么会出现这种情况的原因,但是我不知道这是怎么发生的,为什么会发生这种情况。 p>


根据@Jesper 的图片,我新建一张图片来更具体地解释我的问题。

还有演示代码:

public class Employee implements Cloneable {

    private int employeeId;
    private String employeeName;
    private Department department;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();   // shallow copy
    }

    // Getter() ,setter() and toString() are omitted here
}
public class Department{
    private int id;
    private String name;

    // Getter() ,setter() and toString() are omitted here
}

测试代码:

Department hr = new Department(1, "Human Resource");
Employee original = new Employee(1, "Admin", hr);
Employee cloned = (Employee) original.clone();
cloned.getDepartment().setName("Finance");
System.out.println(original.getDepartment().getName());  //print Finance

还有图片:

红色部分是关键。如果是java.lang.String 对象,它将创建一个新对象(如上所示从“一”到“二”),但如果是另一个类对象(这里是Department 类),那么似乎会有只有一个对象(而不是创建一个),所以我的问题是为什么字符串在深拷贝和浅拷贝中是 special 的?这与String 不变性有关吗?

提前致谢!

【问题讨论】:

  • 不,你有两个独立的用户对象。浅拷贝仍然是拷贝,不是同一个对象。设置一个字段不会改变另一个字段。这适用于任何字段,而不仅仅是字符串。
  • @khelwood 我编辑了我的问题。而在示例 2(员工和部门)中,代码 System.out.println(original.getDepartment() == cloned.getDepartment()); 将打印 true,这意味着原始和克隆共享同一个对象。
  • 关于您的添加:不,字符串没有什么特别之处。您之前的代码和第二个示例之间的区别:您没有更改克隆的Employee 对象中的部门;你要更深一层,你改变Department对象中的name。如果您会使用cloned.setDepartment(new Department(...)); 而不是cloned.getDepartment().setName(...);,那么它将与您的第一个示例相同。
  • "表示原始和克隆共享同一个对象。"——表示原始和克隆中的deparment 字段都指向同一个部门对象,因为您没有复制Department。但是如果你将一个员工的department 设置为另一个Department 对象,它不会影响到另一个员工,因为它们是不同的对象。与第一个示例中的字符串相同。
  • 我知道我哪里出错了:我进入了更深的层次,这让我很困惑!非常感谢!

标签: java string deep-copy shallow-copy


【解决方案1】:

你从这种情况开始:

然后克隆User 对象。你现在有两个User 对象;变量userclone 指的是这两个对象。请注意,它们的两个name 成员变量都引用同一个String 对象,内容为"one"

然后在clone 上调用setName("two"),这将更改第二个User 对象的name 成员变量以引用不同的String 对象,其内容为"two"

请注意,变量user 仍然引用User 对象,该对象的name 成员变量引用"one",所以当你System.out.println(user.getName()); 时,结果是one

【讨论】: