两个相同的程序产生不同的结果

时间:2013-10-30 18:55:34

标签: java

昨天我问了一个关于价值参考的问题,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

6 个答案:

答案 0 :(得分:4)

第一个例子:

您已声明String数组,其唯一成员为"A"。您声明另一个String数组并为其分配x。现在你有两个引用同一个数组的数组引用。您将内容更改为"B",因此它对两个数组引用都可见。您修改了唯一存在的数组对象的内容。

第二个例子:

您已声明String,其内容为"A"。您声明另一个String并为其指定x。现在您有两个字符串引用引用相同的String。您将变量x更改为"B",现在xy指的是不同的字符串,因此您会看到“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]);
}

考虑实际值是什么,以及什么东西只是引用,指针或句柄(各种)名称,可能有助于思考实际值。 xy都是变量,这些变量的是对象的引用(在本例中是字符串数组)。在这种情况下,x的特定值与y的值相同,它是对具有一个元素String "A"的数组的引用。表达式x[0]通过检索变量x具有引用的数组并获取其第一个元素来计算。表达式y[0]通过检索变量y具有引用的数组并获取其第一个元素来计算。由于xy包含对相同数组的引用,因此数组的第一个元素与其自身相同。打印输出为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中,因为字符串是不可变的),你可以使用xy来做,你会看到结果是,因为两个变量都引用了单个对象。但是,第二个作业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";

您正在更改xy引用的地址的值。


在第二个示例中,xy不是引用类型。

所以当你打电话时

String y = x;

它取x处的值并将其复制到y,就像在第一个示例中一样,但由于xy而不是地址,而不是它们是分开的。

所以当你打电话时

x = "B"

它根本不会影响y的值。 y仍为"A"

*技术上String也是引用,只是它们是不可变的,因此行为类似于intfloat等值类型。

答案 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

的原因