• 漫画:老板扣了我1000,因为我没记住阿里巴巴开发手册的这条规则。


    本文故事构思来源于脉脉上的一篇帖子“一行代码引发的血案”。

    其实关于字符串的文章,我之前也写过一篇《诡异的字符串问题》,字符串对于我们开发者而言,可以用最近很流行的一句话“用起来好嗨哟,仿佛人生达到了巅峰”。

    确实大家都用的很嗨,很便利,但 JDK 的工程师在背后付出了努力又有几个人真的在意呢?

    咱们今天就通过一个例子来详细的说明。

    public class StringTest {
    public static void main(String[] args) {

    // 无变量的字符串拼接
    String s = "aa"+"bb"+"dd";
    System.out.println(s);
    // 有变量的字符串拼接
    String g = "11"+s+5;
    System.out.println(g);
    // 循环中使用字符串拼接
    String a = "0";
    for (int i = 1; i < 10; i++) {
    a = a + i;
    }
    System.out.println(a);
    // 循环外定义StringBuilder
    StringBuilder b = new StringBuilder();
    for (int i = 1; i < 10; i++) {
    b.append(i);
    }
    System.out.println(b);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    有同学可能会说,这么一段代码,怎么来区分呢?既然我在对话中说了 Java 从 JDK5 开始,便在编译期间进行了优化,那么编译期间 javac 命令主要干了什么事情呢?一句话归根结底,那么肯定就是把 .java 源码编译成 .class 文件,也就是我们常说的中间语言——字节码。然后 JVM 引擎再对 .class 文件进行验证,解析,翻译成本地可执行的机器指令,这只是一个最简单的模型,其实现在的 JVM 引擎后期还会做很多优化,比如代码热点分析,JIT编译,逃逸分析等。

    说到这里,我记得之前群里有同学说,字节码长得太丑了,看了第一眼就不想看第二眼,哈哈,丑是丑点,但是很有内涵,能量强大,实现了跨平台性。

    关于怎么查看字节码,我之前分享过两个工具,一个 JDK 自带的 javap,另一个IDEA的插件 jclasslib Bytecode viewer。今天给你再分享一个,我之前破解 apk 常用的工具 jad,它会让你看字节码文件轻松很多。

    先说一下,我分别用 Jdk 1.6 - 1.8 自带的 javap 工具进行了反编译,发现生成的 JVM 指令是一样的,所以在此处不会列出每一个版本生成的指令文件。为了便于大家阅读指令文件,这里用jad工具生成,代码如下。

    // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
    // Jad home page: http://www.kpdus.com/jad.html
    // Decompiler options: packimports(3) annotate
    // Source File Name: StringTest.java

    import java.io.PrintStream;

    public class StringTest
    {

    public StringTest()
    {
    // 0 0:aload_0
    // 1 1:invokespecial #1 <Method void Object()>
    // 2 4:return
    }

    public static void main(String args[])
    {
    String s = "aabbdd";
    // 0 0:ldc1 #2 <String "aabbdd">
    // 1 2:astore_1
    System.out.println(s);
    // 2 3:getstatic #3 <Field PrintStream System.out>
    // 3 6:aload_1
    // 4 7:invokevirtual #4 <Method void PrintStream.println(String)>
    String g = (new StringBuilder()).append("11").append(s).append(5).toString();
    // 5 10:new #5 <Class StringBuilder>
    // 6 13:dup
    // 7 14:invokespecial #6 <Method void StringBuilder()>
    // 8 17:ldc1 #7 <String "11">
    // 9 19:invokevirtual #8 <Method StringBuilder StringBuilder.append(String)>
    // 10 22:aload_1
    // 11 23:invokevirtual #8 <Method StringBuilder StringBuilder.append(String)>
    // 12 26:iconst_5
    // 13 27:invokevirtual #9 <Method StringBuilder StringBuilder.append(int)>
    // 14 30:invokevirtual #10 <Method String StringBuilder.toString()>
    // 15 33:astore_2
    System.out.println(g);
    // 16 34:getstatic #3 <Field PrintStream System.out>
    // 17 37:aload_2
    // 18 38:invokevirtual #4 <Method void PrintStream.println(String)>
    String a = "0";
    // 19 41:ldc1 #11 <String "0">
    // 20 43:astore_3
    for(int i = 1; i < 10; i++)
    //* 21 44:iconst_1
    //* 22 45:istore 4
    //* 23 47:iload 4
    //* 24 49:bipush 10
    //* 25 51:icmpge 80
    a = (new StringBuilder()).append(a).append(i).toString();
    // 26 54:new #5 <Class StringBuilder>
    // 27 57:dup
    // 28 58:invokespecial #6 <Method void StringBuilder()>
    // 29 61:aload_3
    // 30 62:invokevirtual #8 <Method StringBuilder StringBuilder.append(String)>
    // 31 65:iload 4
    // 32 67:invokevirtual #9 <Method StringBuilder StringBuilder.append(int)>
    // 33 70:invokevirtual #10 <Method String StringBuilder.toString()>
    // 34 73:astore_3

    // 35 74:iinc 4 1
    //* 36 77:goto 47
    System.out.println(a);
    // 37 80:getstatic #3 <Field PrintStream System.out>
    // 38 83:aload_3
    // 39 84:invokevirtual #4 <Method void PrintStream.println(String)>
    StringBuilder b = new StringBuilder();
    // 40 87:new #5 <Class StringBuilder>
    // 41 90:dup
    // 42 91:invokespecial #6 <Method void StringBuilder()>
    // 43 94:astore 4
    for(int i = 1; i < 10; i++)
    //* 44 96:iconst_1
    //* 45 97:istore 5
    //* 46 99:iload 5
    //* 47 101:bipush 10
    //* 48 103:icmpge 120
    b.append(i);
    // 49 106:aload 4
    // 50 108:iload 5
    // 51 110:invokevirtual #9 <Method StringBuilder StringBuilder.append(int)>
    // 52 113:pop

    // 53 114:iinc 5 1
    //* 54 117:goto 99
    System.out.println(b);
    // 55 120:getstatic #3 <Field PrintStream System.out>
    // 56 123:aload 4
    // 57 125:invokevirtual #12 <Method void PrintStream.println(Object)>
    // 58 128:return
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    这里说一下分析结果。

    1、无变量的字符串拼接,在编译期间值都确定了,所以 javac 工具帮我们把它直接编译成一个字符常量。

    2、有变量的字符串拼接,在编译期间变量的值无法确定,所以运行期间会生成一个StringBuilder 对象。

    3、循环中使用字符串拼接,循环内,每循环一次就会产生一个新的 StringBuilder 对象,对资源有一定的损耗。

    4、循环外使用 StringBuilder,循环内再执行 append() 方法拼接字符串,只会成一个 StringBuilder 对象。

    因此,对于有循环的字符串拼接操作,建议使用 StringBuilder 和 StringBuffer,对性能会有一定的提升。

    其实上面的结论,《阿里巴巴Java开发手册》中有所提到,此文正好与该条结论相对应。

    一个简单的字符串,用起来确实简单,背后付出了多少工程师的心血,在此,深深地佩服詹爷。

    ---------------------
    作者:Java面试那些事儿
    来源:CSDN
    原文:https://blog.csdn.net/wik_123/article/details/86484928
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    算法之二叉树各种遍历
    File类基本操作之OutputStream字节输出流
    W3C DOM 事件模型(简述)
    Linux多线程编程小结
    linux下getsockopt和setsockopt具体解释及測试
    MyBatis入门学习(一)
    [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use &#39;track by&#39; expression to specify uniq
    java中substring的使用方法
    Java Map遍历方式的选择
    E6全部刷机包
  • 原文地址:https://www.cnblogs.com/softidea/p/10434179.html
Copyright © 2020-2023  润新知