zoukankan      html  css  js  c++  java
  • 警惕Java编译器中那些“蜜糖”陷阱

    一、前言

    随着Java编译器不断地向前发展,它为程序员们提供了越来越多的“蜜糖”(compiler suger),极大地方便了程序的开发,例如,foreach的增强模式,自动拆箱与装箱以及字符串的连接操作......

    这些"蜜糖"带给我们很多的便利,但是也存在着一些陷阱。

    二、自动拆装箱陷阱

    首先我们来看看大家最为熟悉的自动拆装箱(boxing),boxing可以自动帮我们完成基本类型和基本类型包裹器之间的转换。

    具体使用方法可以参考有名的Java Gossip(http://openhome.cc/Gossip/Java/Wrapper.html)。

    当然,这个蜜糖也存在着一些臭名昭著的陷阱。对于这些陷阱,我们同样可以从Java Gossip(http://openhome.cc/Gossip/Java/AutoBoxUnBox.html)获得警示。

     integer  数值小于128时读的是常量池的数据,所以用==会得到true 大于128时会得到false,另外integer的值说不可改的,final

    三、字符串连接陷阱

    这里我们会重点介绍一个容易被大家忽视的陷阱“字符串连接”,有下面两个toString方法,你会选择哪一个呢?

    Snippet A:

    1 public String toString(){
    2     return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
    3 }

    Snippet B:

    1 public String toString(){
    2     StringBuilder sb = new StringBuilder(100);
    3     return sb.append("{a:").append(a)
    4           .append(", b:").append(b)
    5           .append(", c:").append(c)
    6           .append("}")
    7           .toString();
    8 }

    记得有些书里面曾经说过我们做大规模字符串连接的时候应该使用StringBuilder,因为StringBuilder可以避免重复地创建String对象。

    这个忠告在JDK6之前也许是对的,但是JDK6以后情况就不是这样了。我们使用反编译工具来看看Snippet A会被Java编译器优化成什么样子呢?

    Snippet C:

    1 public String toString()
    2 {
    3     return (new StringBuilder()).append("{a:").append(a).append(", b:").append(b).append(", c: ").append(c).append("}").toString();
    4 }

    Snippet C是Java编译器优化后Snippet A的结果,我们可以看到Java编译器已经将String的"+"操作符连接转换成了StringBuilder的append连接。

    而且,Snippet A相比较于Snippet B更简洁,更方便。那我们是不是在编写代码的时候就可以放弃StringBuilder了呢?

    下面,我们再比较两个toString代码片段:

    Snippet D:

    1 public String toString()
    2 {
    3     String str = "";
    4     for(int i=0;i<100000;i++){
    5         str += "*";
    6     }
    7     return str;
    8 }

    Snippet E:

    1 public String toString()
    2 {
    3     StringBuilder strBuilder = new StringBuilder();
    4     for(int i=0;i<100000;i++){
    5         strBuilder.append("*"); 
    6     }
    7     return strBuilder.toString();   
    8 }

    那么,Snippnet D和Snippet E的性能还会是一样的吗?

    简单测试就会发现两者的性能差距很大,

    Snippet D耗时大约12s,但是Snippet E耗时不足1ms。

    我们依然可以通过反编译工具来找到答案,通过反编译我们可以发现Snippet D会被编译器成如下片段:

    1 public String toString()
    2 {
    3     String s = "";
    4     for(int i = 0; i < 0x186a0; i++)
    5         s = (new StringBuilder()).append(s).append("*").toString();
    6  
    7     s = (new StringBuilder()).append(s).append("*").toString();
    8     return s;
    9 }

    也就是说Java编译器并没有优化掉for循环结构体,它依然在不断重复地创建StringBuilder对象和String对象。

    因此,当我们选择在一个for结构体里面做大量的字符串连接操作时,我们依然要使用StringBuilder的append操作。

    四、总结

    Java编译器给我们提供了很多的蜜糖,也提供了很多优化的功能(许多基于JMM的优化会给我们带来更多的“surprise”,例如指令重新排序)。

    这些优化给我们带了便利和性能的提升,但是我们使用它们的同时,也必须了解这些优化背后的原理。否则,你可能就得生活在"surprise"当中了。

  • 相关阅读:
    COGS——T2084. Asm.Def的基本算法
    COGS——T1310. [HAOI2006]聪明的猴子
    python(4)- 简单练习:python实现购物车的优化
    Windows CE,你妈吗喊你在多核上玩玩
    xp宿主机和VMware下Ubuntu12.04共享文件夹
    xp主机用VMware9和10安装Ubuntu12.04后无法进入图像界面
    Ubuntu12.04 VMware Tools的安装
    linux 目录结构
    vmware-tools安装指南
    抱怨IT公司人才缺乏?留住现有人才方是正途
  • 原文地址:https://www.cnblogs.com/jaysir/p/5667927.html
Copyright © 2011-2022 走看看