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

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


当前回答

不可改变的意思是不可改变的。字符串对象一旦创建,其数据或状态就不能更改

考虑下面的例子:

class Testimmutablestring{  
  public static void main(String args[]){  
    String s="Future";  
    s.concat(" World");//concat() method appends the string at the end  
    System.out.println(s);//will print Future because strings are immutable objects  
  }  
 }  

让我们考虑一下波纹图,

在这个图中,你可以看到一个新对象被创建为“Future World”。但不改变“未来”。因为字符串是不可变的。s,仍指“未来”。如果你需要给“未来世界”打电话,

String s="Future";  
s=s.concat(" World");  
System.out.println(s);//print Future World

为什么在java中字符串对象是不可变的?

因为Java使用了字符串字面量的概念。假设有5个引用变量,都指向一个对象“Future”。如果一个引用变量改变了对象的值,它将影响到所有的引用变量。这就是为什么字符串对象在java中是不可变的。

其他回答

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

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

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

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

In large applications its common for string literals to occupy large bits of memory. So to efficiently handle the memory, the JVM allocates an area called "String constant pool".(Note that in memory even an unreferenced String carries around a char[], an int for its length, and another for its hashCode. For a number, by contrast, a maximum of eight immediate bytes is required) When complier comes across a String literal it checks the pool to see if there is an identical literal already present. And if one is found, the reference to the new literal is directed to the existing String, and no new 'String literal object' is created(the existing String simply gets an additional reference). Hence : String mutability saves memory... But when any of the variables change value, Actually - it's only their reference that's changed, not the value in memory(hence it will not affect the other variables referencing it) as seen below....


字符串s1 = "旧字符串";

//s1 variable, refers to string in memory
        reference                 |     MEMORY       |
        variables                 |                  |

           [s1]   --------------->|   "Old String"   |

字符串s2 = s1;

//s2 refers to same string as s1
                                  |                  |
           [s1]   --------------->|   "Old String"   |
           [s2]   ------------------------^

s1 = "New String";

//s1 deletes reference to old string and points to the newly created one
           [s1]   -----|--------->|   "New String"   |
                       |          |                  |
                       |~~~~~~~~~X|   "Old String"   |
           [s2]   ------------------------^

原来的字符串'in memory'没有改变,但是 引用变量已被更改,以便它引用新字符串。 如果我们没有s2, Old String仍然在内存中,但是 我们无法访问它…

因为公认的答案不能回答所有的问题。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不是不可变的。

字符串的状态改变后构造。看一下hashcode()方法。String将hashcode值缓存在本地字段中,但直到第一次调用hashcode()才计算它。这种对hashcode的惰性求值将String置于一个有趣的位置,作为状态发生变化的不可变对象,但如果不使用反射,就无法观察到它发生了变化。

所以也许不可变的定义应该是一个不能被观察到已经改变的对象。

如果一个不可变对象在创建后状态发生了变化,但是没有人可以看到它(没有反射),这个对象仍然是不可变的吗?