String类源码及其补充解析
String源码解析具体内容详见转载: http://www.hollischuang.com/archives/99
补充解析:
1:关于value
引用
/**值用于字符存储,---String内部的第一个属性*/
private final char value[ ];
这是一个字符数组,并且是final类型,他用于存储字符串内容,从fianl这个关键字中我们可以看出,String的内容一旦被初始化了是不能被更改的。 虽然有这样的例子: String s = “a”; s = “b” 但是,这并不是对s的修改,而是重新指向了新的字符串, 从这里我们也能知道,String其实就是用char[]实现的。
private final char value[ ];
这是一个字符数组,并且是final类型,他用于存储字符串内容,从fianl这个关键字中我们可以看出,String的内容一旦被初始化了是不能被更改的。 虽然有这样的例子: String s = “a”; s = “b” 但是,这并不是对s的修改,而是重新指向了新的字符串, 从这里我们也能知道,String其实就是用char[]实现的。
String是final修饰的,表示该类不能被继承,String内的方法不能被重写。内部维护了一个char[ ]来存储String的值。
public class IntertorTest { public static void main(String[] args) { // 1 String str = "abc"; // 2 char[] data = {'a','b','c'}; String a = new String(data); } } 1和2其实是一样的。
2:关于hash
引用
/*缓存字符串的散列码。默认为0*/
private int hash;
private int hash;
问题考点:为什么hashCode值选择为31? 详解转载自: https://segmentfault.com/a/1190000010799123
首先看下String类中hashCode()的源码:
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
hashcode()方法解释为数学表达式,即为:h = 31^(n-1)*val[0]+31^(n-2)*val[1]+31^(n-3)*val[2]+......+31^0*val[n-1]
例如:字符串"abcd", h = 31^(3)*a + 31^(2)*b+31^(1)*c+31^(0)*d
解题答案:
1:31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一。
注:将hash值选为质数可以降低hash冲突。
2:31可以被JVM优化,31 * i = (i << 5) - i。(优化内容:乘法可以用移位和减法代替,以获得更好的性能。)
3:String类的常量池
JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享。
[字符串常量池(String pool, String intern pool, String保留池)]是Java堆内存中一个特殊的存储区域, 当创建一个String对象时,jvm会先去常量池用寻找是否存在该值,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象,若不存在于常量池,则会新创建一个对象。(针对String a = "1",a = "2")
注:不该创建对象还是重新引用,改变的都是指针的地址。
总结:
1:单独使用""引号创建的字符串都是常量,编译期就已经确定存储到String Pool中;
2:使用new String("")创建的对象会存储到堆中,是运行期新创建的;
3:使用只包含常量的字符串连接符如"aa" + "aa"创建的也是常量,编译期就能确定,已经确定存储到String Pool中;
4:使用包含变量的字符串连接符如"aa" + s1创建的对象是运行期才创建的,存储在堆中;
引用
String s = new String("abc")这个语句创建了几个对象。
创建了2个对象:
第一个对象是"abc"字符串存储在常量池中,首先若常量池中没有则创建一个对象存于常量池。
第二个对象在JAVA堆中的 String 对象,new出来的对象都存在于堆中(这是不会变的)。
创建了2个对象:
第一个对象是"abc"字符串存储在常量池中,首先若常量池中没有则创建一个对象存于常量池。
第二个对象在JAVA堆中的 String 对象,new出来的对象都存在于堆中(这是不会变的)。
问题考点:为什么String会被设计为final?
解题答案:
1:为了安全。
String被许多的Java类(库)用来当做参数,例如网络连接地址URL,文件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。且在多线程情况下是线程安全的。
2:支持字符串常量池,能提:高性能和减少内存开销。