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. 信号基础知识---单频矩形脉冲信号CW

    %CW%参考:声呐技术 P27,31clc;close all;clear all;%参数-------------------------f0=50;T=0.1;%时宽B=1/T;fs=1000;% ...

  2. JavaScript onclick传递对象参数(easyui传递一行数据时)错误:uncaught SyntaxError: Unexpected identifier

    JavaScript onclick传递对象参数(easyui传递一行数据时)错误:uncaught SyntaxError: Unexpected identifier 博主遇到的是用onclick ...

  3. DIV内容超出长度显示省略号,鼠标移上自动显示全部内容(EasyUI DataGrid)

    如果想把DIV中超出的文本显示成省略号,而不是换行全部显示,有2个办法. 注:本文主要是以EasyUI的DataGrid为案例的,如果是其他场景只要底层是用DIV显示文本的应该都能使用. 首先可以给此 ...

  4. RobotFramework 模拟http接口登录自动化脚本

    RobotFramework 模拟自动化登录脚本思路: 先获取页面cookie值,然后根据cookie值加上请求体提交登录: 一.获取cookie: 以下脚本获取cookie值,并把改脚本封装为关键字 ...

  5. spring boot项目升级到2.0.1,提示java.lang.ClassNotFoundException: org.apache.log4j.Logger错误

    首先肯定是版本出现了一点兼容问题 所以我就进入项目,查看依赖树,cd到项目目录下,mvn dependency:tree 我看到我的项目原先是1.5.9和现在2.0.1的区别在于org.slf4j的依 ...

  6. 搭建Fabric网络(四)运行网络

    启动网络 docker-compose -f docker-compose-cli.yaml up -d如果container cli关闭了,可以手动启动 docker start cli 设置环境变 ...

  7. Vc 检测内存泄漏

    启用内存泄漏检测 检测内存泄漏是 C/c + + 调试器和 C 运行时库 (CRT) 的主要工具调试堆函数. 若要启用调试堆的所有函数,在 c + + 程序中,按以下顺序包含以下语句: C++复制 # ...

  8. 《Miracle-House团队》第三次作业:团队项目的原型设计与开发

    一.实验目的与要求 1.掌握软件原型开发技术 2.学习使用软件原型开发工具 二.实验内容与步骤 1.开发工具: 使用的工具:墨刀(APP端开发原型) 工具简介: 墨刀(MockingBot)是一款简单 ...

  9. 【Node.js】安装及使用

    Node.js是在Chrome的V8 JavaScript引擎上构建的JavaScript运行时.Node.js使用事件驱动的非阻塞I / O模型,使其轻量且高效.Node.js的软件包生态系统npm ...

  10. 数据库-mysql语句-查-WEB服务器

    (1)MySQL中的查询 (2)WEB服务器 Order:  订单.排序 Set:设置.集合 Table:表.桌子 1.MySQL中的简单查询 —— 查询结果的排序 示例:查询出所有员工信息,要求按工 ...