题记
开个新坑,写一些自己的认知,后续想多写一点,可能认知不到位,会露怯,但暴露出来无知也许才能更好的进步。 =. =
String类不可变
本篇假设你以经具备了java的基础知识并且了解String常量池的概念
String为不可变类,这句话做java开发的应该都听说过
但是
为什么不可变?
怎么做到的不可变?
是不是真的不可变?
关于“为什么不可变”讨论的比较多了,我就不多聊了,有需要了解的可以去看一下这篇:如何理解 String 类型值的不可变? 这篇答案写的不错,也顺便说了下“怎么做到的不可变”,不过说的不全,我按自己的理解理一下。
怎么做到的不可变
我理解的String类不可变的定义为:
已经创建的String对象不能修改
基于此我们来翻一下jdk的源码,看一看String的实现:
1 | public final class String |
这段是String类的定义以及最主要的私有成员变量value
,和一个有代表性的方法,我们来拆解看一下关键点。
public final class String
:
final
的作用是不允许String有子类
这样做很重要,它保证了我们定义的 String x = ...
在调用方法时,会有相同的处理逻辑,避免了以下情况的产生:
1 | public class Str { |
输出:
修改过的子类方法输出:2
现在你可能还是没太明白这样做的意义,没关系,接着往下看。
private final char value[]
:
final
定义的变量不可重复赋值private
修饰符使得变量私有化,外部无法直接调用或修改数组中的值
这块很好理解,就是限制修改已生成对象的内容
最后说一下subString
这个方法,这个方法的作用是对当前字符数组做截取处理(beginIndex>0时),最后返回处理过的值。
可以看到该方法返回时采用了new String(...)
创建了一个新的String对象,这样做保证了原有String对象的不变。
String中的方法都是如此不直接操作原对象而是生成一个新的String对象。
再结合前面的例子就可以得出一个结论,final
修饰的String类让我们在调用方法时不用考虑会影响其它引用的问题。
这么说可能你一下想不到影响了会怎样,我们来看一个例子顺便讨论下,String类真的不可变吗?
是不是真的不可变
答案是否定的,总有办法可以让他改变,比如使用反射:
1 | public class Test { |
输出结果:
abc:abc
a2c:a2c
可以看到变量s的值变了,ss的值也跟着变了,因为String类有常量池的概念,使用""
创建的对象都放在常量池里,内容相同的字符串不会创建新对象,定义多次使用的是同一个对象的引用。
假如使用ss
变量做一个很重要的操作,结果中途ss
引用的内容被修改了,你应该能想像的到会怎么样。
总结
不可变主要是因为常量池。
实现靠的是不允许有子类,不对外暴露内容,不提供可修改值的方法。
使用反射最终能达到修改String的目的
java的设计人员从一开始就帮我们想好了解决方法,把String设计为一个不可变的类,当然主要是防止误操作。让我们能放心修改String对象。
本文链接: http://blog.jisuye.com/2017/09/21/string/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!