String是Java基础类型中较为特殊的一个类,我们这里着重探讨一下这个类。
String底层使用一个char类型的数组来存储字符串,且该数组使用了private和final修饰
因而String是不可变的
由于不可变,进而可以理解为常量,因此所以线程安全
StringBuffer与StringBuilder均继承自AbstractStringBuilder(构造器模式),因此我们先来介绍一下AbstractStringBuilder类。
AbstractStringBuilder类中使用一个char类型的数组来存储字符串,同时为我们提供了大量字符串修改操作(insert,append…)
StringBuilder的效率高于StringBuffer。
Java9将上面三个类的底层char数组改成了byte数组
String的+与+=是Java唯二两个重载的运算符,我们通过观察字节码可以发现String的+和+=是创建了一个StringBuilder类来完成这一操作。
但是在for循环中,字节码无法优化成自创建一个StringBuilder类(即for每进行一次循环就要新创建一个StringBuilder),因此我们仍然建议存在大量修改的情况下使用StringBuilder或StringBuffer
JVM为了提升性能减少内存消耗而专门为字符串开辟了一块区域,我们称呼为字符串常量池。
String s1 = "Java";
String s2 = "Java";
s1赋值时,JVM会先看常量池中是否有这个对象,如果没有则创建该对象在常量池中,并返回常量池中的这个对象。
s2赋值时,JVM发现常量池中已经有这个对象,因此直接常量池中这个对象的引用赋值给s2。
因此s1==s2
使用构造方法创建字符串变量时,一般不会和常量池有关系。
但特别的,如果使用了以下形式创建字符串
String a=new String("123");
由于使用了双引号变量,因此会使用常量池。
一:
二:
使用从常量池中得到的引用去初始化一个新的对象并放到堆中(浅拷贝)
intern方法的意义是通过调用该方法的String变量的值去获取常量池中该与该变量值相同的对象并将其引用返回。
这又分为以下两种情况
注意:使用intern的过程并不会改变调用intern方法的对象
说了这么多,我们究竟会在什么场景使用intern呢?
我们这里带来一段fastjson的源码
public Entry(char[] ch, int offset, int length, int hash, Entry next){
characters = new char[length];
System.arraycopy(ch, offset, characters, 0, length);
symbol = new String(characters).intern(); //核心
this.next = next;
this.hashCode = hash;
this.bytes = null;
}
通过使用intern方法,将字符串缓存到了字符串常量池中,同时给到变量的是常量池中的对象而不是构造方法创建的对象,
这样一来后续凡是使用到逻辑相同的变量均是常量池中的同一变量,因此加快了速度。