问题: 理解 Java 的字符串,String、StringBuffer、StringBuilder 有什么区别?
String类由final关键字修饰,表示不可继承;存放数据的数据结构是char[],也是final修饰,同时String类中也没有提供可以直接操作char[]的方法,所以每次使用拼接、裁剪字符串等方法,都会生成一个新的String对象。又由于字符串的操作频繁,对应用性能会有明显影响。
StringBuffer就是用来解决由于操作频繁生成太多的STtring对象的问题,它通过append或者add方法来添加数据,它存放数据的数据结构也是char[],但它没有final修饰,每个操作字符数组的方法都是线程安全的,都有synchronized修饰。但是在保证线程安全的同时,难免避免不了额外的性能消耗。这时候,StringBuilder出现了,StringBulider就是一个线程不安全的类,操作数据的对应方法没有synchronized关键字修饰。
1、String 常量池
由于String操作频繁,通过String常量池来优化应用性能,字符串常量池相当于一个字符串缓冲,保存的数据时字符串对象。
JDK版本1.8 之后JVM把字符串常量池从方法区移到堆内存,并且方法区的实现也从永久代变成元空间。
- 当我们使用字符串面量值的方式创建字符串,它就会去查看字符串常量池,如果有,则直接指向;如果没有,则创建一个字符串对象加入后并指向;
- 当我们使用New String(xxx)的方式来创建字符串对象时,它会在堆上新开辟一块空间来存放字符串对象,并不会与字符串常量池挂钩,不过,我们可以通过intern()方法来达到字符串引用指向字符串常量池的效果
String a = "asd"; //加入常量池
String b = new String("asd"); // 不加入常量池,在堆上新开内存
我们可以通过比较“==“的方式,来得出他们指向地址不一致。
2、String特性
String对象是不可变的,每次操作都会创建新的对象。但它的不可变性在多线程访问的时候,就可以省略同步和锁等待的时间,降低程序设计难度。同时在进行对象拷贝时,也正是它的不可变性,他们可以引用常量池中的同一个对象。
3、“+”链接符的优化
字符串之间通过"+"来进行拼接,Java对其进行了优化。
String s = "abc";
for (int i=0; i<10000; i++) {
s += "abc";
}
/**
* 反编译后
*/
String s = "abc";
for(int i = 0; i < 1000; i++) {
s = (new StringBuilder()).append(s).append("abc").toString();
}