一、前言

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

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

二、自动拆装箱陷阱

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

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

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

三、字符串连接陷阱

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

Snippet A:

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

Snippet B:

public String toString(){
StringBuilder sb = new StringBuilder(100);
return sb.append("{a:").append(a)
.append(", b:").append(b)
.append(", c:").append(c)
.append("}")
.toString();
}

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

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

Snippet C:

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

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

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

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

Snippet D:

    public String toString()
{
String str = "";
for(int i=0;i<100000;i++){
str += "*";
}
return str;
}

Snippet E:

    public String toString()
{
StringBuilder strBuilder = new StringBuilder();
for(int i=0;i<100000;i++){
strBuilder.append("*");
}
return strBuilder.toString();
}

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

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

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

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

    public String toString()
{
String s = "";
for(int i = 0; i < 0x186a0; i++)
s = (new StringBuilder()).append(s).append("*").toString(); s = (new StringBuilder()).append(s).append("*").toString();
return s;
}

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

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

四、总结

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

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

警惕Java编译器中那些“蜜糖”陷阱的更多相关文章

  1. 避坑手册 | JAVA编码中容易踩坑的十大陷阱

    JAVA编码中存在一些容易被人忽视的陷阱,稍不留神可能就会跌落其中,给项目的稳定运行埋下隐患.此外,这些陷阱也是面试的时候面试官比较喜欢问的问题. 本文对这些陷阱进行了统一的整理,让你知道应该如何避免 ...

  2. 编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则)

    编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则) 目录 建议1: 不要在常量和变量中出现易混淆的字母 建议2: 莫让常量蜕变成变量 建议3: 三元操作符的类型务 ...

  3. 避免Java应用中NullPointerException的技巧和最佳实践

    Java应用中抛出的空指针异常是解决空指针的最好方式,也是写出能顺利工作的健壮程序的关键.俗话说"预防胜于治疗",对于这么令人讨厌的空指针异常,这句话也是成立的.值得庆幸的是运用一 ...

  4. Java进阶5 面向对象的陷阱

    Java进阶5 面向对象的陷阱 20131103 Java是一门纯粹面向对象的编程语言,Java面向对象是基础,而且面向对象的基本语法非常多,非常的细,需要程序员经过长时间的学习才可以掌握.本章重点介 ...

  5. Java编程中的一些常见问题汇总

    转载自  http://macrochen.iteye.com/blog/1393502 每天在写Java程序,其实里面有一些细节大家可能没怎么注意,这不,有人总结了一个我们编程中常见的问题.虽然一般 ...

  6. java编译器优化和运行期优化

    概述    最近在看jvm优化,总结一下学习的相关知识 (一)javac编译器 编译过程 1.解析与填充符号表过程 1).词法.语法分析    词法分析将源代码的字符流转变为标记集合,单个字符是程序编 ...

  7. Java语言中的这些知识点有没有用过,工作中有没有入过这些坑?

    在Java语言中,有一些相对生僻的知识,平时用的机会可能不是很多,但如果不了解不掌握这些知识点的话,也可能会掉入陷阱之中,今天我们就来初步梳理一下: 1. goto是java语言中的关键字. &quo ...

  8. Java开发中的23种设计模式详解

    [放弃了原文访问者模式的Demo,自己写了一个新使用场景的Demo,加上了自己的理解] [源码地址:https://github.com/leon66666/DesignPattern] 一.设计模式 ...

  9. Java开发中的23种设计模式详解(转)

    设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

随机推荐

  1. PHP支持的协议和封装的协议

    今天主要看了下PHP支持的协议和封装的协议,主要了解了一下PHP中的配置协议方面的东西,弄明白了以前比较模糊的stream_context_create()等上下文方法,还没有完全看完,先将自己的片段 ...

  2. Python第一天---第一个Python程序

    1.我的环境是windows下,需要安装notepad++,安装Python2,配置环境变量(百度下可以见) 2.打开cmd窗口-----输入I:  [输入要在哪个磁盘存储python代码(我的在I: ...

  3. 【Zookeeper】3.4.9 基本配置

    [hadoop@master1 ~]$ cat zookeeper/conf/zoo.cfg # The number of milliseconds of each tick 每个心跳的时长 单位为 ...

  4. JAVA基础----持续更新

    1.基本数据类型   - 整数型:byte  short  int  long   默认为int 计算时需要转换    - 浮点型:float  double     默认为double    - 布 ...

  5. PHPcms9.6.0任意文件上传漏洞直接getshell 利用教程

    对于PHPcms9.6.0 最新版漏洞,具体利用步骤如下: 首先我们在本地搭建一个php环境,我这里是appserv或者使用phpnow (官网下载地址:http://servkit.org/) (只 ...

  6. EXE转JPG后缀格式工具(真实JPG后缀)

    EXE转JPG后缀格式工具(真实JPG后缀) exe转换为jpg工具,双击之后正常运行. 杀毒软件误报 本程序禁止用于违法操作. 下载地址 链接: https://pan.baidu.com/s/1b ...

  7. 线性代数-矩阵-转置 C和C++的实现

    原理解析: 本节介绍矩阵的转置.矩阵的转置即将矩阵的行和列元素调换,即原来第二行第一列(用C21表示,后同)与第一行第二列(C12)元素调换位置,原来c31与C13调换.即cij与cji调换 . (此 ...

  8. 简述C/C++调用lua中实现的自定义函数

    1.首先说下目的,为什么要这么做 ? 正式项目中,希望主程序尽量不做修改,于是使用C/C++完成功能的主干(即不需要经常变动的部分)用lua这类轻量级的解释性语言实现一些存在不确定性的功能逻辑:所以, ...

  9. LVS之-LAMP搭建wordpress

    author:JevonWei 版权声明:原创作品 LVS搭建wordpress,涉及的知识点有DNS,LAMP,NFS及LVS 网络拓扑图 网络环境 NFS 192.168.198.130 mysq ...

  10. 第4阶段——制作根文件系统之分析init_post()如何启动第1个程序(1)

    本章学习如何启动第一个应用程序 1.在前面的分析中我们了解到,在init进程中内核挂接到根文件系统之后,会开始启动第一个应用程序: kernel_init函数代码如下: static int __in ...