天天看点

【译】Java 8的新特性—终极版1. 简介2. Java语言的新特性3. Java编译器的新特性4. Java官方库的新特性5. 新的Java工具6. JVM的新特性7. 结论8. 参考资料

【译】Java 8的新特性—终极版1. 简介2. Java语言的新特性3. Java编译器的新特性4. Java官方库的新特性5. 新的Java工具6. JVM的新特性7. 结论8. 参考资料

java 8

这个教程包含java开发者经常面对的几类问题:

语言

编译器

工具

运行时(jvm)

java 8是java的一个重大版本,有人认为,虽然这些新特性领java开发人员十分期待,但同时也需要花不少精力去学习。在这一小节中,我们将介绍java 8的大部分新特性。

lambda的设计耗费了很多时间和很大的社区力量,最终找到一种折中的实现方案,可以实现简洁而紧凑的语言结构。最简单的lambda表达式可由逗号分隔的参数列表、->符号和语句块组成,例如:

在上面这个代码中的参数e的类型是由编译器推理得出的,你也可以显式指定该参数的类型,例如:

如果lambda表达式需要更复杂的语句块,则可以使用花括号将该语句块括起来,类似于java中的函数体,例如:

lambda表达式可以引用类成员和局部变量(会将这些变量隐式得转换成final的),例如下列两个代码块的效果完全相同:

lambda表达式有返回值,返回值的类型也由编译器推理得出。如果lambda表达式中的语句块只有一行,则可以不用使用return语句,下列两个代码片段效果相同:

默认方法和抽象方法之间的区别在于抽象方法需要实现,而默认方法不需要。接口提供的默认方法会被接口的实现类继承或者覆写,例子代码如下:

defaulable接口使用关键字default定义了一个默认方法notrequired()。defaultableimpl类实现了这个接口,同时默认继承了这个接口中的默认方法;overridableimpl类也实现了这个接口,但覆写了该接口的默认方法,并提供了一个不同的实现。

java 8带来的另一个有趣的特性是在接口中可以定义静态方法,例子代码如下:

下面的代码片段整合了默认方法和静态方法的使用场景:

这段代码的输出结果如下:

由于jvm上的默认方法的实现在字节码层面提供了支持,因此效率非常高。默认方法允许在不打破现有继承体系的基础上改进接口。该特性在官方库中的应用是:给java.util.collection接口添加新方法,如stream()、parallelstream()、foreach()和removeif()等等。

方法引用使得开发者可以直接引用现存的方法、java类的构造方法或者实例对象。方法引用和lambda表达式配合使用,使得java类的构造方法看起来紧凑而简洁,没有很多复杂的模板代码。

西门的例子中,car类是不同方法引用的例子,可以帮助读者区分四种类型的方法引用。

第一种方法引用的类型是构造器引用,语法是class::new,或者更一般的形式:class<t>::new。注意:这个构造器没有参数。

第二种方法引用的类型是静态方法引用,语法是class::static_method。注意:这个方法接受一个car类型的参数。

第三种方法引用的类型是某个类的成员方法的引用,语法是class::method,注意,这个方法没有定义入参:

第四种方法引用的类型是某个实例对象的成员方法的引用,语法是instance::method。注意:这个方法接受一个car类型的参数:

运行上述例子,可以在控制台看到如下输出(car实例可能不同):

在java 8中使用@repeatable注解定义重复注解,实际上,这并不是语言层面的改进,而是编译器做的一个trick,底层的技术仍然相同。可以利用下面的代码说明:

正如我们所见,这里的filter类使用@repeatable(filters.class)注解修饰,而filters是存放filter注解的容器,编译器尽量对开发者屏蔽这些细节。这样,filterable接口可以用两个filter注解注释(这里并没有提到任何关于filters的信息)。

另外,反射api提供了一个新的方法:getannotationsbytype(),可以返回某个类型的重复注解,例如<code>filterable.class.getannoation(filters.class)</code>将返回两个filter实例,输出到控制台的内容如下所示:

java 8编译器在类型推断方面有很大的提升,在很多场景下编译器可以推导出某个参数的数据类型,从而使得代码更为简洁。例子代码如下:

下列代码是value&lt;string&gt;类型的应用:

参数value.defaultvalue()的类型由编译器推导得出,不需要显式指明。在java 7中这段代码会有编译错误,除非使用<code>value.&lt;string&gt;defaultvalue()</code>。

