博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从字节码角度看String、StringBuffer、StringBuilder的不同
阅读量:6453 次
发布时间:2019-06-23

本文共 16631 字,大约阅读时间需要 55 分钟。

hot3.png

javap将一个或多个类文件进行分解。使用简要说明javap [options] classfile...options命令行选项,详细查看后面的Options介绍classfile一个或多个Class文件(多个使用空格分离),可以使用文件路径或者classPath下的文件或者输入URLDescriptionjavap命令分解卸一个或多个类文件。输出取决于所使用的选项。当没有使用任何选项,那么javap命令打印方案为protected和公共字段和方法。javap命令将输出打印到控制台。Options-help--help-?打印帮助信息-version打印版本信息-l打印行内变量以及局部变量-public显示public访问修饰的内容-protected显示protected、public访问修饰的内容-private-p显示所有的内容-Joption将指定的选项传递给JVM。例如:javap -J-versionjavap -J-Djava.security.manager -J-Djava.security.policy=MyPolicy MyClassNameFor more information about JVM options, see the java command documentation.-s打印内部类签名-sysinfo显示系统信息(路径、大小、日期、MD5哈希)的类处理。-constants显示静态常量-c为每个类中的方法打印反汇编代码,例如,Java字节码指令组成,。-verbose打印堆栈大小,局部变量的数目和方法的参数。Prints stack size, number of locals and arguments for methods.-classpath path指定javap命令使用的路径查找类。覆盖默认的或者当它被设置CLASSPATH环境变量。Specifies the path the javap command uses to look up classes. Overrides the default or the CLASSPATH environment variable when it is set.-bootclasspath path指定的路径加载引导类。默认情况下,引导类类,实现核心Java平台位于jre / lib / rt。jar和其他几个jar文件。Specifies the path from which to load bootstrap classes. By default, the bootstrap classes are the classes that implement the core Java platform located in jre/lib/rt.jar and several other JAR files.-extdir dirs增加一些扩展路径用以获取类库Overrides the location at which installed extensions are searched for. The default location for extensions is the value of java.ext.dirs.

###我们来看看String、StringBuffer、StringBuilder的不同 ####测试类

