字符串是不可变的.究竟是什么意思?

Anu*_*lan 194 java string immutability

我在不可变字符串上编写了以下代码.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

a 1-->a  
a 2-->ty
Run Code Online (Sandbox Code Playgroud)

这里变量的值a已经改变(许多人说不可变对象的内容不能改变).但究竟是什么意思说String是不可改变的?你能否为我澄清一下这个话题?

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

rog*_*hat 397

在继续进行不可变性的大惊小怪之前,让我们先看看String课程及其功能,然后再得出任何结论.

这是如何String工作:

String str = "knowledge";
Run Code Online (Sandbox Code Playgroud)

像往常一样,这会创建一个包含"knowledge"并为其指定引用的字符串str.够简单吗?让我们执行更多功能:

 String s = str;     // assigns a new reference to the same string "knowledge"
Run Code Online (Sandbox Code Playgroud)

让我们看看以下声明如何工作:

  str = str.concat(" base");
Run Code Online (Sandbox Code Playgroud)

这将一个字符串追加" base"str.但是等等,这怎么可能,因为String对象是不可变的?令你惊讶的是,它是.

执行上述语句时,VM将获取值String str,即"knowledge"并附加" base",为我们提供值"knowledge base".现在,由于Strings是不可变的,因此VM无法将此值赋给str,因此它会创建一个新String对象,为其赋值"knowledge base",并为其提供引用str.

这里需要注意的一点是,虽然String对象是不可变的,但它的引用变量却不是.因此,在上面的例子中,引用是为了引用新形成的String对象.

在上面的例子中,我们有两个String对象:第一个是我们使用value创建的"knowledge",指向的s,第二个是"knowledge base"指向的str.但是,从技术上讲,我们有三个String目标,第三个是字面"base"concat声明.

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

如果我们没有什么其他的参考s"knowledge"?我们会失去这一点String.但是,它仍然存在,但由于没有引用而被视为丢失.再看下面的一个例子

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

发生了什么:

  1. 第一行非常简单:创建一个新的String "java"并引用s1它.
  2. 接下来,VM创建另一个新的String "java rules",但没有任何引用它.所以,第二个String瞬间就会丢失.我们无法达到它.

引用变量s1仍然指原始变量String "java".

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

随着应用程序的增长,文字占据大面积内存是非常常见的String,这甚至会导致冗余.因此,为了提高Java的效率,JVM预留了一个称为"字符串常量池"的特殊内存区域.

当编译器看到String文字时,它会String在池中查找.如果找到匹配项,则对新文本的引用将定向到现有文本,String并且不会String创建任何新对象.现有的String还有一个参考.以下是使String对象不可变的观点:

String常量池中,String对象可能具有一个或多个引用.如果多个引用指向同一个引用,String甚至不知道它,如果其中一个引用修改了该String值,则会很糟糕.这就是String对象不可变的原因.

那么,现在你可以说,如果有人改写了String类的功能会怎么样?就是这样的原因String类被标记final,这样没有人可以凌驾于其方法的行为.

  • "如果几个引用指向同一个String而不知道它,那么如果其中一个引用修改了String值就不好了.这就是String对象不可变的原因." AND"如果有人覆盖String类的功能会怎么样?这就是String类被标记为final的原因,这样任何人都无法覆盖其方法的行为." 很好的解释! (18认同)
  • 我认为这个答案甚至比接受的答案更好! (16认同)
  • 这是最好的答案 (9认同)
  • 哇.对我来说,这是最好的解释.谢谢你分享知识.. (3认同)

Eng*_*uad 171

String是不可变的意味着您无法更改对象本身,但您可以更改对象的引用.

执行时a = "ty",实际上是在更改a由String文本创建的新对象的引用"ty".

更改对象意味着使用其方法更改其中一个字段(或者字段是公共字段而不是最终字段,以便可以从外部更新它们而无需通过方法访问它们),例如:

Foo x = new Foo("the field");
x.setField("a new field");
System.out.println(x.getField()); // prints "a new field"
Run Code Online (Sandbox Code Playgroud)

在一个不可变的类中(声明为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"
Run Code Online (Sandbox Code Playgroud)


Gre*_*ill 18

你改变什么a .试试这个:

String a="a";
System.out.println("a 1-->"+a);
String b=a;
a="ty";
System.out.println("a 2-->"+a);
System.out.println("b  -->"+b);
Run Code Online (Sandbox Code Playgroud)

您将看到物体a,然后b是指没有改变.

如果要防止代码更改哪个对象a引用,请尝试:

final String a="a";
Run Code Online (Sandbox Code Playgroud)

  • `String`类是不可变的,因为它没有*可以改变其内容*的方法.将它与`StringBuilder`进行对比,它具有诸如`.append()`和`.delete()`之类的方法,并且不是不可变的. (3认同)

Lio*_*ion 5

字符串是char[]包含一系列UTF-16代码单元,int该数组的偏移量和int长度.

例如.

String s
Run Code Online (Sandbox Code Playgroud)

它为字符串引用创建空间.分配副本引用,但不修改这些引用引用的对象.

你也应该意识到这一点

new String(s)
Run Code Online (Sandbox Code Playgroud)

并没有真正做任何有用的事情.它只创建另一个由相同数组,偏移量和长度支持的实例s.很少有理由这样做,因此被大多数Java程序员认为是不好的做法.

Java的双引号的字符串像"my string"真的来引用实习 String情况下,这样"bar"是相同的字符串实例的引用,不管它有多少次出现在你的代码.


"hello"创建一个池化的实例,并new String(...)创建一个非池化的实例.尝试System.out.println(("hello" == "hello") + "," + (new String("hello") == "hello") + "," + (new String("hello") == new String("hello")));,你应该看到true,false,false