String为什么是不可变的,为什么要设计成final?
一、String为什么要设计成final?
首先讲讲为什么要设计成final,其实String的final最终的目的是要使得String类变得不可变,提高安全和性能。那么我们可以先说说final为不可变的特性提供了什么帮助,然后在说说String不可变的好处。
String的final体现在两个方面,这个很重要,一个是类的final,一个是value数组的final,下面是String源码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
...
}
- 类的final起的作用是不可继承
类的final起的作用是不可继承,由于字符串是需要大量使用的,若是我们使用了继承了String的子类来操作字符串,那会带来两个后果,一是调用子类字符串的方法时,虚拟机要判断是有没有重载后的方法或者是去找它父类对应的方法调用,在大量使用字符串的情况下,肯定会降低运行效率。二是String的方法可能会被重写,设计者认为String类应该是恒定的,不可变的,而方法一旦被重写,那么语义就会被改变,无形中降低了安全性。 - 成员变量 char 数组的value的final起的作用是引用不可改变
这一点很多人会误解,final修饰的变量是引用不可改变,而不是值不可改变。运行下面的代码:
final char[] str = {'a','b','c'};
System.out.println(str);
str[1]='a';
System.out.println(str);
输出是:
abc
aac
可以看到final修饰的变量是可以改变的,那么问题来了,既然写了final值还是可以改变,那这个final的作用是什么呢?我的理解是其实没啥大作用,值不可改变这一块要归功于设计者在String类的方法里都小心翼翼地处理value数组,并且是private修饰的,防止改变值。但是这个final也算是体现了设计者String类不可变的“精神”,因此可见不可变特性是整体String设计带来的效果,并不是仅由final就能完成的。
二、String为什么是不可变的,不可变的好处
1.性能
String字符串是非常常用的,如果不加节制地创建字符串对象,那么内存中存在非常多的字符串对象,效率低下。为了提高性能,java把字符串值放到常量池,然后相同值的String其实是指向了常量池中的同一个内存地址,此为字符串常量池数据共享。
String a = "someValue";
String b = "someValue";
像上面的代码,其实内存中只有一份"someValue"的值,然后a和b同时指向这份值,能够节省非常多的空间,提升性能。
但是数据共享会带来一个问题,就是如果String值是可以改变的,那么我a改变了值,连带的b的值也会跟着改变,这样非常容易出问题且难以排除,因此数据共享的前提是值不可改变,仅引用改变。
2.安全
String是不可变的,那么StringBuffer就相当于可变的String,我们看看下面的代码:
static String stringAdd(String str) {
return str+="!";
}
static StringBuffer stringBufferAdd(StringBuffer str) {
return str.append("!");
}
public static void main(String[] args) {
String string = "value";
StringBuffer stringBuffer = new StringBuffer("value");
System.out.println(stringAdd(string));
System.out.println(stringBufferAdd(stringBuffer));
System.out.println(string);
System.out.println(stringBuffer);
}
输出是:
value!
value!
value
value!
可以看到String的原先的值没有被改变,而StringBuffer的原先的值被改变了。如果String是可变的,那么我们的字符串在传值过程中就会很容易被改变。
三、总结
java中的String虽然不和int一样是基本类型,但是由于太常用了,地位和int这种基本类型是一样的,因此要一样的做到不可继承不可改变等特性(所以在C#中String已经是基本类型)。在大量使用字符串的情况下,为了节省内存,提升效率,就要数据共享,而不可变特性是数据共享的前提。
final设计的目的是为了实现String不可变的特性,只是不可变特性设计中的一部分。
下一篇: 金三银四必看的20道 Vue 面试题