详解StringTable

详解StringTable

String的基本特性

  • 1.9之前String使用char[]数组实现的,1.9之后使用byte[]数组实现,因为大部分存储的字符串都是datin-1(用一个字节就可以表示),所有如果使用byte[]可以节省一半的空间,对于中文这些必须用两个byte的,对其进行标记
  • String是不可变的字符串
  • 字符串常量池是不会存储相同的字符串的,底层是通过HashSet来实现不重复的,所有设置常量池大小越大,不容易产生哈希碰撞,效率越好(比如在xxx.intern()时,需要先看对应位置有没有重复的xxx的操作)
  • 注意这个HashSet存储的是字符串的地址,因为字符串的实现其实是一个char[]数组(1.9之前)

String的内存分配

  • 1.6以及之前,字符串常量池在永久代
  • 1.7时,调整到了堆中,因为永久代比较小,放大量字符串会OOM,而字符串操作很频繁,永久代垃圾回收频率低
  • 1.8方法区变成元空间实现,字符串常量池仍在堆中

dd

字符串的拼接操作

  • 常量与常量的拼接结果也在常量池,编译期优化
  • 只要有一个是变量,结果就在堆中,变量拼接的原理是StringBuilder,而且每次拼接都换创建一个新的StringBuilder
  • 如果拼接结果主动调用intern(),主动将字符串常量池中还没有的字符串放入池中,返回对象地址

案例一:常量拼接

String s1 = "a" + "b" + "c";   // 编译器就会进行优化,对于这种常量拼接,直接变成拼接后的结果
String s2 = "abc";
s1 == s2;  // true    因为编译器优化,s2已经存在在常量池中,直接返回其结果

案例二:变量拼接,会在堆中new一个String对象出来,所有地址会不同(如果变量被final修饰,当作常量处理)

String s1 = "javaEE";
String s2 = "hadoop";

String s3 = "javaEEhadoop";

String s4 = "javaEE" + "hadoop";   
s3 == s4?  // true  同案例一
    
String s5 = s1 + "hadoop";
s3 == s5; // false  s5是堆区对象

String s6 = "javaEE" + s2;
s3 == s6; // false 同上

String s7 = s1 + s2;
s3 == s7; // false 同上

案例三:intern()

String s1 = "java"; 
String s2 = "javahello";
String s3 = s1 + "hello";  // 变量拼接 new出来的对象在堆区
String s4 = s3.intern();  // 调用intern返回结果
s2 == s4; //true 调用intern会返回在字符串常量池中的查询结果,结果有,并且与s2的地址相同(如果不存在,会加载一份,返回其地址)

intern()方法

当前字符串.intern(); 去字符串常量池中找该字符串,存在就返回其地址,不存在就创建一个字符串在字符串常量池中,并返回其地址

分析下面代码

String s1 = new String("a") + new String("b");   // StringBuilder并不会在底层把”ab“添加到字符串常量池
s1.intern();// ”ab"添加进常量池       
String s2 = "ab"; // 常量池 
System.out.println(s1 == s2); // true

为什么会是true? 首先经过测试 ,答案是true没有错,但是很奇怪,因为s1是在堆中的,s2是常量池中对象,为什么会相等

  • jdk7之前,这段代码是false,因为当时字符串常量池在方法区
  • 但是到了jdk7时,将字符串常量池转移到了堆区,这时如果将new 出来的String对象调用intern(),就是在常量池中的一个区域中添加数,添加的不是“ab”,而是这个String对象的地址,所有s1== s2时,s2虽然在常量池,但它保存的数据就是s1的地址\
  • 总结:new String(“ab”)会有两个对象,new + new 不会在常量池创建一个结果对象,这时候如果时高版本,调用intern()就会创建一个常量池对象,但对象中不是字符串而是new+new产生对象的地址

intern

new一个String对象,到底创建了几个对象?

代码: new String(“hello”);

答案:2个

  1. new关键字在堆中创建的对象
  2. 字符串常量池中的对象

代码:new String(“a”) + new String(“b”);

答案:6个

  1. 常量池拼接底层会new StringBuilder();
  2. new在堆中的“a”对象
  3. 常量池中“a”
  4. new在堆中的“b”对象
  5. 常量池中“b”
  6. StringBuilder().toString() new出来的“ab”对象
  7. “ab”? 有没有ab呢,StringBuilder返回字符串使用toString()方法,就是new String(“ab”); 但是这个new 并不会在常量池中添加“ab”

对象
7. “ab”? 有没有ab呢,StringBuilder返回字符串使用toString()方法,就是new String(“ab”); 但是这个new 并不会在常量池中添加“ab”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

踢足球的程序员·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值