java.lang.String做为一个最经常使用的类,出如今各类各样的编程环境中。而其实现方式的不一样可能形成运行效率的大大不一样。只有深刻了解JVM对String的实现机制,才能更好的利用String。java
String是一个final类,所以它不能被继承,同时,用以存储字符串内容的char value[]数组是private、final的,所以一样不能改变它的内容。下面是String类的源码:编程
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; ...
那么在JVM中是怎么对String进行更新处理的呢?答案是新建一个String类,将原引用指向这个新String类。数组
public class Main { public static String test(String s){ return s.toUpperCase(); } public static void main(String []args){ String s1 = "asd"; String s2 = test(s1); System.out.println("s1 is : " + s1 + " and s2 is : " + s2); } } /×× s1 is : asd and s2 is : ASD ×/
s1做为一个拷贝传递给了test函数,返回了一个新的对象引用,而s1自己并无改变。安全
可是不变性形成的一个麻烦就是浪费空间,下降效率,以下代码: 多线程
String s = "a" + "b" + "c";
若是不考虑JVM所作的优化(直接生成一个字符串"abcd",而不是运行时合成;或优化使用 StringBuilder),那么这句语句在执行的时候就会先生成一个"a",而后"a"和"b"组合生成字符串"ab",而后生成"abc",其效率可想而知(听说java最早版本就是如此)。可是上面这个例子,因为都是不可变的字符串,所以JVM会一次性合成"abc",进行优化。那么合成串中包含变量呢?代码以下:app
String a = "adsf"; String s = "a" +"b" +"c" + a;
JVM在处理这段代码的时候就会用到StringBuiler类。jvm
StringBuiler是一个可变的普通类。能够调用append()的方法直接在当前串后面加上新串,而不会生成新的StringBuilder,它是final的,可是,它所实现的AbstractStringBuiler抽象类中,储存值的char[] value不是final的,这点与String不一样。函数
使用相似上面String的代码加以说明,运行后s1和s2的值同时改变了,可见s.append()并非返回一个新串,而是在引用指向的对象上动手脚。优化
public class Main { public static StringBuilder test(StringBuilder s){ return s.append("d"); } public static void main(String []args){ StringBuilder s1 = new StringBuilder("abc"); StringBuilder s2 = s1; test(s1); System.out.println("s1 is : " + s1 + " and s2 is : " + s2); } } /×× s1 is : abcd and s2 is : abcd ×/
回到刚才声明String的代码,ui
String a = "adsf"; String s = "a" +"b" +"c" + a;
让咱们看一看JVM是怎么处理它的,经过javap -c Main命令查看Main的jvm汇编程序:
Compiled from "Main.java" public class Main { public Main(); 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 adsf 2: astore_1 3: new #3 // class java/lang/StringBuilder 6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 10: ldc #5 // String abc 12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: aload_1 16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_2 23: return }
先说说几个指令的含义:
main函数中,首先直接合成字符串"adsf"放入栈顶,而后存入局部变量s。接下来建立一个StringBuiler类,初始化,接着调用append()方法将abc加入,再次调用append()加入adsf,而后调用toString()输出。
可见在复杂的String增加中,JVM基本上都会对String进行优化,使用StringBuiler。可是这个优化并非始终最好的,不能认为有JVM就高枕无忧:
public static void main(String []args){ String add[] = "q w e r t y u i o p".split(" "); String str = ""; for(int i = 0;i < add.length;i++){ str += add[i]; } }
以上代码在一个循环中,每次为str加上一个String,看看它的实现:
public static void main(java.lang.String[]); Code: 0: ldc #2 // String q w e r t y u i o p 2: ldc #3 // String 4: invokevirtual #4 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 7: astore_1 8: ldc #5 // String 10: astore_2 11: iconst_0 12: istore_3 13: iload_3 14: aload_1 15: arraylength 16: if_icmpge 46 19: new #6 // class java/lang/StringBuilder 22: dup 23: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V 26: aload_2 27: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 30: aload_1 31: iload_3 32: aaload 33: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 36: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 39: astore_2 40: iinc 3, 1 43: goto 13 46: return
能够看到,在循环体中(13~43),每次循环,都会新建一个StringBuiler对象。显然JVM在此处犯蠢了,所以咱们不该该寄但愿于JVM进行优化,更好的方法是直接将str声明为StringBuiler:
public static void main(String []args){ String add[] = "q w e r t y u i o p".split(" "); StringBuilder str = new StringBuilder(); for(int i = 0;i < add.length;i++){ str.append(add[i]); } }
这样就只会有一个StringBuiler对象被建立:
public static void main(java.lang.String[]); Code: 0: ldc #2 // String q w e r t y u i o p 2: ldc #3 // String 4: invokevirtual #4 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 7: astore_1 8: new #5 // class java/lang/StringBuilder 11: dup 12: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 15: astore_2 16: iconst_0 17: istore_3 18: iload_3 19: aload_1 20: arraylength 21: if_icmpge 38 24: aload_2 25: aload_1 26: iload_3 27: aaload 28: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: pop 32: iinc 3, 1 35: goto 18 38: return
所以,了解String和StringBuiler的实现,对写出高效的程序颇有必要。
有过了解的应该还知道有一个StringBuffer的类,它一样实现了AbstractStringBuiler接口,大部分函数和StringBuiler功能相同,可是区别就在于这些函数前面加了synchronized关键字,保证线程安全。所以其运行速度要比StringBuiler稍微慢一些,可是保证了多线程下数据的准确性。这里就再也不多说了。