可以说字符串操作是计算机程序设计中最为常见的行为
不可变的String
根据JDK文档的说法,String类代表字符串,Java程序中的所有字符串文字(例如"abc")都被实现为此类实例,例如:
String string = "abc";
//相当于:
char[] data = { 'a','b','c'};
String str = new String(data);
那为什么说String类是不可变的,看一段String类中的部分源代码
哦~,原来是这样。String类是被final修饰的,所以无法继承该类。并且,其所有的成员变量都是私有(private)的,而且没有提供修改私有变量的共有(public)方法,再者,关于对String对象的操作都没有修改当前对象,而是新建一个对象
例如下面这个substing方法,如果返回值不等于自身,该方法就会创建一个String对象,该对象为截取后的结果,而不是在原String对象上进行的
可能有些人就来疑问了,谁说的不能修改。看我这个,s1都被我改的毁容了
其实,这只是表面的错觉而已
起初,3个引用(s1、s2、s3)分别指向各自的对象,如图
s1 欧文 s2 艾弗森 s3 NB执行第12行赋值语句后,并不是修改了s1所指的String对象(欧文),而是修改了引用s1,使其指向引用s2所指的String对象(艾弗森),如图
s1 欧文 s2 艾弗森 s3 NB执行第15行语句时,临时的StringBuilder对象会连接s2与s3所指的对象,并调用toString方法创建一个String对象赋值给引用s1,即引用s1会指向这个新创建的String对象,如图
s1 艾弗森NB 欧文 艾弗森 s2 s3 NB从执行的过程可以看出,String对象“欧文”、“艾弗森”、“NB”在全程过程中都没有改变,改变的只是引用s1所指向的内容,也就是s1的值。
下面的程序给予说明,String对象的不可变性。
可以看出,尽管对s1依次调用了3个方法(toUpperCase、concat和substring),但是s1指向的String内容始终没有改变,而s2、s3与s4确实输出了期望的结果。这是因为,String类提供的一系列操作字符串内容的方法都不是在自身对象上进行的,而是新创建一个String对象,例如,当调用toUpperCase时,并不是修改s1自身指向的String对象,而是新创建了一个String对象,其内容就是s1内容的大写形式。因此,这样既可以满足对String对象的各种操作要求,又保证了String对象的数据成员不会被外界修改。
那为什么要把String设计为不可变呢
其最大的好处就是可以实现资源共享,在对线程操作的时候,可以将其认为不可变的,而不用担心其他线程会对其进行修改
要点总结
- String类是不可变类,其对象一旦创建,就不可修改。
- String类那些看似修改字符序列的方法实际上都是新创建的String对象,而不是修改自身对象
- 由于String对象是不可变的,因此其具有线程安全性,可以自由地实现共享