为什么以下示例似乎反驳Strings是Java中的不可变对象?

sid*_*rma 4 java string mutable

我在Ubuntu下使用OpenJDK Java编译器.我想将一个字符数组转换为一个字符串,当它似乎最终给出了模糊的结果时,我试着写一个toString我自己的方法.在这个过程中,我写了一个测试程序,其中(出于乐趣)我尝试编译以下代码.

class toString{
    public static void main(String[] args){
        string = "abc";
        string = string + "bcd";
        System.out.println(string);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,我知道StringJava中的对象是不可变的,代码实际上应该生成错误但令我惊讶的是,它打印abcbcd到控制台.这是否意味着StringJava中的对象是可变的,或者在这种情况下OpenJDK编译器的实现是否有问题?

tem*_*def 20

您上面发布的代码实际上并没有改变任何字符串,尽管它看起来像.原因是这一行不会改变字符串:

string = string + "bcd";
Run Code Online (Sandbox Code Playgroud)

相反,它的作用是:

  1. 构造一个值为的新字符串string + "bcd".
  2. 更改引用的字符串string以引用此新字符串.

换句话说,实际的具体字符串对象本身没有改变,但确实修改了对这些字符串的引用.Java中的不变性通常意味着不能修改对象,而不能修改对这些对象的引用.

令许多新的Java程序员感到困惑的一个重要细节是,上面的代码经常被写成

string += "bcd";
Run Code Online (Sandbox Code Playgroud)

看起来更强烈,好像它连接bcd到字符串的末尾,从而改变它,即使它等同于上面的代码,因此不会导致对实际String对象的任何更改(同样,它通过创建一个新String对象来工作并改变引用所指的对象.)

要看到这里发生的是你实际上是在更改引用而不是它所引用的字符串,你可以尝试重写要生成的代码string final,这可以防止你更改引用的对象.如果你这样做,你会发现代码不再编译.例如:

class toString{
    public static void main(String[] args){
        final String string = "abc";
        string = string + "bcd";    // Error: can't change string!
        System.out.println(string);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一点 - 使用Strings 时新Java程序员悲伤的另一个常见原因是,String有些方法似乎会改变字符串,但实际上却没有.例如,此代码无法正常工作:

String s = "HELLO, WORLD!";
s.toLowerCase(); // Legal but incorrect
System.out.println(s); // Prints HELLO, WORLD!
Run Code Online (Sandbox Code Playgroud)

这里,调用s.toLowerCase()实际上并不将字符串的字符转换为小写,而是生成一个字符串设置为小写的新字符串.如果你然后重写代码

String s = "HELLO, WORLD!";
s = s.toLowerCase();   // Legal and correct
System.out.println(s); // Prints hello, world!
Run Code Online (Sandbox Code Playgroud)

然后代码将正常运行.同样,这里的关键细节是赋值s不会改变任何具体String对象,而只是调整对象s引用的对象.

希望这可以帮助!


Jon*_*eet 5

不,没有错误 - 你没有改变任何字符串对象的内容.

您正在更改完全不同的字符串变量的值.看看这是两个操作:

  • 创建一个新字符串,表达式的结果 string + "bcd"
  • 将对新字符串的引用分配回string变量

让我们明确地将它们分开:

String string = "abc";
String other = string + "bcd";

// abc - neither the value of string nor the object's contents have changed
System.out.println(string); 

// This is *just* changing the value of the string variable. It's not making
// any changes to the data within any objects.
string = other;
Run Code Online (Sandbox Code Playgroud)

这是非常区分重要的变量对象.变量的值只是引用或基本类型值.更改变量的值不会更改它先前引用的对象的内容.