底层分析:
(1)String类
String类对象代表不可变的字符序列。其底层结构如下:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
private int hash; // Default to 0
private static final long serialVersionUID = -6849794470754667710L;
其核心本质是被final修饰的数组value[],即String类型的字符串内容全部存储到value[]数组中,并且该数组被final修饰,也就是只能被赋值一次,之后不能被改变。因此每次对String类进行改变的时候都会生成一个新的String对象,然后将指针指向新的String对象,所以经常要改变字符串的话,不要使用String类,因为每次生成对象都会对系统性能产生影响,特别是当内存中引用的对象多了以后,JVM的GC就会开始工作,性能就会降低。
(2)StringBuffer类
StringBuffer类对象代表线程安全的可变字符序列。StringBuffer类增加了线程安全的同步机制,而为了保证线程安全是需要付出性能降低的代价。其底层结构如下:
public final class StringBuffer extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
StringBuffer类继承了抽象类AbstractStringBuilder,而在抽象类中也使用了数组value[],并且该数组没有final修饰,即StringBuffer类型的字符串内容全部存储到value[]数组中,并且该数组未被final修饰,也就是可以被更改的。通过append(),delete(),insert()等方法可以更改字符串内容。
使用StringBuffer类时,每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用,所以在字符串对象经常改变的情况下,推荐使用StringBuffer类。
(3)StringBuilder类
StringBuilder类对象代表非线程安全的可变字符序列。StringBuilder类是JDK1.5发布的,它和StringBuffer类本质上没有什么区别,就是去掉了保证线程安全的部分,较少了开销,提高了效率。其底层结构如下:
public final class StringBuilder extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
StringBuilder类继承了抽象类AbstractStringBuilder,而在抽象类中也使用了数组value[],并且该数组没有被final修饰,即StringBuilder类型的字符串内容全部存储到value[]数组中,并且该数组未被final修饰,也就是可以被更改的。通过append(),delete(),insert()等方法可以更改字符串内容。
比较:
(1)长度是否可变
String类本质是被final修饰的char数组,因此长度是不可改变的,在对String字符串进行修改时,都是重新创建对象。
StringBuffer类和StringBuilder类本质是未被final修饰的char数组,因此可以被多次修改,并且不产生新的对象。
(2)执行效率
StringBuilder>StringBuffer>String
(3)应用场景
字符串不经常发生变化的业务场景下(如常量的声明等),用String类。
单线程情况下,如有大量的字符串操作(如JSON的封装),用StringBuilder类。
多线程情况下,如有大量的字符串操作(如HTTP参数解析和封装),用StringBuffer类。
测试代码如下:
public static void main(String [] args){
String str="";
long a=System.currentTimeMillis();
long a1=Runtime.getRuntime().freeMemory();
for(int i=0;i<10000;i++){
str=str+i;
}
long b=System.currentTimeMillis();
long b1=Runtime.getRuntime().freeMemory();
System.out.println("String字符串拼接用时:"+(b-a));
System.out.println("String字符串拼接使用的内存:"+(a1-b1));
StringBuilder s = new StringBuilder("");
long c=System.currentTimeMillis();
long c1=Runtime.getRuntime().freeMemory();
for(int j=0;j<10000;j++){
s.append(j);
}
long d=System.currentTimeMillis();
long d1=Runtime.getRuntime().freeMemory();
System.out.println("StringBuilder字符串拼接用时:"+(d-c));
System.out.println("StringBuilder字符串拼接使用的内存:"+(c1-d1));
}
上述代码中,测试了String类和StringBuilder类进行字符串拼接时的用时和内存占用情况。输出结果如下:
String字符串拼接用时:151
String字符串拼接使用的内存:1093896
StringBuilder字符串拼接用时:0
StringBuilder字符串拼接使用的内存:295008
可以看出使用String类进行字符串拼接时时间和空间效率都很低,因此在由频繁字符串拼接的情景下,避免使用String类,尽量使用StringBuffer类或者StringBuilder类(根据单线程和多线程决定具体使用哪一个)。
总结:
String类,StringBuffer类和StringBuilder类底层均使用了char数组存放字符序列。
String:char数组被final修饰,不可改变,修改String数组时,需要新创建String对象。
StringBuffer:线程安全,效率低。(多线程建议使用)
StringBuilder:非线程安全,效率高。(单线程建议使用)