因为String是引用类型,也就是每个字符串都是一个String实例。通过源码可以看到String底层维护了一个byte数组:private final byte[] value;(JDK9中为byte数组,并非网上所说的char数组)。被final修饰的类不能被继承,也就是不能有子类。
首先,String是引用类型,也就是每个字符串都是一个String实例。通过源码可以看到String底层维护了一个byte数组:private final byte[] value;(JDK9中为byte数组,并非网上所说的char数组)。虽然该数组被修饰为final,但这并不能保证数组的数据不会变化,因此还需要声明为private防止被其他类修改数据。
被final修饰的类不能被继承,也就是不能有子类。那么为什么要把String设计为不能被继承呢?简单来说有两点:安全和效率。
安全
要知道String是一个非常非常基础的类,用处超级广泛,各种各样的类基本都使用到了字符串。
假设String类可以被继承,现在有一个方法method,该方法的参数为String类型,并且该方法利用到了字符串的长度特性:
public int method(String s){
//do something
int a = s.length() + 1;
return a;
}
基于Java的多态特性,当我们把MyString的实例作为参数传入method()方法时,编译器是不会报错的。但是我们的运行结果则完全错误,这会造成非常严重的后果。
相对于每次使用字符串的时候使用final修饰,直接把String类定义为final更为安全,效率也更高。并且,整个类声明为final之后,如果有一个String的引用,则它引用的一定是String对象,而不会是其他类的对象(泛型允许引用子类)。防止世界被熊孩子破坏2333
除了由多态引起的安全问题,还有引用类型本身的问题。
不可变的字符串还可以保证多线程时的线程安全问题。多线程时,只有读操作一般不会引发线程安全问题,当读写同时存在时便容易引发安全问题。当字符串不可变时也就不能写,当然不会引发线程问题。
效率
基于字符串的不可变,才能有字符串常量池这一特性。字符串常量池的诞生是为了提升效率和减少内存分配。可以说我们编程有百分之八十的时间在处理字符串,而处理的字符串中有很大概率会出现重复的情况。正因为String的不可变性,常量池很容易被管理和优化。
并且1.7之前,字符串常量池在方法区,1.7之后在堆内存中,并且不仅仅可以存储对象,还可以存储对象的引用:
String s = new String(“A”) + new String(“B”);//此时常量池存在”A”、”B”,但是不存在”AB”;堆中存在”A”、”B”、”AB”,并且s指向”AB”
s.intern();//1.7之后这里加入的是对象s的引用,而非直接保存”AB”字符串
//intern用来返回常量池中的某字符串,如果常量池中已经存在该字符串,则直接返回常量池中该对象的引用。否则,在常量池中加入该对象,然后 返回引用。
对于什么时候会在常量池存储字符串对象:
显示调用String的intern方法的时候,例如上例。
直接声明字符串字面常量的时候,例如: String a = “aaa”;
直接new String(“A”)方法的参数使用常量的时候
字符串直接常量相加的时候,例如: String c = “aa” + “bb”; 其中的aa/bb只要有任何一个不是字符串字面常量形式,都不会在常量池生成”aabb”. 且此时jvm做了优化,不会同时生成”aa”和”bb”在字符串常量池中
延伸阅读:
什么是string类?
C++、java、VB等编程语言中的名词。 在java、C#中,String类是不可变的,对String类的任何改变,都是返回一个新的String类对象。
string>是C++标准程序库中的一个头文件,定义了C++标准中的字符串的基本模板类std::basic_string及相关的模板类实例
其中的string是以char作为模板参数的模板类实例,把字符串的内存管理责任由string负责而不是由编程者负责,大大减轻了C语言风格的字符串的麻烦。
std::basic_string提供了大量的字符串操作函数,如比较、连接、搜索、替换、获得子串等。并可与C语言风格字符串双向转换。std::basic_string属于C++ STL容器类,用户自定义的类也可以作为它的模板参数,因此也适用C++ STL Algorithm库。
string本质上是以字符作为元素的vector特化版本;不存在0字符结尾这个概念,能装入’\0’这种数据。
std::basic_string类模板存储且操纵类似char的对象的序列。该对象类型的性质由特性类模板std::char_traits的实例来提供,并作为std::basic_string的第二个模板参数 [1] 。
C++11标准规定:basic_string的元素是连续存储的。即对于basic_string s,有:&*(s.begin() + n) == &*s.begin() + n,其中n属于[0, s.size())。换句话说,指向s[0]的指针即为指向CharT[]数组的首元素指针。C++11已经禁止了写入时复制(copy-on-write)的实现,因为存在多线程安全问题。一般都采用了小字符串优化(SSO)实现,如Visual C++:
文章标题:在java中String类为什么要设计成final,发布者:小编,转载请注明出处:https://worktile.com/kb/p/36137