考虑下面的例子。

String str = new String();

str  = "Hello";
System.out.println(str);  //Prints Hello

str = "Help!";
System.out.println(str);  //Prints Help!

在Java中,String对象是不可变的。那么为什么对象str可以被赋值为"Help!"呢?这难道不是与Java中字符串的不变性相矛盾吗?有人能给我解释一下不变性的确切概念吗?

编辑:

好的。我现在明白了,但还有一个问题。下面的代码呢:

String str = "Mississippi"; 
System.out.println(str); // prints Mississippi 

str = str.replace("i", "!"); 
System.out.println(str); // prints M!ss!ss!pp! 

这是否意味着将再次创建两个对象(“Mississippi”和“M!ss!ss!pp!”),并且在replace()方法之后引用str指向不同的对象?


当前回答

这里的不可变性意味着实例可以指向其他引用,但字符串的原始内容不会在原始引用处被修改。 让我用你举的第一个例子来解释。 第一个str指向“Hello”,这是可以的。 第二次它指向“救命!”。 这里str开始指向“Help!”,“Hello”字符串的引用丢失了,我们无法恢复。

事实上,当str试图修改现有内容时,将生成另一个新字符串,str将开始指向该引用。 所以我们看到,在原始引用处的字符串没有被修改,但在它的引用处是安全的,对象的实例开始指向不同的引用,所以不变性是保留的。

其他回答

Use:

String s = new String("New String");
s.concat(" Added String");
System.out.println("String reference -----> "+s); // Output: String reference -----> New String

如果你看到这里我使用concat方法来改变原始字符串,即“新字符串”与字符串“添加的字符串”,但我仍然得到了输出,因此它证明了你不能改变string类对象的引用,但如果你通过StringBuilder类做这件事,它将工作。它列在下面。

StringBuilder sb = new StringBuilder("New String");
sb.append(" Added String");
System.out.println("StringBuilder reference -----> "+sb);// Output: StringBuilder reference -----> New String Added String

对于那些想知道如何在Java中打破字符串不变性的人…

Code

import java.lang.reflect.Field;

public class StringImmutability {
    public static void main(String[] args) {
        String str1 = "I am immutable";
        String str2 = str1;

        try {
            Class str1Class = str1.getClass();
            Field str1Field = str1Class.getDeclaredField("value");

            str1Field.setAccessible(true);
            char[] valueChars = (char[]) str1Field.get(str1);

            valueChars[5] = ' ';
            valueChars[6] = ' ';

            System.out.println(str1 == str2);
            System.out.println(str1);
            System.out.println(str2);           
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

输出

true
I am   mutable
I am   mutable

超级晚的答案,但想把一个简洁的消息从作者的String类在Java

字符串是常量;它们的值在被修改之后就不能再修改了 创建。字符串缓冲区支持可变字符串。因为字符串 对象是不可变的,它们可以被共享。

它可以从这个文档中导出,任何改变字符串的东西,都会返回不同的对象(可以是新的或旧的)。 关于这一点不那么微妙的提示应该来自函数签名。 想想看,为什么他们让一个对象上的函数返回一个对象而不是状态?

public String replace(char oldChar, char newChar) 

还有一个来源使这种行为显式(从替换函数文档)

返回一个新字符串,该字符串由替换所有出现的 oldChar和newChar。

来源:https://docs.oracle.com/javase/7/docs/api/java/lang/String.html取代(char、% 20字符)

作者李·博因顿 作者阿瑟·范霍夫 作者马丁·布赫兹 作者乌尔夫·齐比斯

来源:JavaDoc of String。

不可变性意味着实例化对象的值不能改变,你永远不能把“Hello”变成“Help!”

变量str是一个对象的引用,当你给str赋一个新值时,你没有改变它引用的对象的值,你引用的是一个不同的对象。

字符串不会改变,但对它的引用会改变。你混淆了不可变性和final字段的概念。如果一个字段被声明为final,那么一旦它被赋值,就不能重新赋值。