关于 String,StringBuilder,StringBuffer
关于 String,StringBuilder,StringBuffer 的讨论,已有很多文章;在这里,我希望能刨根问底,更进一步的理解其中的原理。
- String
String 是final类型,不可继承的类;内部存储是字符数组(char[]),也是final ,不可更改;
/** 源码中 String 类的声明 */ public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[];
我们知道 final 修饰变量,只能被赋值一次,赋值成功后,不可再重新赋值。这意味怎么什么呢?先看一下下面的例子:
public static void main(String[] args) { String a = "sdfsdklfjdskl1245"; String b = "1234567489123"; String c = a + b; System.out.println(c); }
这里声明三个字符常量,在初始化时,a和b是字符数组常量,而c则是两个常量字符数组的连接;不信请看编译后的代码:
F:\opensource\panda-demo\src\test\java>javap -c Test Compiled from "Test.java" public class Test { public Test(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // String sdfsdklfjdskl1245 2: astore_1 3: ldc #3 // String 1234567489123 5: astore_2 6: aload_1 7: aload_2 8: invokedynamic #4, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; 13: astore_3 14: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 17: aload_3 18: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 21: return } F:\opensource\panda-demo\src\test\java>
(小弟知识范围有限,若上述代码解读有误,还请指正,不胜感激)
所以 String 字符串的拼接,是不会有性能问题的;那有些面试题说的字符串拼接性能低效又是怎么回事呢?请看下面例子:
public class Test { public static void main(String[] args) { String a = "123456"; for (int i=0; i<10; i++) { a += "dfdsfdsfds"; } System.out.println(a); } }
上面代码中,我们初始化了常量a,并且在循环里面做了多次字符串的拼接,最终a的指针地址指向了字符串拼接后的结果。这里存在以下问题:
1、字符拼接过程产生了大量的字符数组;我们通过第一个例子,知道了字符串底层是字符数组存储,且是不可变的,而字符串拼接是字符数组连接的副本。
2、大量指针的操作;循环中,需要不断调整变量a的值,指向连接后的数组,难免有指针的操作。
综上,String 字符串拼接性能低效,是和 StringBuilder、StringBuffer 做比较的,当然,没有比较,也就没有低效和高效的说法。
再看看 StringBuilder 和 StringBuffer 内部又是怎么玩的。
- StringBuilder
- StringBuffer