1.前言

  本文记录内容来自《深入理解Java虚拟机》的第十章早期(编译期)优化其中一节内容,其他的内容个人觉得暂时不需要过多关注,比如语法、词法分析,语义分析和字节码生成的过程等。主要关注的就是Java的一些语法糖是如何实现的。

  语法糖不会提供实质性的功能改进,但是它们或能提高效率,或能提升语法的严谨性,或能减少编码出错的可能。大量使用语法糖可能会迷失其中,不得要领,下面就介绍一下Java的语法糖的实现。

2.泛型与类型擦除

  JDK5新增了一个特性,就是泛型。本质是参数化类型的应用,就是所操作的数据类型被指定为一个参数,这种参数可以应用在类、接口和方法的创建中。

  Java没有泛型的时候,只能通过Object是所有类型的父类和类型强制转换两个特点的配合来实现类型泛化,比如HashMap的get方法,返回的就是Object,因为map中一切皆有可能。但是这样的操作带来了一些风险,如果强转的类型错误,就会在运行期间抛出异常,我们需要在编码期间就发现这个问题。

  Java的泛型是一种伪泛型,不是C#那种传统意义上的泛型。在C#中,List<int>和List<String>是两种类型,但是Java中还是List。因为Java的泛型只存在于代码中,编译后泛型就消失了,这个就是所说的类型擦除,取而代之的就是插入了强转代码。将一段含有泛型的Java程序编译后,再反编译回来,就会发现反编译的代码中泛型消失了,新增的就是强转代码。

  为什么要使用类型擦除的方式实现泛型的原因不得而知,但是这个确实是个吐槽的地方。有人说性能上泛型会由于强制转型操作和运行期缺少针对类型的优化导致速度比真正的泛型慢。这个评价角度不太对,因为泛型是用于提升语义准确性的。Java的泛型会导致一些奇特的问题。

  比如重载:public static void method(List<String> list)和public static void method(List<Integer> list)这两个方法存在是不会编译通过的,因为类型擦除后是特征签名一模一样的。看似重载不正确的原因找到了:重载要求方法名相同,参数不同,返回值可同可不同。这里参数、方法名一致肯定失败了。但实际上不是,因为如果改一下一个返回String类型,一个返回Integer类型,按照重载的定义,返回值可以不同,这两个方法应该还是一样的,编译不同过才对,而实际上是可以通过的。这又是为什么呢?在Java语言中方法重载实际是要求方法具有不同的特征签名,相同的方法名。特征签名是一个方法中各个参数在常量池中的字段符号引用的集合,返回值不在其中,这样也就能理解重载的要求是方法名相同,参数不同的含义了。而修改了返回值为什么通过了呢?原因在于这不是重载的范畴,在Class文件格式中,只要描述符不是完全一致的两个方法也可以共存。方法的描述符和返回值是有关系的,所以这两个方法的描述符因为返回值的区别是不同的,可以共存,但不是重载。

3.自动装箱、拆箱与遍历循环

  从技术角度来说,这些语法糖在实现和思想上都不如泛型,但是这是使用最多的语法糖。

    自动装箱的操作就是:Integer.valueof(i),在编译阶段替换成了这个

    自动拆箱的操作就是:Integer.intValue()

    集合的foreach操作是:for(its = list.iterator;  its.hasNext; ;) { its.next},采取的是迭代器的方式遍历

    数组的foreach操作实际上是压栈,进栈出栈操作。

  这些原理都什么简单,可以自己写一个类编译后,再反编译看看结果。

  自动装箱的陷阱:

    public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
System.out.println(c == d);
System.out.println(e == f);
System.out.println(c == (a+b));
System.out.println(c.equals(a+b));
System.out.println(g == (a+b));
System.out.println(g.equals(a+b));
}

  上面的代码执行出来1.true 2.false 3.true 4.true 5.true 6.false。

  解释一下上面的含义:

    首先包装类的==比较不会触发自动拆箱,所以1、2的比较是引用比较。那为什么1是true,2是false?上面说了自动装箱是通过Integer.valueof实现的,Java代码对Integer进行了优化,查看源代码可以看到有个缓存,在-128~127之间的整数,获取的是同一个Integer对象,321超过了这个范围,生成的是不同的对象。

    +号会触发自动拆箱,导致==一般是int类型,而不是Integer类型,最后触发c的自动拆箱,两个int类型比较,值相等就是true了。

    4中的+号也会触发自动拆箱,成int类型,但由于是equals方法,又进行了自动装箱,最后判断标准就是Integer.equals的方法,其比较就是两个int类型进行比较,所以是true

    5中和3的步骤类似,但是g自动才行成了long类型,long与int比较,数值的大小比较。

    6和4类似,但是最终变成了Long.equals(Integer),根据Long的equals方法,传入的必须是Long类型才可,所以返回的是false。

  这个例子告诉我们代码中避免这样使用自动装箱和拆箱,掌握不牢可能发生意料之外的情况。

