String的基本特性
-
String:字符串,用""引起来表示
-
String 声明为final,不可以被继承
-
String实现了Serializable接口,表示字符串时支持序列化的
-
String实现了Comparable接口,表示String可以比较大小
-
JDK8之前用char[]存储字符串数据,JDK9改为byte[]
-
String代表不可变得字符序列
字符串常量池中是不会存储相同内容的字符串的
-
String的 String Pool是一个固定大小的Hashtable,默认大小长度是1009.
-
使用-xx:stringTableSize可设置StringTable的长度
-
JDK8中StringTable默认长度改为60013,1009是可设置的最小值
String的内存分配
常量池类似于一个Java系统级别提供的缓存,8种基本数据类型的常量池都是系统协调的。
对于特殊的String类型的常量池:
-
直接使用双引号声明出来的String对象会直接存储在常量池中
-
如果不是用""声明的String对象,可以使用String提供的intern()方法
Java6,字符串常量池放在永久代
Java7,将字符串常量池位置调整到了堆空间中
Java8,虽然去掉了永久代改为元空间,但是字符串常量还在在堆中
String的基本操作
字符串拼接操作
-
常量与常量的拼接结果在常量池,原理是编译期优化
-
常量池中不会存在相同的常量
-
只要其中有一个是变量结果就在堆里面
-
如果拼接使用intern()方法则主动将常量池中还没有的字符串对象放入池中,并返回对象地址
intern():判断字符串常量池中是否存在javaEEhadoop值,若存在则返回常量池中的地址,若字符串常量池中不存在,则在常量池中加载一份javaEEhadoop,并返回对象的地址
只要String拼接的连接符两边出现了变量就会new StringBuilder
String s1="a";
String s2="b";
String s3=s1+s2;
"s1+s2"的执行细节:
StringBuilder s =new StringBuilder();
s.append("a");
s.append("b");
s.toString();
StringBuilder的append()方法添加字符串的效率要远远高于直接String字符串拼接
因为append()方法自始至终只会创建一个StringBuilder对象
StringBuilder s =new StringBuilder();
for(int i=0;i<10;i++){
s.append("a");
}
在实际开发中若能确定前后添加的字符串长度不高于某个限定值highlevel,建议使用构造器
StringBuilder s = new StringBuilder(highlevel)
intern()的使用
若在任意字符串上调用String.intern方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。所以
("a"+"b"+"c").intern()=="abc" //true
Interned 就是确保字符串在内存里只有一份拷贝,节约内存空间
如何确保变量s指向的是字符串常量池中的数据
1. String s ="snhstart";
2. String s = new String ("snhstart").intern();
new String("ab")会创建几个对象 ? //2个
一个对象是new关键字,在堆空间创建的
一个对象是字符串常量池中的对象,字节码指令:ldc
String str = new String("a")+new String("b") //
对象1:new StringBuilder()
对象2:new String("a")
对象3:常量池中的"a"
对象4:new String("b")
对象5:常量池中的"b"
toString()的调用在字符串常量池中,没有生成"ab"
String s=new String ("1")
String s1 = "1"
system.out.println(s==s1);//false
s是堆空间中new的地址,s1是字符串常量池中"1"的地址
String s2 = new String("1")+new String("1")
//s2变量记录的地址为:new String("11")的地址,此行代码后字符串常量池中没有生成"11"
s2.intern();//在字符串常量池中生产"11"
/*在jdk6中创建了一个新的对象,有新的地址
在jdk7中常量池中并没有创建“11”,而是创建了一个指向堆空间“11”的地址
因为7以后,字符串常量池放入堆空间,那么intern判断已经new了一个“11”就不在在字符串常量池中创一个“11”,而是创一个指向new “11”地址指针,所s3虽然是常量池中的“11”但是实际就是new的s2的地址
*/
String s3="11";
system.oout.println(s2==s3);//jdk6:false,jdk7:true
G1的String去重操作
去底层new的char型数组的的重复的操作,并非常量池的
-
当垃圾收集器工作的时候回访问堆上存活的对象。对每一个访问的对象都会检查是否候选的要去重的对象
-
如果是,把这个对象的一个引用插入到队列中等待后续的处理。一个去重的线程在后台运行,处理这个队列:从队列删除这个元素,然后尝试去重它引用的String对象
-
使用hashtable来记录所有的被String对象使用的不重复的char数组,去重时差这个hashtable来看堆上是否已经存在一个一模一样的char数组
-
如果存在String对象会被调整引用那个数组,释放原来数组的引用,最终被垃圾收集器回收掉
-
如果查找失败,char数组会被插入到hashtable,这样以后就可以共享这个数组