zoukankan      html  css  js  c++  java
  • 漫画:老板扣了我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
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    将vue文件script代码抽取到单独的js文件
    git pull 提示错误:Your local changes to the following files would be overwritten by merge
    vue和uniapp 配置项目基础路径
    XAMPP Access forbidden! Access to the requested directory is only available from the local network.
    postman与newman集成
    postman生成代码段
    Curl命令
    POST方法的Content-type类型
    Selenium Grid 并行的Web测试
    pytorch转ONNX以及TnesorRT的坑
  • 原文地址:https://www.cnblogs.com/softidea/p/10434179.html
Copyright © 2011-2022 走看看