4.条件编译

  许多语言都提供了条件编译的途径,比如C、C++的预处理器指示符#ifdef来完成条件编译,他们是用于解决编译时代码的依赖关系,但在Java中没有使用预处理器,因为不需要,编译器不是一个个编译Java文件,而是将所有编译单元的语法树顶级节点输入到待处理列表后再进行编译。

  Java语言也可以实现条件编译,方法就是使用条件是常量的if语句,比如if(true){} else{},这样else模块的内容就会被省略。

  这种条件编译只能写在方法体内,没有办法根据条件调整整个Java类结构。

5.其他语法糖

  内部类、枚举类、断言语句、对枚举和字符串(JDK7)的switch支持、try中定义和关闭资源等。这些可以自己通过javac编译后javap -verbose进行查看字节码,理解其实现原理。

Java的语法糖的更多相关文章

  1. 深入理解 Java try-with-resource 语法糖

    背景 众所周知,所有被打开的系统资源,比如流.文件或者Socket连接等,都需要被开发者手动关闭,否则随着程序的不断运行,资源泄露将会累积成重大的生产事故. 在Java的江湖中,存在着一种名为fina ...

  2. Java虚拟机 - 语法糖

    [深入Java虚拟机]之六:Java语法糖 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家Peter.J.Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语 ...

  3. Hollis原创|不了解这12个语法糖,别说你会Java

    GitHub 2.5k Star 的Java工程师成神之路 ,不来了解一下吗? GitHub 2.5k Star 的Java工程师成神之路 ,真的不来了解一下吗? GitHub 2.5k Star 的 ...

  4. Java语法糖4:内部类

    内部类 最后一个语法糖,讲讲内部类,内部类指的就是在一个类的内部再定义一个类. 内部类之所以也是语法糖,是因为它仅仅是一个编译时的概念,outer.java里面定义了一个内部类inner,一旦编译成功 ...

  5. Java语法糖1:可变长度参数以及foreach循环原理

    语法糖 接下来几篇文章要开启一个Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的 ...

  6. Java中的10颗语法糖

    语法糖(Syntactic Sugar):也称糖衣语法,指在计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用.通常来说,使用语法糖能够增加程序的可读性,减少程序代码出错的 ...

  7. 语法糖----JAVA

    语法糖 语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语.指的是,在计算机语言中添加某种语法,这种语法能使程序 ...

  8. 转:【深入Java虚拟机】之六:Java语法糖

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/18011009 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家P ...

  9. Java 中的语法糖

    百度百科对语法糖的定义 语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这 ...

随机推荐

  1. node.js中通过stream模块实现自定义流

    有些时候我们需要自定义一些流,来操作特殊对象,node.js中为我们提供了一些基本流类. 我们新创建的流类需要继承四个基本流类之一(stream.Writeable,stream.Readable,s ...

  2. idea本地运行JavaWeb项目

    1.需安装的软件有: JDK,当前版本jdk1.8 maven,当前版本3.2.1 mysql,mysql5.7 tomcat,tomcat9 git客户端,TortoiseGit-2.1.0.0-6 ...

  3. 28.Mysql权限与安全

    28.Mysql权限与安全28.1 Mysql权限管理 28.1.1 权限系统的工作原理对连接的用户进行身份认证,合法的用户通过认证,不合法的用户拒绝连接:对通过认证的合法用户赋予相应的权限,用户可以 ...

  4. Failed to connect to 127.0.0.1 port 1080: Connection refused package 问题解决方法

    错误: fatal: unable to access 'https://github.com/******': Failed to connect to 127.0.0.1 port 1080: C ...

  5. 18. pt-pmp

    pt-pmp 是一个非常简单的工具,可以用来获取MySQL的堆栈信息.工具首先获取运行过程中的mysqld堆栈信息,然后将相似的线程进行汇总排序,根据调用频繁程度从高到低打印出来. 查看pt-pmp的 ...

  6. mysql开启调试日志general_log开启跟踪日志

    general_log = 1 general_log_file = /tmp/umail_mysql.log 有时候,不清楚程序执行了什么sql语句,但是又要排除错误,找不到原因的情况下, 可以在m ...

  7. 20165213周启航java学习总结

    20165213周启航java学习总结 一.每周博客链接及二维码 预备作业一:我所期望的师生关系:http://www.cnblogs.com/rocedu/p/6255835.html#WEEK15 ...

  8. Python:每日一题007

    题目: 输出 9*9 乘法口诀表. 程序分析: 分行与列考虑,共9行9列,i控制行,j控制列. 个人思路及代码: 第一版: for i in range(1,10): for j in range(1 ...

  9. AX_HelpGenerator

    HelpGenerator helpGenerator; ; helpGenerator = infolog.helpGenerator(); helpGenerator.showURL(" ...

  10. JavaScript 方法

    对象的定义 var xiaoming = { name: '小明', birth: 1990 }; 是,如果我们给xiaoming绑定一个函数,就可以做更多的事情.比如,写个age()方法,返回xia ...