今天学习了下字符串
字符串常量池
字符串常量池底层是用StringTable做的,而StringTable是做HashTable做的。在堆区
Hashtable的实现原理跟HashMap差不多,都是数组加链表。
java中的字符串在jvm中是如何存储的?
key生成策略
源码
hashValue = hash_string(name, len);
index = hash_to_index(hashValue);
1.根据字符串和字符串的长度计算出hashValue
2.再根据HashValue计算出index,index就是key
value
HashtableEntry<oop, mtSymbol>* entry = new_entry(hashValue, string());
将String的实例instanceOopDesc封装成HashtableEntry
char数组
在之前的文章中也说过,java存储数组是用两种类型:
TypeArrayKlass:存储基本数据类型数组
ObjArrayKlass:存储引用类型数组
我们创建String对象的时候也会生成一个char数组
public static void main(String[] args) {
char[] arr = new char[]{'1', '2'};
while (true);
}
这个是用HSDB工具看到的,jdk自带的工具
不同方式创建字符串在JVM中的存在形式
双引号
这种方式可以创建String对象1个,也可以说创建了2个OOP对象:InstanceOopdesc和TypeArrayOopdesc
验证:
new String
这种方式可以说创建String对象2个,也可以说创建了3个OOP对象:2个InstanceOopdesc和1个TypeArrayOopdesc.
为什么?
因为jvm创建字符串对象的时候会先在字符串常量池中去找,如果找到了就直接返回,如果没找到就会创建一个该字符串对象。然后他自己本身还new了一个String对象,所有就是两个String对象,三个oop对象
验证:
两个双引号
这种方式可以说创建String对象1个,也可以说创建了2个OOP对象
为什么应该知道了把?验证我就不贴了,你们自己动手
两个new String
这种方式可以说创建String对象3个,也可以说创建了4个OOP对象
拼接字符串底层是怎么实现的
双引号 + 双引号
public class TestString_5 {
public static void main(String[] args) {
test2();
}
public static void test2() {
String s1 = "1";
String s2 = "1";
String s = s1 + s2; //会形成2个String对象,2个typeArrrayOop对象
// String a = “1”+“2”;//只会形成1个String对象和1个typeOop对象
}
}
字节码指令
0 ldc #2 <1>
2 astore_0
3 ldc #2 <1>
5 astore_1
6 new #3 <java/lang/StringBuilder>
9 dup
10 invokespecial #4 <java/lang/StringBuilder.<init>>
13 aload_0
14 invokevirtual #5 <java/lang/StringBuilder.append>
17 aload_1
18 invokevirtual #5 <java/lang/StringBuilder.append>
21 invokevirtual #6 <java/lang/StringBuilder.toString>
24 astore_2
25 return
可以看到字符串拼接底层是用的StringBuilder,最后再调用了toString方法。我们再来看看StringBuilder的toString方法,可以看到它底层也是new了一个String对象。但是注意这个对象不会进入常量池
验证不会加入常量池
如果会加入常量池,那么下面代码应该是相等的
双引号 + new
public class TestString_6 {
public static void main(String[] args) {
test2();
}
public static void test2() {
String s1 = "1";
String s2 = new String("1");
String s = s1 + s2;
}
}
字节码指令
0 ldc #2 <1>
2 astore_0
3 new #3 <java/lang/String>
6 dup
7 ldc #2 <1>
9 invokespecial #4 <java/lang/String.<init>>
12 astore_1
13 new #5 <java/lang/StringBuilder>
16 dup
17 invokespecial #6 <java/lang/StringBuilder.<init>>
20 aload_0
21 invokevirtual #7 <java/lang/StringBuilder.append>
24 aload_1
25 invokevirtual #7 <java/lang/StringBuilder.append>
28 invokevirtual #8 <java/lang/StringBuilder.toString>
31 astore_2
32 return
可以看到也是一样的
String对象的intern方法
简单来说:就是查看常量池是否有该对象,如果没有就创建一个返回。