【问题标题】:Java String variable setting - reference or value?Java字符串变量设置-引用还是值?
【发布时间】:2011-04-29 17:39:13
【问题描述】:

以下 Java 代码段来自 AP 计算机科学实践考试。

String s1 = "ab";
String s2 = s1;
s1 = s1 + "c";
System.out.println(s1 + " " + s2);

这段代码的输出是 BlueJ 上的“abc ab”。但是,可能的答案选择之一是“abc abc”。答案可能取决于 Java 是否将字符串引用设置为原始类型(按值)或对象(按引用)。

为了进一步说明这一点,让我们看一个基本类型的示例:

int s1 = 1;
int s2 = s1; // copies value, not reference
s1 = 42;

System.out.println(s1 + " " + s2); // prints "1 42"

但是,假设我们有持有余额的 BankAccount 对象

BankAccount b1 = new BankAccount(500); // 500 is initial balance parameter
BankAccount b2 = b1; // reference to the same object
b1.setBalance(0);
System.out.println(b1.getBalance() + " " + s2.getBalance()); // prints "0 0"

我不确定字符串是哪种情况。它们在技术上是对象,但我的编译器在将变量设置为彼此时似乎将它们视为原始类型。

如果 Java 传递像原始类型这样的字符串变量,答案是“abc ab”。但是,如果 Java 将 String 变量视为对任何其他 Object 的引用,则答案将是“abc abc”

你认为哪个是正确答案?

【问题讨论】:

  • 您查看过 String 的文档吗?它是说是类还是原语?
  • 您可能想阅读javadoc,因为它会回答您的问题。
  • System.out.println(s1 + " " + s2); // prints "1 42" 不是真的......它打印"42 1"
  • @whytheq 我得到“42 1”作为我的输出。我读了这个,我就像WTF? s2 在重新分配 s1 之前已经复制了值 42.

标签: java string variables reference


【解决方案1】:

java 字符串是不可变的,因此您的重新分配实际上会导致您的变量指向 String 的新实例,而不是更改 String 的值。

String s1 = "ab";
String s2 = s1;
s1 = s1 + "c";
System.out.println(s1 + " " + s2);

在第 2 行,s1 == s2 AND s1.equals(s2)。在第 3 行连接后,s1 现在引用了一个不同的实例,其值为“abc”,因此既不是 s1==s2 也不是 s1.equals(s2)。

【讨论】:

  • 确实如此,但实际上并不能回答 OP 更基本的问题。
  • 当然可以。它隐含地解释了他对 String 案例与 BankAccount 的比较是不一样的。一种是分配一个新的实例,而另一种只是简单地修改一个现有的实例。为清楚起见 s1=s1+"c" 基本上等同于 b=new StringBuilder(s1);b.append('c');s1=b.toString();
【解决方案2】:

java.lang.String 是一个对象,而不是原语。

第一个例子中的代码做了什么:

  1. 将 s1 定义为“ab”
  2. 将 s2 设置为与 s1 相同的底层对象
  3. 将 s1 设置为新字符串,该字符串是 s1 的旧值和“c”的组合

但要回答您关于参考或价值的问题,这是通过参考。

