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&#61;0;i<10;i&#43;&#43;){s.append("a");
}
在实际开发中若能确定前后添加的字符串长度不高于某个限定值highlevel&#xff0c;建议使用构造器
StringBuilder s &#61; new StringBuilder(highlevel)
intern()的使用
若在任意字符串上调用String.intern方法&#xff0c;那么其返回结果所指向的那个类实例&#xff0c;必须和直接以常量形式出现的字符串实例完全相同。所以
("a"&#43;"b"&#43;"c").intern()&#61;&#61;"abc" //true
Interned 就是确保字符串在内存里只有一份拷贝&#xff0c;节约内存空间
如何确保变量s指向的是字符串常量池中的数据
1. String s &#61;"snhstart";
2. String s &#61; new String ("snhstart").intern();
new String("ab")会创建几个对象 &#xff1f; //2个一个对象是new关键字&#xff0c;在堆空间创建的一个对象是字符串常量池中的对象&#xff0c;字节码指令&#xff1a;ldc
String str &#61; new String("a")&#43;new String("b") //对象1&#xff1a;new StringBuilder()对象2&#xff1a;new String("a")对象3&#xff1a;常量池中的"a"对象4&#xff1a;new String("b")对象5&#xff1a;常量池中的"b"toString()的调用在字符串常量池中&#xff0c;没有生成"ab"
String s&#61;new String ("1")
String s1 &#61; "1"
system.out.println(s&#61;&#61;s1);//false
s是堆空间中new的地址&#xff0c;s1是字符串常量池中"1"的地址String s2 &#61; new String("1")&#43;new String("1")
//s2变量记录的地址为&#xff1a;new String("11")的地址&#xff0c;此行代码后字符串常量池中没有生成"11"
s2.intern();//在字符串常量池中生产"11"
/*在jdk6中创建了一个新的对象&#xff0c;有新的地址
在jdk7中常量池中并没有创建“11”&#xff0c;而是创建了一个指向堆空间“11”的地址
因为7以后&#xff0c;字符串常量池放入堆空间&#xff0c;那么intern判断已经new了一个“11”就不在在字符串常量池中创一个“11”&#xff0c;而是创一个指向new “11”地址指针&#xff0c;所s3虽然是常量池中的“11”但是实际就是new的s2的地址
*/
String s3&#61;"11";
system.oout.println(s2&#61;&#61;s3);//jdk6:false,jdk7:true
G1的String去重操作
去底层new的char型数组的的重复的操作&#xff0c;并非常量池的
-
当垃圾收集器工作的时候回访问堆上存活的对象。对每一个访问的对象都会检查是否候选的要去重的对象
-
如果是&#xff0c;把这个对象的一个引用插入到队列中等待后续的处理。一个去重的线程在后台运行&#xff0c;处理这个队列&#xff1a;从队列删除这个元素&#xff0c;然后尝试去重它引用的String对象
-
使用hashtable来记录所有的被String对象使用的不重复的char数组&#xff0c;去重时差这个hashtable来看堆上是否已经存在一个一模一样的char数组
-
如果存在String对象会被调整引用那个数组&#xff0c;释放原来数组的引用&#xff0c;最终被垃圾收集器回收掉
-
如果查找失败&#xff0c;char数组会被插入到hashtable&#xff0c;这样以后就可以共享这个数组