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

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

image

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

确实大家都用的很嗨,很便利,但 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);
    }
}

有同学可能会说,这么一段代码,怎么来区分呢?既然我在对话中说了 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、无变量的字符串拼接,在编译期间值都确定了,所以 javac 工具帮我们把它直接编译成一个字符常量。

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

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

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

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

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

image

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

image

博客已迁移,欢迎关注 最新博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,841评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,415评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,904评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,051评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,055评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,255评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,729评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,377评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,517评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,420评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,467评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,144评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,735评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,812评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,029评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,528评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,126评论 2 341

推荐阅读更多精彩内容