不可变到底是什么意思——也就是说,对象可变或不可变的结果是什么?特别是,为什么Java的字符串是不可变的?
我的理解是StringBuilder类型类似于String的可变等价。什么时候我会使用StringBuilder而不是字符串,反之亦然?
不可变到底是什么意思——也就是说,对象可变或不可变的结果是什么?特别是,为什么Java的字符串是不可变的?
我的理解是StringBuilder类型类似于String的可变等价。什么时候我会使用StringBuilder而不是字符串,反之亦然?
当前回答
不可变对象
一个对象被认为是不可变的,如果它的状态在被构造后不能改变。对于创建简单、可靠的代码,最大程度地依赖于不可变对象是一种被广泛接受的合理策略。
不可变对象在并发应用程序中特别有用。由于它们不能改变状态,因此不会被线程干扰破坏或在不一致的状态下观察到。
程序员通常不愿意使用不可变对象,因为他们担心创建新对象的成本,而不是在适当的地方更新对象。对象创建的影响经常被高估,并且可以被一些与不可变对象相关的效率所抵消。这包括减少垃圾收集带来的开销,以及消除保护可变对象不受损坏所需的代码。
下面的子节采用一个实例是可变的类,并从中派生一个具有不可变实例的类。在这样做的过程中,他们给出了这种转换的一般规则,并演示了不可变对象的一些优点。
源
其他回答
我真的很喜欢SCJP Sun认证程序员Java 5学习指南的解释。
为了提高Java的内存效率,JVM留出了一个特殊的内存区域,称为“字符串常量池”。当编译器遇到String字面值时,它会检查池,看看是否已经存在相同的String。如果找到匹配,则对新字面值的引用将指向现有的String,并且不会创建新的String字面值对象。
不可变对象是指创建后不能修改的对象。一个典型的例子是字符串字面量。
越来越流行的D编程语言通过“不变”关键字具有“不变性”的概念。查看Dr.Dobb关于它的文章http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29。它完美地解释了这个问题。
java.time
这可能有点晚了,但为了理解什么是不可变对象,请考虑以下来自新的Java 8日期和时间API (Java . Time)的示例。你可能知道,Java 8中的所有日期对象都是不可变的,所以在下面的例子中
LocalDate date = LocalDate.of(2014, 3, 18);
date.plusYears(2);
System.out.println(date);
输出:
2014-03-18
这将打印与初始日期相同的年份,因为plusYears(2)返回一个新对象,因此旧日期仍然不变,因为它是一个不可变对象。一旦创建,您就不能进一步修改它,日期变量仍然指向它。
因此,该代码示例应该捕获并使用由plusYears调用实例化并返回的新对象。
LocalDate date = LocalDate.of(2014, 3, 18);
LocalDate dateAfterTwoYears = date.plusYears(2);
date.toString()…2014-03-18 dateAfterTwoYears.toString()…2016-03-18
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