可变性
有⼀些对象的内容是不变的 (immutable):⼀旦它们被创建,它们总是表⽰相同的值。另⼀些对象是可变的(mutable):它们有改变内部 值对应的⽅法。
String 就是不变对象的⼀个例⼦,⼀个 String 对象总是表⽰相同的字符串。⽽ StringBuilder 则是可变 的,它有对应的⽅法来删除、插⼊、替换字符串内部的字符,等等。
因为 String 是不变的,⼀旦被创建,⼀个 String 对象总是有⼀样的值。为了在⼀个 String 对象字符串后 加上另⼀个字符串,你必须创建⼀个新的 String 对象:
与此相对, StringBuilder 对象是可变的。这个类有对应的⽅法来改变对象,⽽不是返回⼀个新的对象:
当有别的索引指向同⼀个对象时,它们的⾏为会⼤不相同。
既然我们已经有了不变的 String 类,为什么还要使⽤可变的 StringBuilder 类呢?⼀个常⻅的使⽤环境就是 当你要同时创建⼤量的字符串,如果使⽤不变的字符串,这会发⽣很多“暂时拷⻉”——第⼀个字符“0”实际上就被拷⻉了n次,第⼆个字符被拷⻉了 n-1次,等等。总的来说,它会花费O(N^2)的时间来做拷⻉,即使最终我们的字符串只有n个字符。StringBuilder 的设计就是为了最⼩化这样的拷⻉,它使⽤了简单但是聪明的内部结构避免了做任何拷⻉(除⾮ 到了极限情况)。如果你使⽤ StringBuilder ,可以在最后⽤ toString() ⽅法得到⼀个 String 的结果:
优化性能是我们使⽤可变对象的原因之⼀。另⼀个原因是为了分享:程序中的两个地⽅的代码可以通过共享⼀个数 据结构进⾏交流。
可变性带来的⻛险
是使⽤不可变类型要⽐可变类型安全的多,同时也会让代码更易懂、更具备可改动性。可变性会使得别⼈很难 知道你的代码在⼲吗,也更难制定开发规定(例如规格说明)。
传⼊可变对象
防御性复制
返回可变对象
返回⼀个复制品
别名会让可变类型存在⻛险
事实上,如果你只在⼀个⽅法内使⽤可变类型⽽且该类型的对象只有⼀个索引,这时并不会有什么⻛险。⽽上⾯的 例⼦告诉我们,如果⼀个可变对象有多个变量索引到它——这也被称作“别名”,这时就会有产⽣bug的⻛险。
变化与契约(contract)
可变对象会使得契约(例如规格说明)变得复杂
可变对象降低了代码的可改动性