【讨论】:

    【解决方案3】:

    String 一个类,所以String变量一个引用。但它是一种语言固有的,从某种意义上说,Java 对其有特殊的处理和语法,这就是为什么你可以做像你的例子这样的事情。

    参见例如http://download.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html.

    【讨论】:

      【解决方案4】:

      在 Java 中,String 对象被分配并传递引用;在这方面,它们的行为与任何其他对象完全一样。

      但是,Strings 是不可变的:没有一个操作可以修改现有字符串的值,而无需创建新对象。例如,这意味着s1 = s1 + "c" 创建了一个新对象,并将存储在s1 中的引用替换为对该新对象的引用。

      【讨论】:

        【解决方案5】:

        您的 BankAccount 和 String 之间的区别在于 String 是不可变的。没有“setValue()”或“setContent()”之类的东西。您的银行帐户的等效示例是:

        BankAccount b1 = new BankAccount(500); // 500 is initial balance parameter
        BankAccount b2 = b1; // reference to the same object
        b1 = new BankAccount(0);
        System.out.println(b1.getBalance() + " " + s2.getBalance()); // prints "0 500"
        

        因此,如果您以这种方式考虑(实际上不是编译器所做的,而是在功能上等效),字符串连接场景是:

        String s1 = "ab";
        String s2 = s1;
        s1 = new String("abc");
        System.out.println(s1 + " " + s2); //prints "abc ab"
        

        【讨论】:

          【解决方案6】:

          String 是否被视为原始对象或对象无关紧要!

          在 String 示例中,两个字符串的连接会产生一个新的 String 实例,然后将其分配给 s1。变量 s2 仍然引用未更改的(!)旧 String 实例。

          假设 BankAccount 有一个设置余额的方法,该方法返回一个新的 BankAccount,您的示例可能如下所示:

          BankAccount b1 = new BankAccount(500); // 500 is initial balance parameter
          BankAccount b2 = b1; // reference to the same object
          b1 = b1.createNewAccountWithBalance(0); // create and reference a new object
          System.out.println(b1.getBalance() + " " + b2.getBalance()); // prints "0 500"
          

          【讨论】:

            【解决方案7】:

            断言

            如果 Java 将字符串变量视为对任何其他对象的引用,则答案将是“abc abc”

            不正确。 Java 确实将字符串变量视为对任何其他对象的引用。 Strings 是对象,但答案仍然是“abc ab”。

            问题不在于赋值运算符的作用。在您的示例中,赋值运算符在每种情况下都分配对 String 对象的引用。

            问题在于连接运算符 ('+') 的作用。它创建一个新的 String 对象。正如其他人所说,这是必要的,因为 String 对象是不可变的,但这是操作员行为的问题,而不仅仅是因为 String 是不可变的。即使 String 对象是可变的,连接运算符也可以返回一个新的对象。

            相比之下,在您的第二个示例中,b1.setBalance(0) 不会创建新对象,它会修改现有对象。

            【讨论】:

              【解决方案8】:

              确实,String 是一个类,它是通过引用分配/传递的。 但令人困惑的是声明:

              String s = "abc";
              

              这表明 String 是一个原始的(如 'int x = 10;' ); 但这只是一个捷径,语句 'String s = "abc";'实际上编译为'String s = new String( "abc" );' 就像'Integer x = 10;'被编译为'Integer x = new Integer( 10 );'

              这种机制称为“装箱”。

              更令人困惑的是:有一个类'Integer'和一个原始的'int', 但 String 没有原始等价物(尽管 char[] 很接近)

              Sije de Haan

              【讨论】:

                【解决方案9】:
                int s1 = 1;
                int s2 = s1; // copies value, not reference
                s1 = 42;
                
                System.out.println(s1 + " " + s2); // prints "1 42"
                

                不打印"1 42",但"42 1"。考虑每个离散行。首先s1分配1,然后s2分配s1,到现在为止是1(假设java还没有看到第三行。 ) 然后 java 看到第三行并立即将 s1 更改为 42。之后 java 被告知打印它到目前为止所知道的,即 s1 是 42 而 s2 是 1(旧的 s1)。

                至于字符串也是如此。

                String s1 = "ab";
                String s2 = s1;
                s1 = s1 + "c";
                System.out.println(s1 + " " + s2);// prints "abc ab".
                

                Fort String 它不一定会改变 s1,而是 s1 现在引用堆内存中的新 String 对象,但旧的“ab”对象仍然存在,新引用 s2!

                【讨论】:

                  猜你喜欢
                  • 2017-10-11
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-05-08
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-11-07
                  • 1970-01-01
                  相关资源
                  最近更新 更多