一、常量池
1、常量池分类
(1)class文件中的常量池:存在于硬盘上,使用命令“javap -verbose”可以查看;
(2)运行时常量池:InstanceKlass的一个属性,ConstantPool* _constants,存放在方法区(元空间);
(3)字符串常量池:String Pool,存在于堆区,底层是StringTable,StringTable继承自HashTable。
二、StringTable
1、StringTable类的继承关系(Hashtable本质上是通过数组+链表来存储字符串的)
2、StringTable存储以及获取字符串的过程(index和hashValue我写反了)
(1)当代码中定一个或多个字符串被定义时,字符串会通过字符串值和字符串长度计算hashcode值,即hashValue。
(2)存放字符串时,比较hashValue是否存在,如果不存在则将字符串的hashValue放到数组中,并根据hashValue计算出一个index值,HashTable的key指向这个index,Hashtable的value指针指向InstanceOopDes(InstanceOopDes被封装成HashTableEntry)。
(3)如果hashValue存在,则根据算法计算出不同的index,并将String的的value存放到链表的下一个地址中。
(4)获取时,根据hashcode计算出index,根据index去除字符串的值。
3、StringTable源码解析
四、字符串在内存中的存在形式(StringTable的key是hashValue,value是HashTableEntry)
1、一个双引号
2、两个双引号
3、一个new
4、两个new
5、字符串创建步骤(以String s1 = new String("11");为例)
(1)在字符串常量池中查找是否有此字符串(“11”),如果有则返回对应的String对象;如果没有则在字符串常量池中创建String的OopDesc对象和char数组的TypeArrayOopDesc对象;
(2)将这个String对象对应的InstanceOopDesc封装成HashtableEntry对象,HashtableEntry对象作为StringTable的value进行存储;
(3)new关键字使的String的OopDesc在堆中再次创建一份。所以new String("11")会创建出来两个String的OopDesc对象和一个char数组的typeArrayOopDesc对象。
6、String的特殊构造
String(char value[], int offset, int count)构造方法不会在常量池中创建。即通过此构造方法创建的String对象只有一个。比如:String str = new String(new char[]{'1'}, 0, 1)。
五、连接字符串的底层实现
1、“+”连接字符串原理
(1)“+”连接字符串本质上是通过构建StringBuilder对象,调用其append()和toString()方法实现的字符串拼接。由于StringBuilder的toString方法是同过“return new String(value, 0, count);”来实现的,所以通过“+”拼接的字符串并不会在常量池中存在。
(2)“+”连接字符串时,不会将字符串写进常量池。即,不会生产HashTableEntry,无法从常量池中查找。
(3)当两个常量字符串都被final修饰时,两者拼接在编译时会被直接赋值成拼接后的形式,此时和直接创建这个拼接后的字符串内存地址相同。若有一个未被final修饰,则条件不成立。如下图:
final修饰的new String形式在拼接时,不会被编译成直接赋值后的形式,拼接后的字符串地址是一个新的地址。因为final修饰的是String引用,而不是字面量,所以地址不同。如下图:
2、示例
(1)双引号+双引号
(2)双引号+new String("")
六、intern的底层实现原理
1、原理:
(1)去常量池中找字符串,如果有就返回,如果没有,就把String对应的InstanceOopDes封装成StringTable;
(2)intern会强制把拼接的字符串写进常量池。
2、图解
其他:栈中存放的东西
1、如果是基本类型,栈中存储的就是值本身
2、如果是引用类型,栈中存储的就是引用(内存地址)
3、oop是Java中的对象在JVM中的形式。
习题:
1、这句代码创建了几个对象?为什么?
String s1 = new String("子牙真帅");
答:两个String对象,一个oop char[]对象。见
2、这句代码创建了几个对象?为什么?
String s2 = "子牙" + "子牙";
答:一个String对象,一个oop char[]对象。编译器会将此值变成拼接后的形式。
3、这句代码创建了几个对象?为什么?
String s2 = "子牙" + new String("真帅");
答:4个String+3个char[] oop对象。"子牙"会创建一个String对象和一个char[]的oop对象,new String("真帅")会创建两个String对象和1个char[]的oop对象,拼接后的s2会在内存中生成一个String对象和一个拼接后的char[]的oop对象。