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)
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指的是同一个对象(如上图所示).
dav*_*vek 16
当你这样做
SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;
Run Code Online (Sandbox Code Playgroud)
你有2个对同一个对象的引用.这意味着无论您使用哪个参考对象,在使用第二个参考时,您所做的更改都将可见.
想象一下:你在房间里有一台电视机,但有两个遥控器:你使用哪个遥控器无关紧要,你仍然会对同一个底层物体(电视机)进行更改.
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()只创建新版本并在字段上复制的方法.你可以使用序列化/反射等来做聪明的副本,但这显然更复杂.