串池: StringTable,可以理解为一个对象数组 [ "a" ,"b","ab"] 每一个元素都是一个字符串对象
1. 常量池与串池的关系
String s1="a";
String s2="b";
String s3="ab";
上述代码的运行过程:
常量池中的信息会被加载到运行时常量池中,这时 a b ab 都是常量池中的符号,还没有变成Java字符串对象
当编译过后运行到对应的行数 会执行
-
ldc #2 将 a 符号变成 “a” 字符串对象
-
ldc #3 将 b 符号变成 “b” 字符串对象
-
ldc #4 将 ab 符号变成 “ab” 字符串对象
执行第一句后会去串池中比较,如果找到了,就把串池中的value赋给这个String对象,如果找不到信息,就把 “a” 字符串对象放进池中
附加:StringTable是一个hashtable结构,不能扩容
2. 字符串拼接
String s4=s1+s2;
编译之后在jvm中的拼接过程
new String = new StringBuilder().append(s1).append(s2).toString
-
面试题:以上述条件为基础 s4==s3 的结果是什么
-
答案:false
-
原因:s3声明并赋值后存储在串池中,而从s4的运行结果来看最后调用了toString方法,查看toString方法,会发现它使用new关键字创建了新的String对象,是存储在堆内存中的,他们两个并不是一个对象,这也是证明了字符串的比较不能使用 == 而要使用equals方法,
-
详细内容可以参考本人的另一篇文章hashcode重写必要性:直观方式理解重写hashcode的必要性-CSDN博客
-
-
理解性延展
String s1="a"; String s2="a";
这里s1==s2的结果为true,因为这里操作的是串池,再次声明 "a"对象会直接去串池寻找,找到了直接返回,是同一个字符串对象
String s1="a"; String s2="a"; String s3=s1+s2; String s4=s1+s2;
这里s3==s4的结果为false,依照上面的内容,s3与s4都会分别new一个新的String对象,在堆内存中指向两个不同的对象,只是他们作为字符串展示出来的结果一致而已
3. 编译期优化
String s1="ab";
String s2="a"+"b";
System.out.println(s1==s2)
上述代码运行结果为true
Javac在编译期的优化,由于"a"和"b"都是常量,结果在编译器就已经确定为"ab" 但是上一个字符串拼接的内容是变量相加,所以不会优化
4. intern方法
//a存放在串池中
String a="ab"
//堆中分别new了两个对象 "a" "b" 拼接后返回一个String对象 "ab" 所以变量b是存储在堆中的
String b=new String("a")+new String("b")
//尝试将b放入串池,如果有则不放入,直接返回串池中的对象,这里放入失败,b仍然在堆中,返回的是串池中的对象,本质上和a是一个对象
String s= b.intern();
System.out.println(a==b);
System.out.println(b==s);
上述代码的结果为 false true
过程分析:①串池中放入一个 "ab" 对象 ,②堆中new了一个由 "a" "b" 拼接的对象 ③尝试将b放入串池,发现串池中已经存在"ab"对象,直接返回给s
误区分析:变量b 如果没有inern成功的话,仅仅是一个存放在堆中的对象,如果调用intern方法之后,发现串池中没有这个字符串对象,那么就会把 b 所引用的堆中的对象放入串池然后返回,所以没有a变量的情况下 b是存放在堆中的,如果有a变量,则b从堆中移动到串池中
扩展:jdk1.6之前 如上代码调用intern的时候,是把b复制一份放入串池,也就是说无论如何b都是一个存放在堆中的对象