创建字符串的方式:
String 是final的,jdk1.8的时候,底层是一个char[ ] , jdk1.9的时候用的是 byte[ ],原因是堆空间中有大量的字符串,实际使用时发现大部分字符串都是拉丁文,1个字节就可以存放,而char 需要2个字节,所以为了节约空间,和减少GC的次数,就把char[ ] 改成了 byte[ ] ,在我们创建字符串的时候,会尝试压缩字符串,
也不光是String做了修改,StringBuilder,StringBuffer都做了相应的修改
String是不可变的:任何对原有的字符串进行的修改操作,其实都是创建了新的字符串
字符串常量池不会存储相同内容的字符串
因为8种基本数据类型和String都非常常用,为了提高使用效率,都有常量池的概念,类似于一个java系统级别的缓存,8种基本数据类型的常量池是系统来协调的,String类型的比较特殊,有两种主要的使用方法:
1.6时,字符串常量池在永久代,1.7就到了堆中,1.8去除了永久代,但是字符串常量池依然保留在了堆中,
调整字符串常量池的原因:
所有的字符串都放在了堆中,如果空间不足,可以直接调整堆的大小就好,而永久代的空间较小,不方便优化
永久代的垃圾回收频率很低
该代码对应的字节码文件如下
public static void main(String[] args) {
String aa = "aaa";
String bb = "bb";
String cc = aa + bb;
}
但是如果,两个字符串都是常量,在编译期就被优化掉了,就不会使用 StringBuilder 拼接了,所以实际工作中,对于可以使用final修饰的,尽量使用final修饰
public static void main(String[] args) {
final String aa = "aaa";
final String bb = "bb";
String cc = aa + bb;
}
通过StringBuilder 的append() 来拼接字符串的效率要远远高于 字符串的直接拼接
如果不是使用双引号声明的字符串,可以使用intern() 方法,该方法会使用equals()判断常量池中有没有该字符串,存在就返回常量池中的地址,不存在就把对象放入池中,并返回对象的地址
想要保证字符串在字符串常量池中:
关于intern的使用:
//这个操作会创建两个对象,一个是在字符串常量池中创建的,另一个是堆中用new创建的
new String("ab") ;
下面的代码有六个对象:
public static void main(String[] args) {
String ss = new String("a") + new String("b");
}
StringBuilder 的 toString() ,StringBuilder 中的数据不为空,就 new 一个新的 字符串,上面就是new String(“ab”),但是这里的“ab” 并不在字符串常量池中
private transient char[] toStringCache;
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
下面代码在jdk1.6和jdk1.7以后的执行结果不一样,因为1.7的字符串常量池从1.6的元空间移到了堆中
public static void main(String[] args) {
String s1 = new String("1");
s1.intern();
String s2 = "1";
System.out.println(s1 == s2); //false
//这里最终结果相当于 new String("11"),但是字符串常量池并没有“11”
String s3 = new String("1") + new String("1");
s3.intern(); //在字符串常量池中生成“11” ,在jdk1.6中是创建了一个新的对象,所以结果为false,
//而1.7以后,字符串常量池在堆中,为了节省空间,调用intern方法,需要向字符串常量池中存在字符串时,如果发现
//堆中已经有了该字符串,就不会在字符串常量池中创建新的字符串了,而是直接引用堆中的字符串
String s4 = "11"; //这里使用的是上一行代码生成的“11” 的地址
System.out.println(s3==s4); //1.6 false , 1.7以后 true
//所以在1.7以后,常量池中的地址和s3相等
}
字符串常量池中既有字符串对象,又有引用
如果有大量的重复字符串,使用intern 可以节约内存空间,(虽然也会创建很多对象,但是会及时回收掉,直接从常量池中使用,而不是直接使用堆中的对象)