我在不可变字符串上写了下面的代码。

public class ImmutableStrings {

    public static void main(String[] args) {
        testmethod();
    }

    private static void testmethod() {
        String a = "a";
        System.out.println("a 1-->" + a);
        a = "ty";
        System.out.println("a 2-->" + a);
    }
}

输出:

a 1-->a  
a 2-->ty

这里变量a的值被改变了(尽管许多人说不可变对象的内容不能被改变)。但是说字符串是不可变的到底是什么意思呢?你能给我解释一下这个话题吗?

来源:https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html


当前回答

可能上面提供的每个答案都是正确的,但我的答案是特定于使用hashCode()方法,以证明诸如,字符串…一旦创建就不能修改,修改后会在不同的内存位置产生新的值。

public class ImmutabilityTest {

    private String changingRef = "TEST_STRING";

    public static void main(String a[]) {

        ImmutabilityTest dn = new ImmutabilityTest();

        System.out.println("ChangingRef for TEST_STRING OLD : "
                + dn.changingRef.hashCode());

        dn.changingRef = "NEW_TEST_STRING";
        System.out.println("ChangingRef for NEW_TEST_STRING : "
                + dn.changingRef.hashCode());

        dn.changingRef = "TEST_STRING";
        System.out.println("ChangingRef for TEST_STRING BACK : "
                + dn.changingRef.hashCode());

        dn.changingRef = "NEW_TEST_STRING";
        System.out.println("ChangingRef for NEW_TEST_STRING BACK : "
                + dn.changingRef.hashCode());

        String str = new String("STRING1");
        System.out.println("String Class STRING1 : " + str.hashCode());

        str = new String("STRING2");
        System.out.println("String Class STRING2 : " + str.hashCode());

        str = new String("STRING1");
        System.out.println("String Class STRING1 BACK : " + str.hashCode());

        str = new String("STRING2");
        System.out.println("String Class STRING2 BACK : " + str.hashCode());

    }
}

输出

ChangingRef for TEST_STRING OLD : 247540830
ChangingRef for NEW_TEST_STRING : 970356767
ChangingRef for TEST_STRING BACK : 247540830
ChangingRef for NEW_TEST_STRING BACK : 970356767
String Class STRING1 : -1163776448
String Class STRING2 : -1163776447
String Class STRING1 BACK : -1163776448
String Class STRING2 BACK : -1163776447

其他回答

String S1="abc";
S1.concat("xyz");
System.out.println("S1 is", + S1);
String S2=S1.concat("def");
System.out.println("S2 is", + S2);

这表明一旦创建了一个字符串对象,它就不能被更改。每次你需要创建new并放入另一个String。年代

在进一步讨论不可变性之前,让我们在得出任何结论之前稍微了解一下String类及其功能。

这是String的工作原理:

String str = "knowledge";

这,像往常一样,创建一个包含“知识”的字符串,并为其分配一个引用str。简单吗?让我们执行更多的函数:

 String s = str;     // assigns a new reference to the same string "knowledge"

让我们看看下面的语句是如何工作的:

  str = str.concat(" base");

这将追加一个字符串“base”到str。但等一下,这是怎么可能的,因为字符串对象是不可变的?出乎你的意料,它是。

当执行上述语句时,虚拟机接受String str的值,即。“knowledge”并附加“base”,赋予我们“knowledge base”这个值。现在,由于字符串是不可变的,VM不能将这个值分配给str,所以它创建了一个新的String对象,给它一个值“知识库”,并给它一个引用str。

这里需要注意的一点是,虽然String对象是不可变的,但它的引用变量不是。这就是为什么在上面的例子中,引用被用来引用一个新形成的String对象。

在上面例子的这一点上,我们有两个String对象:第一个是我们用值"knowledge"创建的,由s指向,第二个是"knowledge base",由str指向。但是,从技术上讲,我们有三个String对象,第三个是concat语句中的文字"base"。

关于字符串和内存使用的重要事实

如果我们对“知识”没有另一种指称会怎样?我们会失去那个字符串。然而,它仍然存在,但由于没有参考资料,将被视为丢失。 下面再看一个例子

String s1 = "java";
s1.concat(" rules");
System.out.println("s1 refers to "+s1);  // Yes, s1 still refers to "java"

发生了什么:

第一行非常简单:创建一个新的String "java",并将s1引用给它。 接下来,虚拟机创建另一个新的字符串“java rules”,但是什么都没有 指它。因此,第二个字符串立即丢失。我们够不到 它。

引用变量s1仍然引用原始字符串“java”。

几乎每个应用于String对象以修改它的方法都会创建新的String对象。那么,这些String对象在哪里呢?这些都存在于内存中,任何编程语言的关键目标之一就是有效地利用内存。

随着应用程序的增长,字符串字面值占用大量内存是很常见的,这甚至会导致冗余。因此,为了使Java更高效,JVM留出了一个特殊的内存区域,称为“字符串常量池”。

当编译器看到String字面值时,它会在池中查找String。如果找到匹配,则对新文字的引用将指向现有的String,并且不会创建新的String对象。现有的String只是多了一个引用。下面是使String对象不可变的要点:

在String常量池中,一个String对象可能有一个或多个引用。如果几个引用指向同一个String而不知道它,如果其中一个引用修改了该String值,那就不好了。这就是为什么String对象是不可变的。

现在你可以说,如果有人重写了String类的功能呢?这就是String类被标记为final的原因,这样任何人都不能重写其方法的行为。

字符串是不可变的,这意味着你不能改变对象本身,但是你可以改变对对象的引用。

当你执行a = "ty"时,你实际上是在改变a对一个由String字面值"ty"创建的新对象的引用。

改变一个对象意味着使用它的方法来改变它的一个字段(或者字段是公共的而不是final的,这样它们就可以从外部更新而不需要通过方法访问它们),例如:

Foo x = new Foo("the field");
x.setField("a new field");
System.out.println(x.getField()); // prints "a new field"

而在一个不可变类(声明为final,以防止通过继承修改)(它的方法不能修改它的字段,而且字段总是私有的,建议是final),例如String,你不能改变当前的String,但你可以返回一个新的String,即:

String s = "some text";
s.substring(0,4);
System.out.println(s); // still printing "some text"
String a = s.substring(0,4);
System.out.println(a); // prints "some"

If some object bar holds a reference to a mutable object foo and encapsulates some of its state in mutable aspects of foo's state, that will allow code which can change those aspects of foo to change the corresponding aspects of bar's state without actually touching bar or even knowing of its existence. Generally, this means that objects which encapsulate their own state using mutable objects must ensure that no references to those objects are exposed to any code which might unexpectedly mutate them. By contrast, if bar holds a reference to an object moo and only uses immutable aspects of moo other than identity to encapsulate its state, then bar can freely expose moo to outside code without worrying about anything the outside code might do to it.

不可变对象是创建后状态不能修改的对象。

a = "ABC" <——immutable object。"a"保存对对象的引用。 并且,a = "DEF" <——另一个不可变对象,"a"现在保存着对它的引用。

一旦你分配了一个字符串对象,该对象就不能在内存中被改变。

总之,您所做的就是将“a”的引用更改为一个新的字符串对象。