2

昨天问了一个关于按值引用,奇怪的从一个数组到另一个数组的复制值的问题,看了这段代码,我以为我明白了答案:

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

4

6 回答 6

4

第一个例子:

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

第二个例子:

您已声明 a String,其内容为"A"。您声明另一个String并分配它x。现在您有两个引用相同的字符串引用String。您将变量更改x"B", 现在xy指的是不同的字符串,所以您会看到“B A”。您没有更改原始字符串对象(Strings 是不可变的)。

于 2013-10-30T18:58:41.063 回答
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.

于 2013-10-30T19:17:55.227 回答
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 中不能,因为字符串是不可变的),您可以使用xor来完成y,并且您会在其中看到结果,因为这两个变量都有引用单个对象。但是,第二个赋值x = "B";使 a 的成为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)
于 2013-10-30T18:59:03.327 回答
1

String对象是不可变的,这就是你得到这种行为的原因。

在您的第二个示例中,您将“A”分配给变量x,然后创建变量y,它指向与变量相同String的内存x。然后,您将变量更改x为指向新的String. 但是,这不会影响变量y;它仍然指向初始的String.

于 2013-10-30T18:59:16.693 回答
1

在您的第二个实例中,您将 String 设置为一个值,该值在 Java 中被视为原始类型。您正在执行一项永久性的一次性价值分配。

在第一种情况下,当您将 y 分配给 x 时,您正在处理数组。数组是对内存中多个值的引用,因此当您说 y = x 时,您正在设置 y,以便它引用与 x 相同的值。因此,x 和 y 永远彼此相等。

这样,x[0] 和 y[0] 指向相同的确切值,如果您更改一个,则两个引用都会更改。

于 2013-10-30T19:00:04.807 回答
1

在您的第一个示例中,该行

String[] y = x; 

将 x 设置为 y。 x 指的是内存地址。

所以当你打电话时

x[0] = "B";

您正在更改该地址的值,这两者都x引用y


在第二个示例中,x并且y不是引用类型。

所以当你打电话时

String y = x;

它获取 at 的值x并将其复制到y,就像在第一个示例中一样,但是因为xandy而不是address,所以它们是分开的。

所以当你打电话时

x = "B"

它根本不影响的价值yy遗迹"A"

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

于 2013-10-30T19:01:20.853 回答