据我所知字符串确实已经成为 Java 开发人员最常用的类了,而且是大量使用。我们都知道,String 其实是封装了字符,所以俩字符串连接就是将字符串对象里面的字符连起来。很多人习惯使用 +
来连接字符串,也有人会用 StringBuilder 的 append
方法。
看看如果我们在程序中直接使用 +
来连接字符串的情况,用下面一个简单的例子来说明,进行两个字符串连接操作,即 s3 = s1 + s2
。
public class TestString { public static void main(String[] args) { String s1 = "www"; String s2 = "ccc"; String s3 = s1 + s2; } }
接着 javap -c TestString.class
看一下编译后的情况,可以看到编译器其实是对 +
进行了转换的,转成了 StringBuilder 对象来操作了,首先使用 s1 创建 StringBuilder 对象,然后用 append
方法连接 s2,最后调用 toString
方法完成。
public class com.seaboat.string.TestString { public com.seaboat.string.TestString(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #16 // String www 2: astore_1 3: ldc #18 // String ccc 5: astore_2 6: new #20 // class java/lang/StringBuilder 9: dup 10: aload_1 11: invokestatic #22 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 14: invokespecial #28 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 17: aload_2 18: invokevirtual #31 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: invokevirtual #35 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 24: astore_3 25: return }
前面可以看到 +
在编译器作用下都会转成 StringBuilder 的 append
方法执行,所以如果抛开运行效率来说,它们其实本质是一样的。
本质一样是否就能说明它们时等价的呢?或者说能否为了方便直接用 +
来连接字符串,剩下的事就交给编译器了?继续看个例子,在这个例子中有个 for 循环进行字符串连接操作。
public class TestString2 { public static void main(String[] args) { String s = "www"; for (int i = 0; i < 10; i++) s += i; } }
编译后的情况如下,不熟悉指令没关系,我们只看重要的部分, if_icmplt 8
,这个就是 for 循环的条件判断,小于10则不断跳到8的位置,8后面其实就是创建 StringBuilder 对象,并以本地变量s的值初始化该对象,接着再将本地变量i append
到 StringBuilder 对象中,最后调用 toString
方法将所得值存到本地变量s。
这样来看循环中每次都要创建 StringBuilder 对象,而且要调用 toString
方法,这样的执行效率显然比较低,而且增加了 GC 的压力。
public class com.seaboat.string.TestString2 { public com.seaboat.string.TestString2(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #16 // String www 2: astore_1 3: iconst_0 4: istore_2 5: goto 30 8: new #18 // class java/lang/StringBuilder 11: dup 12: aload_1 13: invokestatic #20 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 16: invokespecial #26 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 19: iload_2 20: invokevirtual #29 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 23: invokevirtual #33 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 26: astore_1 27: iinc 2, 1 30: iload_2 31: bipush 10 33: if_icmplt 8 36: return }
把事情都丢给编译器是不友好的,为了能让程序执行更加高效,最好是我们自己来控制 StringBuilder 的实例,比如下面,只创建一个 StringBuilder 实例,后面用 append
方法连接字符串。
public class TestString3 { public static void main(String[] args) { StringBuilder sb = new StringBuilder("www"); for (int i = 0; i < 10; i++) sb.append(i); } }
编译后的情况如下,首先创建一个 StringBuilder 对象,使用字符串"www"来实例化该对象,接着循环调用 append
方法将本地变量i添加到 StringBuilder 对象中。
public class com.seaboat.string.TestString3 { public com.seaboat.string.TestString3(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #16 // class java/lang/StringBuilder 3: dup 4: ldc #18 // String www 6: invokespecial #20 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 9: astore_1 10: iconst_0 11: istore_2 12: goto 24 15: aload_1 16: iload_2 17: invokevirtual #23 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 20: pop 21: iinc 2, 1 24: iload_2 25: bipush 10 27: if_icmplt 15 30: return }
-------------推荐阅读------------
我的2017文章汇总——机器学习篇
我的2017文章汇总——Java及中间件
我的2017文章汇总——深度学习篇
我的2017文章汇总——JDK源码篇
我的2017文章汇总——自然语言处理篇
我的2017文章汇总——Java并发篇
跟我交流,向我提问:
公众号的菜单已分为“读书总结”、“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。
为什么写《Tomcat内核设计剖析》
欢迎关注: