为什么下面的例子似乎反驳了 String 是 Java 中不可变对象的说法?

发布于 2024-11-29 23:21:46 字数 493 浏览 3 评论 0原文

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

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

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

I am using the OpenJDK Java compiler under Ubuntu. I wanted to convert a character array to a string and when that seemed to have ended up giving ambiguous results, I tried to write a toString method of my own. In the process, I wrote a test program wherein (out of the fun of it) I tried to compile the following code.

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

Now, I know that String objects in Java are immutable and the code should have in fact generated an error but to my surprise, it printed abcbcd to the console. Does this mean that String objects in Java are mutable or is there something wrong with the implementation of OpenJDK compiler in this case?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

相权↑美人 2024-12-06 23:21:46

您上面发布的代码实际上不会改变任何字符串,尽管看起来确实如此。原因是该行不会改变字符串:

string = string + "bcd";

相反,它的作用是:

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

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

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

string += "bcd";

看起来更强烈的样子,就好像它将 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);
    }
}

最后一点 - 对于新 Java 程序员来说,使用 String 时另一个常见的原因是 String 具有看似会改变字符串的方法,但实际上并没有。例如,此代码无法正常工作:

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

此处,对 s.toLowerCase() 的调用实际上并未将字符串的字符转换为小写,而是生成具有字符集的新字符串为小写。如果您随后将代码重写为

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

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

希望这有帮助!

The code that you've posted above does not actually mutate any strings, though it looks like it does. The reason is that this line doesn't mutate the string:

string = string + "bcd";

Instead, what this does is:

  1. Construct a new string whose value is string + "bcd".
  2. Change what string is referenced by string to refer to this new string.

In other words, the actual concrete string objects themselves weren't changed, but the references to those strings were indeed modified. Immutability in Java usually means that objects cannot be modified, not the references to those objects.

An important detail that confuses a lot of new Java programmers is that the above line is often written as

string += "bcd";

which looks even more strongly as though it's concatenating bcd onto the end of the string and thereby mutating it, even though it's equivalent to the above code and therefore doesn't cause any changes to the actual String object (again, it works by creating a new String object and changing what object the reference refers to.)

To see that what's going on here is that you're actually changing the reference and not the string it refers to, you can try rewriting the code to make string final, which prevents you from changing what object is referenced. If you do so, you'll find that the code no longer compiles. For example:

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

One final note - another common cause of grief for new Java programmers when using Strings is that String has methods that appear to mutate the string but in actuality do not. For example, this code does not work correctly:

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

Here, the call to s.toLowerCase() does not actually convert the characters of the string to lower case, but instead produces a new string with the characters set to lower case. If you then rewrite the code as

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

Then the code will behave properly. Again, the key detail here is that the assignment to s does not change any concrete String object, but just adjusts what object s refers to.

Hope this helps!

晌融 2024-12-06 23:21:46

不,没有错误 - 您没有更改任何字符串对象的内容。

您正在更改完全不同的字符串变量的值。将其视为两个操作:

  • 创建一个新字符串,即表达式 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;

区分变量对象非常重要。变量的值只是一个引用或原始类型值。更改变量的值不会更改它先前引用的对象的内容。

Nope, no error - you're not changing the contents of any string object.

You're changing the value of a string variable which is entirely different. Look at this as two operations:

  • Creating a new string, the result of the expression string + "bcd"
  • Assigning the reference to the new string back to the string variable

Let's separate them out explicitly:

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;

It's very important to distinguish between variables and objects. The value of a variable is only ever a reference or a primitive type value. Changing the value of a variable does not change the contents of the object to which it previously referred.

ゞ记忆︶ㄣ 2024-12-06 23:21:46

区别在于对象的引用和对象本身。

String XXX = "xxx";

方法:
创建一个新变量并将引用分配给包含文字字符串“xxx”的对象 String 的实例。

XXX = XXX + "yyy";

含义:

获取对名为 XXX 的变量中的对象的引用。
创建一个 String 类型的新对象,其中包含字符串文字“yyy”。
执行字符串 + 运算符将它们添加在一起。
此操作将创建一个新的 String 对象,其中包含文字字符串“xxxyyy”。
完成这一切之后,我们再次将对新对象的引用放入变量 XXX 中。

包含“xxx”的旧引用对象不再使用,但其内容从未被修改。

作为反证,有一个例子:

String a = "abc";
String b = "def";
String c = a;

a = a + b;

System.out.println(a); // will print "abcdef".
System.out.println(b); // will print "def".
System.out.println(c); // will print "abc".

// Now we compare references, in java == operator compare references, not the content of objects.

System.out.println(a == a); // Will print true
System.out.println(a == c); // Will print false, objects are not the same!

a = c;

System.out.println(a == c); // Will print true, now a and b points on the same instance.

对象实例是存在于程序内存的一部分中的“抽象”东西。引用变量是对该内存部分的引用。
您只能通过变量(或返回值)访问对象。

一个对象可以有多个指向它的变量。

String 是不可变的意味着您无法修改 String 对象使用的内存区域的内容,但是当然,您可以根据需要自由更改和交换对其的引用。

The difference is between References to an object and the object itself.

String XXX = "xxx";

Means:
Make a new variable and assign the reference to an instance of object String that contains the literal string "xxx".

XXX = XXX + "yyy";

Means:

Get the reference to the object we have in variable named XXX.
Make a new object of type String that contains the string literal "yyy".
Add them together executing the string + operator.
This operation will create a new String object that contains the literal string "xxxyyy".
After all this, we put the reference to the new object again in the variable XXX.

The old referenced object containing "xxx" is not used anymore but the content of it was never modified.

As a counter proof, there is an example:

String a = "abc";
String b = "def";
String c = a;

a = a + b;

System.out.println(a); // will print "abcdef".
System.out.println(b); // will print "def".
System.out.println(c); // will print "abc".

// Now we compare references, in java == operator compare references, not the content of objects.

System.out.println(a == a); // Will print true
System.out.println(a == c); // Will print false, objects are not the same!

a = c;

System.out.println(a == c); // Will print true, now a and b points on the same instance.

Object instance are something "abstract" that live in a portion of memory of your program. A reference variable is a reference to that portion of memory.
You can access objects only through variables (or return values).

A single object can have more than one variable pointing at it.

String is immutable means that you cannot modify the content of that area of memory used by the String object, but of course, you are free to change and exchange references to it as you prefer.

情独悲 2024-12-06 23:21:46

它并不反驳它。它实际上不会编译,因为 string 没有声明为 String 对象。但是,假设您的意思是:

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

请参阅 + 运算符创建一个新字符串,并保持“abc”不变。原来的“abc”仍然存在,但您实际上所做的只是创建一个新字符串“abcbcd”,并在执行以下操作时覆盖对“abc”的原始引用:string = string +“bcd”。如果您将该代码更改为这样,您就会明白我的意思:

class toString {
    public static void main(String[] args ) {
        String originalString = "abc";
        String newString = originalString + "bcd";

        System.out.println( originalString );  // prints the original "abc";
        System.out.println( newString );       // prints the new string "abcbcd";
    }
}

It doesn't refute it. It actually won't compile since string isn't declared as a String object. But, let's say you meant:

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

See the + operator creates a new String leaving "abc" in tact. The original "abc" still exists, but all you've actually done is create a new string "abcbcd" and overwrite the original reference to "abc" when doing: string = string + "bcd". If you changed that code to this you'll see what I mean:

class toString {
    public static void main(String[] args ) {
        String originalString = "abc";
        String newString = originalString + "bcd";

        System.out.println( originalString );  // prints the original "abc";
        System.out.println( newString );       // prints the new string "abcbcd";
    }
}
离线来电— 2024-12-06 23:21:46

String 对象不可变的,您只需将 string 的值重新分配给 string + "bcd"你的代码示例。您不是修改现有的String对象,而是创建一个新对象并将其分配给旧名称。

String objects are immutable, you are simply re-assigning the value of string to string + "bcd" in your code example. You are not modifying the existing String object, you are creating a new one and assigning it to the old name.

半衬遮猫 2024-12-06 23:21:46

string = string + "bcd"String 的新实例设置为变量 string,而不是修改该对象

string = string + "bcd" sets a new instance of String to the variable string, rather than modify that object

梦回梦里 2024-12-06 23:21:46

它所做的就是创建一个新对象并替换旧对象。
如果您想要可变字符串,请查看字符串生成器。

what this will do is that it will make a new object, and replace the old one.
if you want mutable string look at string builder.

天气好吗我好吗 2024-12-06 23:21:46

String 变量 string 本身是可变的。

String 对象 "abc" 是不可变的,字符串对象 "bcd" 也是如此,并且结果连接,“abbcbcd”。最后的结果被分配给变量。

在执行该代码片段时,没有字符串发生变化。

The String variable string itself is mutable.

The String object "abc" is immutable, as is the String object "bcd", and the result of the concatenation, "abcbcd". That last result is assigned to the variable.

No String was mutated in the execution of that code snipped.

诠释孤独 2024-12-06 23:21:46

该字符串是不可变的...您的示例所做的只是表明您可以将新的字符串引用分配给变量。

如果我们编译代码,并稍微更改它:

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

您将看到“abc”,然后是“abcbcd”,这可能会让您认为字符串已更改,但实际上没有更改。

当您执行 string = /* 不管 */ 时,您将用新值覆盖 string 变量中原来的内容。

如果 String 有一个方法,例如 setCharAt(int index, char value) 那么它就是可变的。

The string is immutable... all your example does is show that you can assign a new string reference to a variable.

If we make the code compile, and change it slightly:

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

You will see "abc" and then "abcbcd" which, could lead you to think that the string has changed, but it has not.

When you do the string = /* whatever */ you are overwriting what used to be in the variable called string with a new value.

If String had a method, such as setCharAt(int index, char value) then it would be mutable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文