java 8拓宽了注解的应用场景。现在,注解几乎可以使用在任何元素上:局部变量、接口类型、超类和接口实现类,甚至可以用在函数的异常定义上。下面是一些例子:

elementtype.type_user和elementtype.type_parameter是java 8新增的两个注解,用于描述注解的使用场景。java 语言也做了对应的改变,以识别这些新增的注解。

在java 8中这个特性是默认关闭的,因此如果不带-parameters参数编译上述代码并运行,则会输出如下结果:

如果带-parameters参数,则会输出如下结果(正确的结果):

如果你使用maven进行项目管理,则可以在maven-compiler-plugin编译器的配置项中配置-parameters参数:

java 8增加了很多新的工具类(date/time类),并扩展了现存的工具类,以支持现代的并发编程、函数式编程等。

接下来看一点使用optional的例子:可能为空的值或者某个类型的值:

如果optional实例持有一个非空值,则ispresent()方法返回true,否则返回false;orelseget()方法,optional实例持有null,则可以接受一个lambda表达式生成的默认值;map()方法可以将现有的opetional实例的值转换成新的值;orelse()方法与orelseget()方法类似,但是在持有null的时候返回传入的默认值。

上述代码的输出结果如下:

再看下另一个简单的例子:

这个例子的输出是:

steam api极大得简化了集合操作(后面我们会看到不止是集合),首先看下这个叫task的类:

task类有一个分数(或伪复杂度)的概念,另外还有两种状态:open或者closed。现在假设有一个task集合:

首先看一个问题:在这个task集合中一共有多少个open状态的点?在java 8之前,要解决这个问题,则需要使用foreach循环遍历task集合;但是在java 8中可以利用steams解决:包括一系列元素的列表,并且支持顺序和并行处理。

运行这个方法的控制台输出是:

这里有很多知识点值得说。首先,tasks集合被转换成steam表示;其次,在steam上的filter操作会过滤掉所有closed的task;第三,maptoint操作基于每个task实例的task::getpoints方法将task流转换成integer集合;最后,通过sum方法计算总和,得出最后的结果。

中间操作会返回一个新的steam——执行一个中间操作(例如filter)并不会执行实际的过滤操作,而是创建一个新的steam,并将原steam中符合条件的元素放入新创建的steam。

晚期操作(例如foreach或者sum),会遍历steam并得出结果或者附带结果;在执行晚期操作之后,steam处理线已经处理完毕,就不能使用了。在几乎所有情况下,晚期操作都是立刻对steam进行遍历。

steam的另一个价值是创造性地支持并行处理(parallel processing)。对于上述的tasks集合,我们可以用下面的代码计算所有任务的点数之和:

这里我们使用parallel方法并行处理所有的task,并使用reduce方法计算最终的结果。控制台输出如下:

对于一个集合,经常需要根据某些条件对其中的元素分组。利用steam提供的api可以很快完成这类任务,代码如下:

控制台的输出如下:

最后一个关于tasks集合的例子问题是:如何计算集合中每个任务的点数在集合中所占的比重,具体处理的代码如下:

控制台输出结果如下:

最后,正如之前所说,steam api不仅可以作用于java集合,传统的io操作(从文件或者网络一行一行得读取数据)可以受益于steam处理,这里有一个小例子:

stream的方法onclose 返回一个等价的有额外句柄的stream,当stream的close()方法被调用的时候这个句柄会被执行。stream api、lambda表达式还有接口默认方法和静态方法支持的方法引用,是java 8对软件开发的现代范式的响应。

我们接下来看看java.time包中的关键类和各自的使用例子。首先,clock类使用时区来返回当前的纳秒时间和日期。clock可以替代system.currenttimemillis()和timezone.getdefault()。

这个例子的输出结果是:

第二,关注下localdate和localtime类。localdate仅仅包含iso-8601日历系统中的日期部分;localtime则仅仅包含该日历系统中的时间部分。这两个类的对象都可以使用clock对象构建得到。

上述例子的输出结果如下:

上述这个例子的输出结果如下:

如果你需要特定时区的data/time信息,则可以使用zonedatetime,它保存有iso-8601日期系统的日期和时间,而且有时区信息。下面是一些使用不同时区的例子:

最后看下duration类,它持有的时间精确到秒和纳秒。这使得我们可以很容易得计算两个日期之间的不同,例子代码如下:

这个例子用于计算2014年4月16日和2015年4月16日之间的天数和小时数,输出结果如下:

这个代码的输出结果如下:

这个例子的输出结果如下:

新的base64api也支持url和mine的编码解码。

(base64.geturlencoder() / base64.geturldecoder(), base64.getmimeencoder() / base64.getmimedecoder())。

java8版本新增了很多新的方法,用于支持并行数组处理。最重要的方法是parallelsort(),可以显著加快多核机器上的数组排序。下面的例子论证了parallexxxx系列的方法:

上述这些代码使用parallelsetall()方法生成20000个随机数,然后使用parallelsort()方法进行排序。这个程序会输出乱序数组和排序数组的前10个元素。上述例子的代码输出的结果是:

java 8还添加了新的java.util.concurrent.locks.stampedlock类,用于支持基于容量的锁——该锁有三个模型用于支持读写操作(可以把这个锁当做是java.util.concurrent.locks.readwritelock的替代者)。

在java.util.concurrent.atomic包中也新增了不少工具类,列举如下:

doubleaccumulator

doubleadder

longaccumulator

longadder

java 8提供了一些新的命令行工具,这部分会讲解一些对开发者最有用的工具。

jjs是一个基于标准nashorn引擎的命令行工具,可以接受js源码并执行。例如,我们写一个func.js文件,内容如下:

可以在命令行中执行这个命令:<code>jjs func.js</code>,控制台输出结果是:

jdeps是一个相当棒的命令行工具,它可以展示包层级和类层级的java类依赖关系,它以.class文件、目录或者jar文件为输入,然后会把依赖关系输出到控制台。

这个命令会输出很多结果,我们仅看下其中的一部分:依赖关系按照包分组,如果在classpath上找不到依赖,则显示"not found".

通过为开发者提供很多能够提高生产力的特性,java 8使得java平台前进了一大步。现在还不太适合将java 8应用在生产系统中,但是在之后的几个月中java 8的应用率一定会逐步提高(ps:原文时间是2014年5月9日,现在在很多公司java 8已经成为主流,我司由于体量太大,现在也在一点点上java 8,虽然慢但是好歹在升级了)。作为开发者,现在应该学习一些java 8的知识,为升级做好准备。

<a href="http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html" target="_blank">what’s new in jdk 8</a>

<a href="http://docs.oracle.com/javase/tutorial/" target="_blank">the java tutorials</a>

<a href="http://blog.arungupta.me/2014/03/wildfly8-jdk8-netbeans8-javaee7-excellent-combo-enterprise-java/" target="_blank">wildfly 8, jdk 8, netbeans 8, java ee</a>

<a href="http://winterbe.com/posts/2014/03/16/java-8-tutorial/" target="_blank">java 8 tutorial</a>

<a href="http://marxsoftware.blogspot.ca/2014/03/jdeps.html" target="_blank">jdk 8 command-line static dependency checker</a>

<a href="http://marxsoftware.blogspot.ca/2014/03/illuminating-javadoc-of-jdk-8.html" target="_blank">the illuminating javadoc of jdk</a>

<a href="http://blog.jooq.org/2014/04/04/java-8-friday-the-dark-side-of-java-8/" target="_blank">the dark side of java 8</a>

<a href="http://www.eclipse.org/downloads/java8/" target="_blank">installing java™ 8 support in eclipse kepler sr2</a>

<a href="http://www.baeldung.com/java8" target="_blank">java 8</a>

<a href="http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html" target="_blank">oracle nashorn. a next-generation javascript engine for the jvm</a>

<a href="http://www.importnew.com/6675.html">— 身为一名java程序员,大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法。我们首先要判断这个返回值是否为null,只有在非空的前提下才能将其作为其他方法的参数。这正是一些类似guava的外部api试图解决的问题。本文深入介绍了java 8 optional类是如何解决该问题。</a>

<a href="http://wendal.net/2014/03/30.html">— jdk  之前, eclipse自带的ecj编译器,同本地变量表,把方法参数的名字,放在最前面,使其编译出来的class的名字看推测.  而 jdk8把这种行为规范化</a>

<a href="http://www.infoq.com/cn/articles/spring-4-java-8">— 具有众多新特性和函数库的java 8发布之后,spring 4.x已经支持其中的大部分。有些java 8的新特性对spring无影响,可以直接使用,但另有些新特性需要spring的支持。本文将带您浏览spring 4.0和4.1已经支持的java 8新特性。</a>