在Java中复制对象

use*_*900 71 java variables pointers reference object

我了解到,当您在Java中修改变量时,它不会更改它所基于的变量

int a = new Integer(5);
int b = a;
b = b + b;
System.out.println(a); // 5 as expected
System.out.println(b); // 10 as expected
Run Code Online (Sandbox Code Playgroud)

我假设对象有类似的东西.考虑这个课程.

public class SomeObject {
    public String text;

    public SomeObject(String text) {
        this.setText(text);
    }

    public String getText() {
        return text;
    }   

    public void setText(String text) {
        this.text = text;
    }
}
Run Code Online (Sandbox Code Playgroud)

在我尝试这段代码后,我感到困惑.

SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;
s2.setText("second");
System.out.println(s1.getText()); // second as UNexpected
System.out.println(s2.getText()); // second as expected
Run Code Online (Sandbox Code Playgroud)

请向我解释为什么更改任何对象会影响另一个对象.我知道变量文本的值存储在两个对象的内存中的相同位置.

为什么变量的值是独立的但与对象相关?

另外,如果简单的赋值不能完成工作,如何复制SomeObject?

bri*_*ium 131

Java中的每个变量都是一个引用.所以,当你这样做

SomeClass s2 = s1;
Run Code Online (Sandbox Code Playgroud)

你只需指向s2与点相同的对象s1.您实际上是将参考s1(指向实例SomeClass)的值分配给s2.如果修改s1,s2也会被修改(因为它指向同一个对象).

原始类型有一个例外:int, double, float, boolean, char, byte, short, long.它们按值存储.因此,在使用时=,您只需指定值,但它们不能指向同一个对象(因为它们不是引用).这意味着

int b = a;
Run Code Online (Sandbox Code Playgroud)

只设置值b的值a.如果改变a,b不会改变.

在一天结束时,一切都是按值分配,它只是引用的值而不是对象的值(除了上面提到的基本类型).

所以在你的情况下,如果你想复制一份s1,你可以这样做:

SomeClass s1 = new SomeClass("first");
SomeClass s2 = new SomeClass(s1.getText());
Run Code Online (Sandbox Code Playgroud)

或者,您可以添加一个复制构造函数SomeClass,将实例作为参数并将其复制到自己的实例中.

class SomeClass {
  private String text;
  // all your fields and methods go here

  public SomeClass(SomeClass copyInstance) {
    this.text = new String(copyInstance.text);
  }
}
Run Code Online (Sandbox Code Playgroud)

有了这个,你可以很容易地复制一个对象:

SomeClass s2 = new SomeClass(s1);
Run Code Online (Sandbox Code Playgroud)

  • `s1.clone()`是_sometimes_一个选项. (15认同)
  • +1引用和基元按值复制.混淆是你不区分引用和引用的对象.;) (13认同)
  • Java中的每个变量都存储*值*.由于类是引用类型,因此类类型变量中的值是*reference*.当您按照本答案或问题中的第一个语句进行分配时,您正在复制*引用**值*.您没有通过引用复制任何内容. (4认同)
  • 你可以使用s1.clone()将对象复制到s2,它不会传递对s2的引用,而是将s1的数据复制到s2. (3认同)
  • @MayurRaiyani [为什么不使用java克隆方法](http://jcranky.com/2009/08/26/why-not-use-java-clone-method/) (3认同)

Eng*_*uad 39

@ brimborium的答案非常好(+1给他),但我只想用一些数字来详细说明.让我们先进行原始任务:

int a = new Integer(5);
int b = a;
b = b + b;
System.out.println(a); // 5 as expected
System.out.println(b); // 10 as expected
Run Code Online (Sandbox Code Playgroud)
int a = new Integer(5);
Run Code Online (Sandbox Code Playgroud)

1- First语句创建一个值为5的Integer对象.然后,在将其赋值给变量时a,Integer对象将被取消装箱并a作为基元存储.

创建Integer对象之后,在赋值之前:

在此输入图像描述

分配后:

在此输入图像描述

int b = a;
Run Code Online (Sandbox Code Playgroud)

2-这将只读取值a然后存储到其中b.

(Integer对象现在有资格进行垃圾回收,但此时不一定是垃圾回收)

在此输入图像描述

b = b + b;
Run Code Online (Sandbox Code Playgroud)

3-这将读取b两次的值,将它们加在一起,并将新值放入b.

在此输入图像描述


另一方面:

SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;
s2.setText("second");
System.out.println(s1.getText()); // second as UNexpected
System.out.println(s2.getText()); // second as expected
Run Code Online (Sandbox Code Playgroud)
SomeObject s1 = new SomeObject("first");
Run Code Online (Sandbox Code Playgroud)

1-创建一个新的SomeObject类实例,并将其分配给引用s1.

在此输入图像描述

SomeObject s2 = s1;
Run Code Online (Sandbox Code Playgroud)

2-这将使引用s2指向指向的对象s1.

在此输入图像描述

s2.setText("second");
Run Code Online (Sandbox Code Playgroud)

3-在引用上使用setter时,它将修改引用指向的对象.

在此输入图像描述

System.out.println(s1.getText());
System.out.println(s2.getText());
Run Code Online (Sandbox Code Playgroud)

4-两者都应该打印second,因为两个引用s1并且s2指的是同一个对象(如上图所示).

  • 很酷,为你的答案+1。虽然我想建议一些补充:1)图像可以小一点。2)您应该添加他的另一个问题的答案(如何复制`SomeObject`),也许还可以使用图像。 (2认同)

dav*_*vek 16

当你这样做

SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;
Run Code Online (Sandbox Code Playgroud)

你有2个对同一个对象的引用.这意味着无论您使用哪个参考对象,在使用第二个参考时,您所做的更改都将可见.

想象一下:你在房间里有一台电视机,但有两个遥控器:你使用哪个遥控器无关紧要,你仍然会对同一个底层物体(电视机)进行更改.

  • +1,我喜欢电视遥控器类比.我猜原始人是没有遥控器的电视,你必须亲自去实际的设置并改变价值......或者其他什么?:) (5认同)

Bri*_*new 10

当你写:

SomeObject s1 = new SomeObject("first");
Run Code Online (Sandbox Code Playgroud)

s1不是SomeObject.这是一个参考SomeObject对象.

因此,如果将s2分配给s1,则只需指定引用/句柄,底层对象也是相同的.这在Java中很重要.一切都是按值传递,但你永远不会传递对象 - 只引用对象.

因此,当您分配s1 = s2,然后调用一个方法s2更改一个对象时,底层对象将被更改,并且当您通过引用该对象时,该对象是可见的s1.

这是对象不变性的一个论据.通过使对象不可变,它们不会在您之下发生变化,从而以更可预测的方式运行.如果要复制对象,最简单/最实用的方法是编写一个copy()只创建新版本并在字段上复制的方法.你可以使用序列化/反射等来做聪明的副本,但这显然更复杂.