第五节 字符串底层实现

一、常量池

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对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值