一、String的特性
字符串常量池是不会存储相同的字符串的。
1.为什么jdk9要将String底层改为byte[]?
因为大多数String对象仅仅包含一个拉丁文,只需要一个字节就可以存储,导致一般的空间没有被使用到,节省了空间,减少了GC的触发次数,提高了系统的性能。
但是处理中文时,(LATIN1编码集支持的字符有限,其中就不支持中文字符,因此才保留了UTF16兜底)。
2.设置字符串常量池的大小
二、String的内存分配
1.StringTable为什么要从永久代调整到堆中?
①永久代内存较小 ②永久代垃圾回收频率较低
三、字符串的拼接
1.示例代码
/**
* s1 + s2 的执行细节:
* ①StringBuilder sb = new StringBuilder();
* ②sb.append("a")
* ③sb.append("b")
* ④sb.toString(); --> 约等于 new String("ab")
*/
@Test
public void test(){
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2;
System.out.println(s3 == s4);//false
}
2.StringBuilder的append方法效率比字符串之间拼接高
在开发中:如果基本确定要添加的字符串长度不超过某个限定值highLevel的情况下,建议使用构造器:StringBuilder sb = new StringBuilder(highLevel);
四、intern()的使用
1.public native String intern();
2.典型题目示例
public class StringInternTest {
public static void main(String[] args) {
String s = new String("1");
//s = s.intern(); //这样s = s2才为true
s.intern(); //调用此方法前“1”就已经存在与常量池中了,存在就会返回一个指向常量池的值,但是这里s没有接收该值,所以为false
String s2 = "1";
System.out.println(s == s2);//false
String s3 = new String("a") + new String("b");//调用toString,在字符串常量池中不会生成"ab"
//执行完上行代码后,字符串常量池中不存在“ab”
s3.intern(); //jdk6:在字符串常量池中生成“ab”,s4->字符串常量池“ab”,s3在堆空间, 所以为false
//jdk7/8:由于堆空间已经存在“ab”,这时字符串常量池会保存堆空间“ab”的地址
String s4 = "ab"; //s4->字符串常量池“ab”->s3,所以为true
System.out.println(s3 == s4);//jdk6:false jdk7/8:true
}
}
3.总结String和intern()的使用
4.使用intern()测试效率:空间上
对于程序中大量存在的字符串,尤其其中存在很多重复的字符串时,使用intern()可以节省很多内存空间。(由于堆中已经有的字符串,常量池中会将该对象的引用地址复制放入其中,而不会去新建一个字符串)
五、G1中String的去重操作