【问题标题】:two of the same program produces different results两个相同的程序产生不同的结果
【发布时间】:2013-10-30 18:55:34
【问题描述】:

昨天我问了一个关于按值引用的问题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

【问题讨论】:

  • x[0] = "B" 通过替换索引 0 处的对象来修改现有对象 xx = "B" 重新分配并因此替换 x
  • String[]String 在 Java 中并不相同。

标签: java


【解决方案1】:

第一个例子:

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

第二个例子:

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

        在您的第一个示例中,行

        String[] y = x; 
        

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

        所以当你打电话时

        x[0] = "B";
        

        您正在更改该地址的值,xy 都引用了该地址。


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

        所以当你打电话时

        String y = x;
        

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

        所以当你打电话时

        x = "B"
        

        它根本不影响y 的值。 y 仍然是 "A"

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

        【讨论】:

          【解决方案5】:

          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

          【讨论】:

            【解决方案6】:

            我认为关键的误解体现在:

            我认为我声明了x,然后y 指的是x

            Java 中的变量持有 referencesobjects(和原语,但这在这里并不重要)。唯一可以被变量引用的是对象。变量不是对象,因此没有任何东西可以引用它们。让我们更仔细地考虑这两个代码示例。

            第一种情况,有两个变量和一个数组:

            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 的值相同,它是对具有一个元素的数组的引用,即字符串"A"。表达式x[0] 是通过检索变量x 具有引用的数组并获取其第一个元素来计算的。表达式y[0] 是通过检索变量y 具有引用的数组并获取其第一个元素来计算的。因为xy 持有对same 数组的引用,所以数组的第一个元素与其自身相同。打印输出为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 进行修改,您会看到结果,因为 both 变量都引用了一个 single 对象。然而,第二个赋值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)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-08-25
              • 2019-02-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多