4

These days I am learning the clone method of Java. I learned that clone uses shallow copy. If I want to implement a deep copy of an object, then I should follow the following principles from 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.

In my understanding, String in Java is a kind of reference, in order to better express my point of view, this is the code:

// 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
}

And the following code is the test code:

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"

So in my example, it seems I create a Deep Copy of User(do I?)

Here is my understanding of Java memory:

①means I create a copy, ② is the change of the String.

So if my picture is correct, it should print "two", right?

I know the String in java is immutable, and I think this is probably the reason why this happens, but I don't know how does this happens and the reason why this happens.


According to @Jesper's picture, I create a new picture to explain my question more specifically.

And the demo code:

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
}

Test code:

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

And the picture:

The red part is the key. If it is the java.lang.String object, it will create a new one(from "one" to "two" as above showed), but if it is another class object(Here is Department class) then it seems there will be only one object(instead of creating one), so my question is Why String is special in Deep Copy and Shallow copy? Is this related to String immutability?

Thanks in advance!

chenlangping
  • 101
  • 2
  • 7
  • 3
    No, you have two **separate** User objects. A shallow copy is still a copy, not the same object. Setting a field in one does not alter the field in the other. That goes for any field, not just a string. – khelwood Apr 01 '21 at 07:18
  • @khelwood I edited my question. And in example 2(Employee and Department), and the code `System.out.println(original.getDepartment() == cloned.getDepartment());` will print `true` which means the original and the clone share the same object. – chenlangping Apr 01 '21 at 08:29
  • 1
    About your addition: No, there is nothing special about string. The difference between your earlier code and the second example: You are not changing the department in your cloned `Employee` object; you're going a level deeper, you change the `name` in the `Department` object. If you would have done `cloned.setDepartment(new Department(...));` instead of `cloned.getDepartment().setName(...);` then it would be the same as your first example. – Jesper Apr 01 '21 at 08:59
  • 2
    "means the original and the clone share the same object."—It means that the `deparment` field in the original and the clone are both pointing to the same Department object, because you didn't copy `Department`. But if you set the `department` of one employee to another `Department` object, it will not affect the other employee, because they are different objects. Same as with the strings in your first example. – khelwood Apr 01 '21 at 09:07
  • I know where I went wrong: **I got to a level deeper** and that makes me confused! Thank you so much! – chenlangping Apr 01 '21 at 09:16
  • That’s why the name shallow copy or shallow cloning in Java. If only primitive type fields or Immutable objects are there then there is no difference between shallow and deep copy in Java. [https://www.geeksforgeeks.org/deep-shallow-lazy-copy-java-examples/] – Ulviyya Ibrahimli May 26 '21 at 11:26

1 Answers1

11

You start with this situation:

enter image description here

Then you clone the User object. You now have two User objects; the variables user and clone refer to those two objects. Note that both their name member variables refer to the same String object, with the content "one".

enter image description here

Then you call setName("two") on clone, which will change the name member variable of the second User object to refer to a different String object, with the content "two".

enter image description here

Note that the variable user still refers to the User object which has its name member variable referring to "one", so when you System.out.println(user.getName()); the result is one.

Jesper
  • 202,709
  • 46
  • 318
  • 350