4

I have this code in Java.

public class CloneTest implements Cloneable{
    String name;
    int marks;
    public CloneTest(String s, int i) {
        name = s;
        marks = i;
    }

    public void setName(String s) {
        name = s;
    }

    public void setMarks(int i) {
        marks = i;
    }

    @Override
    public Object clone() {
        return new CloneTest(this.name, this.marks);
    }
}

I have created one object of this class, and then cloned it. Now, when I change the value of name in one object, the value of name remains unchanged in the other. The strange thing here is in the constructor, I am just using a simple reference for name, not creating a new String for name. Now, since Strings are reference types, I expected the String in the clone to be changed as well. Can anyone tell me what's going on? Thanks in advance!

EDIT

Code Testing

CloneTest real = new CloneTest("Molly", 22);
CloneTest clone = real.clone();
real.setName("Dolly");

I used the "Inspect Variables" feature of BlueJ to check the values.

JavaNewbie_M107
  • 2,007
  • 3
  • 21
  • 36

5 Answers5

4

Assume that original is the name of original CloneTest object, and cloned is the cloned object that you created from original using the clone() method.

This is what happened:
1. Your cloned.name and original.name are pointing at the same object, which in this case was a String.
2. Then you asked your original.name to point to a different String object ("Dolly"). This happens when you assign the new String object ("Dolly") to the reference original.name.
3. But, the cloned.name still points to the first String object ("Dolly").

Hence, cloned.name still prints the 1st String object.

Now, if you are able to change the content of the String object without reassigning the references, then the change in clone.name will reflect in original.name. But for String objects, this is not possible due to the immutability of Strings. However, you can reflect the change from the clone to original with StringBuffers which are mutable strings so to speak. Take a look at this example code for the same: https://gist.github.com/VijayKrishna/5967668

vijay
  • 2,646
  • 2
  • 23
  • 37
  • 2
    Immutability has nothing to do with it. It's just simple reassignment of a reference variable. – GriffeyDog Jul 10 '13 at 15:06
  • perhaps. but, what if it were an int array and an element of the array for the cloned object were changed? – vijay Jul 10 '13 at 15:11
  • @vijay than this change would be visible in both instances, because there is only one array with 2 references to it. – jlordo Jul 10 '13 at 15:16
  • In your example, as applied to the OPs question, he would be reassigning the reference variable to a new array, something like `real.setMyIntArray(new int[]{0,1,2});`. The OPs question is why real.setName("Dolly"); doesn't change both object instances. It's not because String is immutable, it's because you're reassigning a reference variable in the original instance. – GriffeyDog Jul 10 '13 at 15:16
  • @GriffeyDog yes you are correct. It is because the OP is reassigning the reference itself, in this case it is only a matter of reassignment. However, my point is that if the internal content, say like a field of the reference, or element of an array, were changed, then change would be reflected. Imagine, if Strings were not immutable, and String assignments caused the content of the String to change instead of creating a new String object, then the change would be visible in this case. – vijay Jul 10 '13 at 15:24
  • The point is, in this case the OP is assigning a reference variable to a new object, `String` or otherwise. `String` immutability does not come into play in this question. – GriffeyDog Jul 10 '13 at 15:30
  • For this specific question, agreed. Thank you for the clarification. :) – vijay Jul 10 '13 at 16:20
2

Each instance of your class has different references to an object. You you're just changing reference not modifying object. If you place your string in some holder object, then clone it and set string inside the holder (not a holder reference but string reference inside holder) then you'll have your changes in both of clones

Bitman
  • 1,996
  • 1
  • 15
  • 18
0

So are you saying you are doing something like:

 public void testSomeMethod() {

      CloneTest a = new CloneTest("a", 1);

      CloneTest b = (CloneTest) a.clone();

      a.setName("b");

      assertFalse(b.name.equals(a.name));
      assertEquals("b", a.name);
      assertEquals("a", b.name);  
  }

?

If so, then all these assertions should pass. Your clone method has reference types in it, and when initially cloned, they refer to the same object. But the setName("...") changes the value that instance points to, not the value of the referred to object.

Kristian H
  • 174
  • 5
0

Get some better clarity along with @vijay answer by looking hash code.

    CloneTest real = new CloneTest("Molly", 22);
    CloneTest clone = (CloneTest) real.clone();
    int h1=real.name.hashCode();
    int h2=clone.name.hashCode();
    System.out.println("h1 "  + h1  + " h2 " + h2); // same
    real.setName("sak");
    h1=real.name.hashCode();
    h2=clone.name.hashCode();
    System.out.println("h1 "  + h1  + " h2 " + h2); //different

Output :

 h1 74525175 h2 74525175
 h1 113629 h2 74525175
vels4j
  • 11,208
  • 5
  • 38
  • 63
0
package com.test;
class Manager implements Cloneable
{
String firstName;
String lastName;
int age;
public Manager(String fname,String lname,int a)
{
    this.firstName=fname;
    this.lastName=lname;
    this.age=a;
}
public String getFirstName() {
    return firstName;
}
public void setFirstName(String firstName) {
    this.firstName = firstName;
}
public String getLastName() {
    return lastName;
}
public void setLastName(String lastName) {
    this.lastName = lastName;
}
public int getAge() {
    return age;
}
public void setAge(int age) {
    this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
    // TODO Auto-generated method stub
    return super.clone();
}

}

public class TestCloning {
public static void main(String[] args) throws CloneNotSupportedException {
    Manager m1=new Manager("Sadik","Tahir",26);
    Manager m_clone=(Manager)m1.clone();
    Manager m2=m1;
    System.out.println("M1 Details:::");
    System.out.println("Fisrt Name:"+m1.getFirstName()+",LastName:"+m1.getLastName()+",Age:"+m1.getAge());
    System.out.println("Hashcode:"+m1.hashCode());
    System.out.println("M_Clone Details:::");
    System.out.println("Fisrt Name:"+m_clone.getFirstName()+",LastName:"+m_clone.getLastName()+",Age:"+m_clone.getAge());
    System.out.println("Hashcode:"+m_clone.hashCode());
    System.out.println("M2 Details:::");
    System.out.println("Fisrt Name:"+m2.getFirstName()+",LastName:"+m2.getLastName()+",Age:"+m2.getAge());
    System.out.println("Hashcode:"+m2.hashCode());
    m1.setFirstName("Afreen");
    m1.setLastName("Khan");
    m1.setAge(25);
    System.out.println("M1 Details:::");
    System.out.println("Fisrt Name:"+m1.getFirstName()+",LastName:"+m1.getLastName()+",Age:"+m1.getAge());
    System.out.println("Hashcode:"+m1.hashCode());
    System.out.println("M_Clone Details:::");
    System.out.println("Fisrt Name:"+m_clone.getFirstName()+",LastName:"+m_clone.getLastName()+",Age:"+m_clone.getAge());
    System.out.println("Hashcode:"+m_clone.hashCode());
    System.out.println("M2 Details:::");
    System.out.println("Fisrt Name:"+m2.getFirstName()+",LastName:"+m2.getLastName()+",Age:"+m2.getAge());
    System.out.println("Hashcode:"+m2.hashCode());
}

}