对Java中的克隆感到困惑吗
我用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
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());
}