Java: 为什么String在Deep Copy和Shallow copy中比较特殊?

Java: Why String is special in Deep Copy and Shallow copy?

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

中的以下原则

No need to separately copy primitives. All the member classes in original class should support cloning and in clone method of original class in context should call super.clone() on all member classes.

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

// 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内存的理解:

①表示我创建了一个副本,②是String的变化。

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

我知道java中的String是不可变的,我想这可能是造成这种情况的原因,但我不知道这是怎么发生的以及发生这种情况的原因。


根据@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 对象,它会创建一个新的(如上所示从“一”到“二”),但如果它是另一个 class 对象(这里是 Department class) 那么似乎只有一个对象(而不是创建一个),所以我的问题是为什么 String 在 Deep Copy 和 Shallow Copy 中是 special?这与 String 不变性有关吗?

提前致谢!

你从这种情况开始:

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

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

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