I am trying to understand the concept of shallow vs deep copy in Java. There is a lot of articles and Q&A about this subject, but whenever I try to implement these concepts in a real Java code, everything become unclear to me.
One of the answers on which I base my understanding is in this link, where deep and shallow copying are explained via schemas.
I will show you below my implementation for each case:
- Shallow copy:
I took for my example the method System.arraycopy() as I read in many articles that it performs a shallow copy (along with the clone method)
public class Test {
    public static void main(String[] args) {
        NameValue[] instance1 = {
                new NameValue("name1", 1),
                new NameValue("name2", 2),
                new NameValue("name3", 3),
        };
        NameValue[] instance2 = new NameValue[instance1.length];
        // Print initial state
        System.out.println("Arrays before shallow copy:");
        System.out.println("Instance 1: " + Arrays.toString(instance1));
        System.out.println("Instance 2: " + Arrays.toString(instance2));
        // Perform shallow copy
        System.arraycopy(instance1, 0, instance2, 0, 3);
        // Change instance 1
        for (int i = 0; i < 3; i++) {
            instance1[i].change();
        }
        // Print final state
        System.out.println("Arrays after shallow copy:");
        System.out.println("Instance 1: " + Arrays.toString(instance1));
        System.out.println("Instance 2: " + Arrays.toString(instance2));
    }
    private static class NameValue {
        private String name;
        private int value;
        public NameValue(String name, int value) {
            super();
            this.name = name;
            this.value = value;
        }
        public void change() {
            this.name = this.name + "-bis";
            this.value = this.value + 1;
        }
        @Override
        public String toString() {
            return this.name + ": " + this.value;
        }
    }
}
The result of the execution of the main methods is as follows:
Arrays before shallow copy:
Instance 1: [name1: 1, name2: 2, name3: 3]
Instance 2: [null, null, null]
Arrays after shallow copy:
Instance 1: [name1-bis: 2, name2-bis: 3, name3-bis: 4]
Instance 2: [name1-bis: 2, name2-bis: 3, name3-bis: 4]
this result is an accordance with the schema of the previous link:

- Deep copy:
I took for this example the method Arrays.copyOf() as I read in many articles that it performs a deep copy (along with the Arrays.copyOfRange method)
public static void main(String[] args) {
    NameValue[] instance1 = {
            new NameValue("name1", 1),
            new NameValue("name2", 2),
            new NameValue("name3", 3),
    };
    NameValue[] instance2 = new NameValue[instance1.length];
    // Print initial state
    System.out.println("Arrays before deep copy:");
    System.out.println("Instance 1: " + Arrays.toString(instance1));
    System.out.println("Instance 2: " + Arrays.toString(instance2));
    // Perform deep copy
    instance2 = Arrays.copyOf(instance1, 3);
    // Change instance 1
    for (int i = 0; i < 3; i++) {
        instance2[i].change();
    }
    // Print final state
    System.out.println("Arrays after deep copy:");
    System.out.println("Instance 1: " + Arrays.toString(instance1));
    System.out.println("Instance 2: " + Arrays.toString(instance2));
}
which displays:
Arrays before deep copy:
Instance 1: [name1: 1, name2: 2, name3: 3]
Instance 2: [null, null, null]
Arrays after deep copy:
Instance 1: [name1-bis: 2, name2-bis: 3, name3-bis: 4]
Instance 2: [name1-bis: 2, name2-bis: 3, name3-bis: 4]
If we base the deep copy logic on the previous schema, this should be the result:

As you may notice, the result of the execution of the main method is different from the logic of the schema above.
Any explanation will be welcome.
 
     
     
     
    