【问题标题】:Confused about cloning in Java对Java中的克隆感到困惑
【发布时间】:2013-07-10 14:54:27
【问题描述】:

我有这段 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 的值保持不变。这里奇怪的是在构造函数中,我只是为name 使用了一个简单的引用,而不是为name 创建一个新的String。现在,由于Strings 是引用类型,我希望克隆中的String 也会被更改。谁能告诉我发生了什么事?提前致谢!

编辑

代码测试

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

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

【问题讨论】:

  • 甚至 Wrapper 类都是 ;)
  • 如果您在其中一个对象名称中设置了新名称,则会获得对传递字符串的新引用,而另一个对象保留该引用
  • 字符串是不可变的,当你改变它时,会引用一个新的字符串
  • 实际上,您对分配的工作方式感到困惑:每次使用“=”时,您都在更改引用。因此,当您使用 setXXX 时,您正在更改对新对象的引用

标签: java clone cloning


【解决方案1】:

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

事情是这样的:
1. 您的 cloned.nameoriginal.name 指向同一个对象,在本例中是一个字符串。
2. 然后你让你的original.name 指向一个不同的字符串对象(“Dolly”)。当您将新的 String 对象(“Dolly”)分配给引用 original.name 时,就会发生这种情况。
3. 但是,cloned.name 仍然指向第一个 String 对象(“Dolly”)。

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

现在,如果您能够在不重新分配引用的情况下更改 String 对象的内容,那么 clone.name 中的更改将反映在 original.name 中。但是对于字符串对象,由于字符串的不变性,这是不可能的。但是,您可以使用 StringBuffers 反映从 cloneoriginal 的变化,可以说是可变字符串。看一下这个示例代码:https://gist.github.com/VijayKrishna/5967668

【讨论】:

  • 不变性与它无关。这只是对引用变量的简单重新分配。
  • 也许吧。但是,如果它是一个 int 数组并且克隆对象的数组元素被更改了怎么办?
  • @vijay 在这两种情况下都可以看到这种变化,因为只有一个数组有 2 个引用。
  • 在您的示例中,应用于 OPs 问题,他会将引用变量重新分配给一个新数组,例如real.setMyIntArray(new int[]{0,1,2});。 OP 的问题是为什么 real.setName("Dolly");不会更改两个对象实例。这不是因为 String 是不可变的,而是因为您在原始实例中重新分配了一个引用变量。
  • @GriffeyDog 是的,你是对的。这是因为 OP 正在重新分配引用本身,在这种情况下,它只是重新分配的问题。但是,我的观点是,如果内部内容(比如引用的字段或数组的元素)发生了变化,那么变化就会反映出来。想象一下,如果 Strings 不是不可变的,并且 String 赋值导致 String 的内容发生变化而不是创建一个新的 String 对象,那么在这种情况下更改将是可见的。
【解决方案2】:

你的类的每个实例对一个对象都有不同的引用。您只是在更改参考而不是修改对象。如果您将字符串放在某个持有者对象中,然后克隆它并在持有者内设置字符串(不是持有者引用,而是持有者内部的字符串引用),那么您将在两个克隆中进行更改

【讨论】:

    【解决方案3】:

    你是说你正在做类似的事情:

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

    ?

    如果是这样,那么所有这些断言都应该通过。您的 clone 方法中有引用类型,当最初克隆时,它们引用同一个对象。但是 setName("...") 改变的是实例指向的值,而不是被引用对象的值。

    【讨论】:

      【解决方案4】:

      通过查看哈希码和@vijay 答案获得更清晰的答案。

          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
      

      【讨论】:

        【解决方案5】:
        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());
        }
        

        }

        【讨论】:

          猜你喜欢
          • 2014-03-29
          • 2018-10-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-10-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多