public class Test {    public static void main(String[] args) {        long start = System.currentTimeMillis();        for (int i = 0; i < 100000; i++) {            //我们一般拼接字符的时候都不会拼接太多次100次其实就算比较多了            contactStringWithLoop(100);        }        System.out.println(System.currentTimeMillis() - start);        //916毫秒        start = System.currentTimeMillis();        for (int i = 0; i < 100000; i++) {            //我们一般拼接字符的时候都不会拼接太多次100次其实就算比较多了            contactStringWithStringBuilder(100);        }        System.out.println(System.currentTimeMillis() - start);        //244毫秒        start = System.currentTimeMillis();        for (int i = 0; i < 100000; i++) {            //我们一般拼接字符的时候都不会拼接太多次100次其实就算比较多了            contactStringWithStringBuffer(100);        }        System.out.println(System.currentTimeMillis() - start);        //620毫秒    }    /**     * 直接拼接字符串     *     * @return     */    public static String contactString() {        String string = "直接" + "对字符串" + "进行" + "多次的拼接"                + "看看最后编译" + "的字节码" + "会是神马" + "样子" + "的";        return string;    }    /**     * 与上面的方法其实一致,只是在拼接中引入了一个多个变量     *     * @param str     * @return     */    public static String contactStringWithParam(String str, String str2, String str3) {        String string = "直接" + str3 + "进行" + "多次的拼接"                + "看看最后编译" + str + "会是神马" + str2 + "的";        return string;    }    /**     * 通过循环来拼接字符串     *     * @param loopCount 循环的次数     * @return     */    public static String contactStringWithLoop(int loopCount) {        String string = "";        for (int i = 0; i < loopCount; i++) {            string += i;        }        return string;    }    /**     * 使用StringBuffer循环拼接字符串     *     * @param loopCount     * @return     */    public static String contactStringWithStringBuffer(int loopCount) {        StringBuffer sb = new StringBuffer();        for (int i = 0; i < loopCount; i++) {            sb.append(i);        }        return sb.toString();    }    /**     * 使用StringBuilder循环拼接字符串     *     * @param loopCount     * @return     */    public static String contactStringWithStringBuilder(int loopCount) {        StringBuilder sb = new StringBuilder();        for (int i = 0; i < loopCount; i++) {            sb.append(i);        }        return sb.toString();    }}

####使用javap -c Test 来获得字节码信息

Compiled from "Test.java"public class Test {  public Test();    Code:       0: aload_0              1: invokespecial #1                  // Method java/lang/Object."
":()V 4: return public static void main(java.lang.String[]); Code: 0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 3: lstore_1 4: iconst_0 5: istore_3 6: iload_3 7: ldc #3 // int 100000 9: if_icmpge 24 12: bipush 100 14: invokestatic #4 // Method contactStringWithLoop:(I)Ljava/lang/String; 17: pop 18: iinc 3, 1 21: goto 6 24: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 27: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 30: lload_1 31: lsub 32: invokevirtual #6 // Method java/io/PrintStream.println:(J)V 35: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 38: lstore_1 39: iconst_0 40: istore_3 41: iload_3 42: ldc #3 // int 100000 44: if_icmpge 59 47: bipush 100 49: invokestatic #7 // Method contactStringWithStringBuilder:(I)Ljava/lang/String; 52: pop 53: iinc 3, 1 56: goto 41 59: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 62: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 65: lload_1 66: lsub 67: invokevirtual #6 // Method java/io/PrintStream.println:(J)V 70: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 73: lstore_1 74: iconst_0 75: istore_3 76: iload_3 77: ldc #3 // int 100000 79: if_icmpge 94 82: bipush 100 84: invokestatic #8 // Method contactStringWithStringBuffer:(I)Ljava/lang/String; 87: pop 88: iinc 3, 1 91: goto 76 94: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 97: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J 100: lload_1 101: lsub 102: invokevirtual #6 // Method java/io/PrintStream.println:(J)V 105: return public static java.lang.String contactString(); Code: 0: ldc #9 // String 直接对字符串进行多次的拼接看看最后编译的字节码会是神马样子的 2: astore_0 3: aload_0 4: areturn public static java.lang.String contactStringWithParam(java.lang.String, java.lang.String, java.lang.String); Code: 0: new #10 // class java/lang/StringBuilder 3: dup 4: invokespecial #11 // Method java/lang/StringBuilder."
":()V 7: ldc #12 // String 直接 9: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 12: aload_2 13: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 16: ldc #14 // String 进行 18: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: ldc #15 // String 多次的拼接 23: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: ldc #16 // String 看看最后编译 28: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: aload_0 32: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 35: ldc #17 // String 会是神马 37: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 40: aload_1 41: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 44: ldc #18 // String 的 46: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 49: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 52: astore_3 53: aload_3 54: areturn public static java.lang.String contactStringWithLoop(int); Code: 0: ldc #20 // String 2: astore_1 3: iconst_0 4: istore_2 5: iload_2 6: iload_0 7: if_icmpge 35 10: new #10 // class java/lang/StringBuilder 13: dup 14: invokespecial #11 // Method java/lang/StringBuilder."
":()V 17: aload_1 18: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: iload_2 22: invokevirtual #21 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 25: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 28: astore_1 29: iinc 2, 1 32: goto 5 35: aload_1 36: areturn public static java.lang.String contactStringWithStringBuffer(int); Code: 0: new #22 // class java/lang/StringBuffer 3: dup 4: invokespecial #23 // Method java/lang/StringBuffer."
":()V 7: astore_1 8: iconst_0 9: istore_2 10: iload_2 11: iload_0 12: if_icmpge 27 15: aload_1 16: iload_2 17: invokevirtual #24 // Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer; 20: pop 21: iinc 2, 1 24: goto 10 27: aload_1 28: invokevirtual #25 // Method java/lang/StringBuffer.toString:()Ljava/lang/String; 31: areturn public static java.lang.String contactStringWithStringBuilder(int); Code: 0: new #10 // class java/lang/StringBuilder 3: dup 4: invokespecial #11 // Method java/lang/StringBuilder."
":()V 7: astore_1 8: iconst_0 9: istore_2 10: iload_2 11: iload_0 12: if_icmpge 27 15: aload_1 16: iload_2 17: invokevirtual #21 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 20: pop 21: iinc 2, 1 24: goto 10 27: aload_1 28: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 31: areturn }

###我们来分一下分解出的字节码: #####contactString方法[在javac结果的64行]

public static java.lang.String contactString();    Code:       0: ldc           #9                  // String 直接对字符串进行多次的拼接看看最后编译的字节码会是神马样子的       2: astore_0             3: aload_0              4: areturn

0: ldc #9 // String 直接对字符串进行多次的拼接看看最后编译的字节码会是神马样子的这里可以看出,编译器直接将编译结果进行了转换,没有使用+而直接使用了拼接后的字符串(因为不包含变量的拼接,所以可以预想到最终结果) 结论:对java中字符串直接拼接时可以直接使用+的方式来拼接。 #####contactStringWithParam[在javac结果的71行]

public static java.lang.String contactStringWithParam(java.lang.String, java.lang.String, java.lang.String);    Code:       0: new           #10                 // class java/lang/StringBuilder       3: dup                  4: invokespecial #11                 // Method java/lang/StringBuilder."
":()V 7: ldc #12 // String 直接 9: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 12: aload_2 13: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 16: ldc #14 // String 进行 18: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: ldc #15 // String 多次的拼接 23: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 26: ldc #16 // String 看看最后编译 28: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: aload_0 32: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 35: ldc #17 // String 会是神马 37: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 40: aload_1 41: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 44: ldc #18 // String 的 46: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 49: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 52: astore_3 53: aload_3 54: areturn

0: new #10 // class java/lang/StringBuilder这句话是对应声明了一个StringBuilder,然后每次拼接实际使用的是StringBuilder的append方法。 结论:在针对这种没有循环但是有变变量拼接的字符串时,使用StringBuilder与使用String + 的方式没有区别,但是String + 的方式更加省时省力,而且相对清晰。 #####contactStringWithLoop[在javac结果的99行]

public static java.lang.String contactStringWithLoop(int);    Code:       0: ldc           #20                 // String        2: astore_1             3: iconst_0             4: istore_2             5: iload_2              6: iload_0              7: if_icmpge     35      10: new           #10                 // class java/lang/StringBuilder      13: dup                 14: invokespecial #11                 // Method java/lang/StringBuilder."
":()V 17: aload_1 18: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: iload_2 22: invokevirtual #21 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 25: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 28: astore_1 29: iinc 2, 1 32: goto 5 35: aload_1 36: areturn

首先先初始化循环变量:

0: iconst_0               1: istore_1

这两行代码相当于int i = 0这句代码(iconst_0 是数字 0,istore_1 就是表示局部变量1,这里就是源码里的 i 了)

5: iload_2       6: iload_0       7: if_icmpge     35

这三行意思就是 i 是否小于 10 ,小于则继续往下执行,否则就跳到 编号为 35的 return那里也即跳出for循环了,所以这里的实际意义是每次循环时新建一个StringBuilder,然后本次循环结束时返回StringBuilder的toString()的结果。 #####对比contactStringWithStringBuffer[在javac结果的122行]

public static java.lang.String contactStringWithStringBuffer(int);    Code:       0: new           #22                 // class java/lang/StringBuffer       3: dup                  4: invokespecial #23                 // Method java/lang/StringBuffer."
":()V 7: astore_1 8: iconst_0 9: istore_2 10: iload_2 11: iload_0 12: if_icmpge 27 15: aload_1 16: iload_2 17: invokevirtual #24 // Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer; 20: pop 21: iinc 2, 1 24: goto 10 27: aload_1 28: invokevirtual #25 // Method java/lang/StringBuffer.toString:()Ljava/lang/String; 31: areturn

上面是先new StringBuilder,之后只是对这个StringBuilder进行append。 结论:当使用变量并循环拼接字符串时,应该使用StringBuilder的方式。

main方法中的测试结果也可以看出使用String、StringBuilder、StringBuffer分别进行10000次的百次字符拼接,StringBuilder的性能远远高于StringBuffer和String。【StringBuffer是线程安全的吗,所以损耗一些性能但是也优于String,主要是因为每次new StringBuilder的代价比较大】

转载于:https://my.oschina.net/coderknock/blog/684696

你可能感兴趣的文章
查看自己的ip和采用什么方式上网(网通/电信)
查看>>
如何在程序打开时窗口恢复上次关闭的大小和位置?
查看>>
关于Java String对象创建问题解惑
查看>>
应该是Angular2的一个bug?
查看>>
Eclipse(Helios) 初始化失败的问题
查看>>
SQL Server 2012 数据库镜像配置完整篇
查看>>
MariaDB 10之TokuDB存储引擎
查看>>
Flex与.NET互操作(一):基于Socket的网络连接
查看>>
WPF中Style的使用
查看>>
组策略设置服务器安全-----不显示最后的用户名
查看>>
慢查询日志的另外一种方法
查看>>
引发CPU占用率快100%的可能原因
查看>>
公司今年的一道校招笔试题--五猴分桃
查看>>
[数据恢复答疑]RAID5有一块硬盘离线后,为什么不建议马上做REBUILD?
查看>>
DB2临时表在SQL过程和SQL语句中的测试总结
查看>>
Exchange 2013 EAC之管理员重置普通用户密码
查看>>
三线跑酷例子BlocksRun的技术点
查看>>
如何应对DDOS网络攻击
查看>>
新闻奖颁给了一个写稿机器人(来自新华社)
查看>>
Android应用程序在新的进程中启动新的Activity的方法和过程分析
查看>>