昨天我问了一个关于价值参考的问题,strange сopy values from one array to another,并认为在看到这段代码后我理解了答案:
public static void main(String[] args) {
String[] x = {"A"};
String[] y = x;
x[0] = "B";
System.out.print(x[0] + " " + y[0]);
}
然后我看到这个与第一个相同的例子:
public static void main(String[] args) {
String x = "A";
String y = x;
x = "B";
System.out.print(x + " " + y);
}
我不明白为什么在这个例子中,正确的答案是B A
,而不是B B
。我想,我宣布x
,然后y
引用x
。
答案 0 :(得分:4)
第一个例子:
您已声明String
数组,其唯一成员为"A"
。您声明另一个String
数组并为其分配x
。现在你有两个引用同一个数组的数组引用。您将内容更改为"B"
,因此它对两个数组引用都可见。您修改了唯一存在的数组对象的内容。
第二个例子:
您已声明String
,其内容为"A"
。您声明另一个String
并为其指定x
。现在您有两个字符串引用引用相同的String
。您将变量x
更改为"B"
,现在x
和y
指的是不同的字符串,因此您会看到“B A”。您没有更改原始字符串对象(String
是不可变的。)
答案 1 :(得分:1)
我认为关键的误解在:
我认为我宣布
x
,然后y
引用x
。
Java中的变量将引用保存到对象(和原语,但这在这里并不重要)。变量唯一可以引用的是对象。变量不是对象,因此没有任何东西可以引用它们。让我们更仔细地考虑两个代码示例。
在第一种情况下,有两个变量和一个数组:
public static void main(String[] args) {
String[] x = {"A"};
String[] y = x;
x[0] = "B";
System.out.print(x[0] + " " + y[0]);
}
考虑实际值是什么,以及什么东西只是引用,指针或句柄(各种)名称,可能有助于思考实际值。 x
和y
都是变量,这些变量的值是对象的引用(在本例中是字符串数组)。在这种情况下,x
的特定值与y
的值相同,它是对具有一个元素String "A"
的数组的引用。表达式x[0]
通过检索变量x
具有引用的数组并获取其第一个元素来计算。表达式y[0]
通过检索变量y
具有引用的数组并获取其第一个元素来计算。由于x
和y
包含对相同数组的引用,因此数组的第一个元素与其自身相同。打印输出为B B
。
在第二种情况下,有两个变量,没有数组。
public static void main(String[] args) {
String x = "A";
String y = x;
x = "B";
System.out.print(x + " " + y);
}
变量x
包含对字符串"A"
的引用。第一项作业String y = x;
使y
保持对同一字符串"A"
的引用。此时,如果你可以修改关于字符串的任何内容(但是你不能在Java中,因为字符串是不可变的),你可以使用x
或y
来做,你会看到结果是,因为两个变量都引用了单个对象。但是,第二个作业x = "B";
使x
的值成为对不同字符串"B"
的引用。它不会更改y
的值,它仍然是对字符串"A"
的引用。因此,评估表达式x
,并且由于x
包含对字符串"B"
的引用,因此结果为"B"
。评估表达式y
,并且由于y
包含对字符串"A"
的引用,因此结果为"A"
。打印输出为B A
。
也许关键是变量和对象是完全不同的。对象存在,期间。变量保存对象的引用。当你做
之类的事情x.doSomething();
您正在检索x
的值,该值是一个对象,然后调用该对象的doSomething()
方法。您可以检索变量引用的对象,并且可以使变量引用另一个对象(通过赋值)。这些是你可以对变量做的唯一的事情。你可以做的任何其他事情,你正在做一个对象。这在以下情况下尤为重要:
String[] x = {"A"}; ; there's an array {"Α"} and the variable `x` _refers_ to it
String[] y = x; ; retrieve the array to which `x` refers, and make `y` refer to it, too
x[0] = "B"; ; retrieve the array to which `x` refers (and to which `y` also refers)
; and make "B" its first element.
String x = "A"; ; there's a string "A", and the variable `x` _refers to it
String y = x; ; retrieve the string to which `x` refers, and may `y` refer to it, too
x = "B"; ; make `x` refer to the string "Β" (has no effect on what `y` refers to)
答案 2 :(得分:1)
String
个对象是不可变的,这就是你得到这种行为的原因。
在第二个示例中,您将“A”分配给变量x
,然后创建变量y
,该变量指向内存中与变量String
相同的x
。然后,您将变量x
更改为指向新的String
。但是,这不会影响变量y
;它仍然指向最初的String
。
答案 3 :(得分:1)
在第二个实例中,您将String设置为一个值,该值在Java中被视为基本类型。您正在进行永久性的一次性值分配。
在第一个实例中,当您将y分配给x时,您正在处理数组。数组是对内存中几个值的引用,所以当你说y = x时,你设置y使它引用与x相同的值。因此,x和y永远相等。
由此,x [0]和y [0]指向相同的精确值,如果更改了一个,则在两个引用中都会更改。
答案 4 :(得分:1)
在您的第一个示例中,行
String[] y = x;
将x设置为y。 但 x指的是内存地址。
所以当你打电话的时候
x[0] = "B";
您正在更改x
和y
引用的地址的值。
在第二个示例中,x
和y
不是引用类型。
所以当你打电话时
String y = x;
它取x
处的值并将其复制到y
,就像在第一个示例中一样,但由于x
和y
是值而不是地址,而不是它们是分开的。
所以当你打电话时
x = "B"
它根本不会影响y
的值。 y
仍为"A"
*技术上String
也是引用,只是它们是不可变的,因此行为类似于int
和float
等值类型。
答案 5 :(得分:1)
在
String[] x = {"A"};
您正在创建引用x
并将其分配给新创建的数组
x ---> ["A"]
下一步
String[] y = x;
您正在创建引用y
并为其指定值x
,因此它将引用包含x
的相同数组
x \
}--->["A"]
y /
不像
y -> x ---> ["A"]
现在您可以通过两个引用访问相同的数组。这意味着,如果您通过x
进行修改,则可以通过y
看到此修改,因为它是相同的数组,所以
x[0] = "B";
会做
x \
}--->["B"]
y /
这就是System.out.print(x[0] + " " + y[0]);
的结果为B B
现在在你的第二个例子中
String x = "A";
创建引用x
并为其指定字符串"A"
String y = x;
创建另一个引用y
并为其分配相同的x
值,这样它们将再次使用相同的对象
x \
}---> "A"
y /
但在
x = "B";
是没有像第一个示例中那样修改同一个对象,但更改引用x
以保存不同的字符串"B"
所以现在情况看起来像
x ---> "B"
y ---> "A"
这就是System.out.print(x + " " + y);
打印B A
。