对Java中的克隆感到困惑吗

对Java中的克隆感到困惑吗,java,clone,cloning,Java,Clone,Cloning,我用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 voi

我用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);
    }
}
我创建了这个类的一个对象,然后克隆了它。现在,当我在一个对象中更改
name
的值时,在另一个对象中name的值保持不变。奇怪的是在构造函数中,我只是对
名称
使用了一个简单的引用,而不是为
名称
创建一个新的
字符串。现在,由于
String
s是引用类型,我希望克隆中的
String
也会被更改。谁能告诉我发生了什么事?提前谢谢

编辑

代码测试

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

我使用BlueJ的“检查变量”功能来检查值。

假设
original
是原始克隆测试对象的名称,
cloned
是您使用
clone()
方法从
original
创建的克隆对象

事情就是这样:
1.您的
克隆的.name
原始的.name
指向同一个对象,在本例中是一个字符串。
2.然后,您要求您的
original.name
指向另一个字符串对象(“Dolly”)。将新字符串对象(“推拉”)指定给引用
original.name

3.但是,
cloned.name
仍然指向第一个字符串对象(“Dolly”)

因此,
cloned.name
仍然打印第一个字符串对象


现在,如果您能够在不重新分配引用的情况下更改字符串对象的内容,则
clone.name
中的更改将反映在
original.name
中。但是对于字符串对象,由于字符串的不变性,这是不可能的。但是,您可以反映从
克隆
原始
的更改,可以说是可变字符串。请看一下相同的示例代码:

类的每个实例对一个对象都有不同的引用。您只是在更改引用,而不是修改对象。如果您将字符串放在某个holder对象中,然后克隆它并在holder中设置字符串(不是holder引用,而是holder中的字符串引用),那么您将在这两个克隆中进行更改,那么您是说您正在执行以下操作:

 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);  
  }
?


如果是这样,那么所有这些断言都应该通过。克隆方法中有引用类型,最初克隆时,它们引用同一对象。但是setName(“…”)更改了实例指向的值,而不是引用对象的值。

通过查看哈希代码,与@vijay answer一起获得更好的清晰度

    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
输出:

 h1 74525175 h2 74525175
 h1 113629 h2 74525175
}


}

即使包装器类也是;)如果您在其中一个对象中设置了新名称,则对象名称会获取对传递字符串的新引用,而另一个对象保持引用字符串不可变,当您更改它时,将引用一个新字符串。实际上,您对赋值的工作方式感到困惑:每次使用“=”时,您都在更改引用。因此,当您使用setXXX时,您正在更改对新对象的引用。可替换性与此无关。这只是简单地重新分配一个引用变量。但是,如果它是一个int数组,并且克隆对象的数组元素发生了更改,该怎么办?@vijay在这两种情况下都会看到此更改,因为只有一个数组有两个引用。在您的示例中,应用于OPs问题时,他会将引用变量重新分配给一个新数组,类似于
real.setMyIntArray(新的int[]{0,1,2})。OPs的问题是为什么real.setName(“Dolly”);不会同时更改两个对象实例。这不是因为字符串是不可变的,而是因为您在原始实例中重新分配了一个引用变量。@GriffeyDog是的,您是正确的。这是因为OP正在重新分配引用本身,在这种情况下,这只是一个重新分配的问题。然而,我的观点是,如果内部内容(比如引用字段或数组元素)发生了更改,则会反映出更改。想象一下,如果字符串不是不可变的,并且字符串赋值导致字符串的内容发生更改,而不是创建一个新的字符串对象,那么在这种情况下,更改是可见的。
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());
}