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. springboot和mybatis之thymleaf整合简单插入用户数据

    编写mapper接口和对应的mapper.xml文件,注意对应的注解 @Mapper @Repository public interface StudentMapper { void insertS ...

  2. Dart 基础重点截取 Dart 2 20180417

    官网教程 https://www.dartlang.org/guides/language/language-tour dart是一个单线程的语言,没有多线程 Final and const If y ...

  3. 写jsp文件时需要注意的一些小细节

    ①jsp文件的最开始的部分: <%@ page language="java" contentType="text/html; charset=UTF-8" ...

  4. 一个域名下多个Vue项目

    公司写的网站要英文和中文的,所以就写了两个项目,都是用vue写的单页面项目,但是域名只有一个,所以就想把两个vue项目合并到一个域名下面.思考:vue的页面都是单页面应用,说白了就是一个index.h ...

  5. Django model进阶

    Django-model进阶   QuerySet 可切片 使用Python 的切片语法来限制查询集记录的数目 .它等同于SQL 的LIMIT 和OFFSET 子句. >>> Ent ...

  6. React-router4 第八篇 ReactCSSTransitionGroup 动画转换

    https://reacttraining.com/react-router/web/example/animated-transitions 动画转换这么高级,其实是又引入了一个组件,没什么特别, ...

  7. 深入理解JVM(五)JVM优化策略

    5.2一些案例: 1.高性能硬件部署策略: (1)背景:某公司升级了硬件(CPU升级为4核,内存增加为16G),发现不定期出现网页失去响应. (2)原因:①内存增加之后,项目中有在内存中处理文件的大对 ...

  8. Reading | 《DEEP LEARNING》

    目录 一.引言 1.什么是.为什么需要深度学习 2.简单的机器学习算法对数据表示的依赖 3.深度学习的历史趋势 最早的人工神经网络:旨在模拟生物学习的计算模型 神经网络第二次浪潮:联结主义connec ...

  9. 05-使用jQuery操作input的value值

    表单控件是我们的重中之重,因为一旦牵扯到数据交互,离不开form表单的使用,比如用户的注册登录功能等 那么通过上节知识点我们了解到,我们在使用jquery方法操作表单控件的方法: $(selector ...

  10. 跨域ajax问题

    1. Intro 在用ajax请求时,请求的域名和所在域名不同,会出现跨域问题导致请求失败. 复杂请求: 条件: .请求方式:HEAD.GET.POST .请求头信息: Accept Accept-L ...