不可变到底是什么意思——也就是说,对象可变或不可变的结果是什么?特别是,为什么Java的字符串是不可变的?

我的理解是StringBuilder类型类似于String的可变等价。什么时候我会使用StringBuilder而不是字符串,反之亦然?


当前回答

因为公认的答案不能回答所有的问题。11年零6个月后,我不得不给出答案。

有人能解释一下什么是不可变吗?

希望你指的是不可变对象(因为我们可以考虑不可变引用)。

对象是不可变的:iff一旦创建,它们总是表示相同的值(没有任何改变值的方法)。

为什么字符串是不可变的?

尊重上面的定义,这可以通过查看Sting.java源代码来检查。

不可变对象的优点/缺点是什么? 不可变类型有:

更安全,远离虫子。 更容易理解。 而且更愿意改变。

为什么像StringBuilder这样的可变对象应该优先于String,反之亦然?

缩小问题为什么我们在编程中需要可变的StringBuilder ? 它的一个常见用途是将大量字符串连接在一起,就像这样:

String s = "";
for (int i = 0; i < n; ++i) {
    s = s + n;
}

使用不可变字符串,这会产生大量临时拷贝——在构建最终字符串的过程中,字符串的第一个数字(“0”)实际上被复制n次,第二个数字被复制n-1次,依此类推。实际上,做所有这些复制需要花费O(n2)时间,尽管我们只连接了n个元素。

StringBuilder的设计目的是最小化这种复制。它使用了一个简单但巧妙的内部数据结构来避免任何复制,直到最后,当你使用toString()调用来请求最终的String时:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
  sb.append(String.valueOf(n));
}
String s = sb.toString();

获得良好的性能是我们使用可变对象的原因之一。另一个是方便的共享:通过共享一个公共的可变数据结构,程序的两个部分可以更方便地通信。

更多信息请访问:https://web.mit.edu/6.005/www/fa15/classes/09-immutability/#useful_immutable_types

其他回答

String s1="Hi";
String s2=s1;
s1="Bye";

System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
System.out.println(s1); //Bye

s1="Hi":创建一个包含"Hi"值的对象s1。

S2 =s1:引用s1对象创建对象S2。

s1="Bye":之前的s1对象的值不会改变,因为s1有String类型,而String类型是一个不可变的类型,相反,编译器创建了一个新的String对象与"Bye"值和s1引用它。在这里,当我们打印s2值时,结果将是“Hi”而不是“Bye”,因为s2引用了前一个具有“Hi”值的s1对象。

一旦实例化,就不能更改。考虑一个类,它的实例可能用作哈希表或类似的键。查看Java最佳实践。

不可变对象是不能通过编程改变的对象。它们特别适用于多线程环境或其他多个进程能够更改(突变)对象中的值的环境。

不过,澄清一下,StringBuilder实际上是一个可变对象,而不是不可变对象。常规的java String是不可变的(意味着一旦创建了它,就不能在不改变对象的情况下更改底层字符串)。

例如,假设我有一个名为ColoredString的类,它有一个String值和一个String颜色:

public class ColoredString {

    private String color;
    private String string;

    public ColoredString(String color, String string) {
        this.color  = color;
        this.string = string;
    }

    public String getColor()  { return this.color;  }
    public String getString() { return this.string; }

    public void setColor(String newColor) {
        this.color = newColor;
    }

}

在这个例子中,ColoredString被认为是可变的,因为您可以在不创建新的ColoredString类的情况下更改(突变)它的一个关键属性。这可能很糟糕的原因是,例如,假设您有一个GUI应用程序,它有多个线程,并且您正在使用ColoredStrings将数据打印到窗口。如果你有一个ColoredString的实例,它被创建为

new ColoredString("Blue", "This is a blue string!");

然后你会期望字符串总是“Blue”。然而,如果另一个线程获得了这个实例并调用

blueString.setColor("Red");

当你想要一个“蓝色”的字符串时,你会突然,很可能出乎意料地得到一个“红色”的字符串。正因为如此,在传递对象实例时,几乎总是首选不可变对象。在确实需要可变对象的情况下,通常只需从特定的控制字段传递副本来保护对象。

概括一下,在Java中,Java .lang. string是一个不可变对象(一旦创建就不能更改),而Java .lang. stringbuilder是一个可变对象,因为它可以在不创建新实例的情况下进行更改。

不可变意味着一旦对象被创建,它的所有成员都不会改变。字符串是不可变的,因为你不能改变它的内容。 例如:

String s1 = "  abc  ";
String s2 = s1.trim();

在上面的代码中,字符串s1没有改变,使用s1创建了另一个对象(s2)。

因为公认的答案不能回答所有的问题。11年零6个月后,我不得不给出答案。

有人能解释一下什么是不可变吗?

希望你指的是不可变对象(因为我们可以考虑不可变引用)。

对象是不可变的:iff一旦创建,它们总是表示相同的值(没有任何改变值的方法)。

为什么字符串是不可变的?

尊重上面的定义,这可以通过查看Sting.java源代码来检查。

不可变对象的优点/缺点是什么? 不可变类型有:

更安全,远离虫子。 更容易理解。 而且更愿意改变。

为什么像StringBuilder这样的可变对象应该优先于String,反之亦然?

缩小问题为什么我们在编程中需要可变的StringBuilder ? 它的一个常见用途是将大量字符串连接在一起,就像这样:

String s = "";
for (int i = 0; i < n; ++i) {
    s = s + n;
}

使用不可变字符串,这会产生大量临时拷贝——在构建最终字符串的过程中,字符串的第一个数字(“0”)实际上被复制n次,第二个数字被复制n-1次,依此类推。实际上,做所有这些复制需要花费O(n2)时间,尽管我们只连接了n个元素。

StringBuilder的设计目的是最小化这种复制。它使用了一个简单但巧妙的内部数据结构来避免任何复制,直到最后,当你使用toString()调用来请求最终的String时:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
  sb.append(String.valueOf(n));
}
String s = sb.toString();

获得良好的性能是我们使用可变对象的原因之一。另一个是方便的共享:通过共享一个公共的可变数据结构,程序的两个部分可以更方便地通信。

更多信息请访问:https://web.mit.edu/6.005/www/fa15/classes/09-immutability/#useful_immutable_types