Java 9 新特性概览
作者: 一字马胡
转载标志 【2017-11-03】
更新日志
日期 | 更新内容 | 备注 |
---|---|---|
2017-11-03 | 添加转载标志 | 持续更新 |
pre-reade&本文涉及的内容
首先,你可以在Java 9 Download下载java 9,然后就可以体验java 9了。本文是对java 9新特性的概览而不是详解,因为要详解的话需要收集大量的信息,而且有可能还不能覆盖所有特性,还有一点是本人才疏学浅,未免对新事物的理解过于迟钝,所以不太可能很快理解java 9中的新特性,比如java 9为什么要有某个特性,为什么增加这个特性,为什么删除了某个东西,这些都需要长期的技术积累和实践,从实际项目中来发现这些需求。本文涉及的java 9的新特性有一些会多讲一些篇幅,有一些会一带而过,比如本文比较在意的新增的API,会写一些示例代码来体验,而对于比较底层的JVM优化等内容不会涉及太多(无能为力,慢慢补上吧!)。下面先将本文的主要内容做一下索引。
- Java Modular System
- Enhance Process API
- Enhance Collection API
- Enhance Streams API
- Enhance Optional API
- Reactive API Support(RxJava)
- Private Methods of Interface
- Java Shell Support(REPL)
- Enhance Javadoc(search)
- Other Details
本文将基于上面提到的10点内容对java 9的新特性进行浅读解读,Java 8已经足够强大,但是java 9应该比java 8 要更强大,并且java的发版在未来会加快速度,需要快速学习啊。对于java 9 更为详细的描述应该参考更多的资料,最好可以结合jdk源码(或者openjdk源码)来看更好。
Java 9 新特性
Java Modular System
Java 9 一个比较大的改动就是增加了模块系统,基于 Jigsaw [ˈdʒɪgsɔ:] 项目,这是一个较为庞大复杂的项目,它的目标主要是下面两点:
- 可靠的配置:明确模块边界和模块之间的依赖关系
- 强封装性:通过封装模块内部私有细节,来避免不希望发生的依赖关系,也能避免一些安全问题等。
而Jigsaw被拆分为下面几个具体的项目:
- JEP 261: Module System,实现模块化系统;
- JEP 200: The Modular JDK,将JDK自身进行模块化;
- JEP 201: Modular Source Code,按照模块化的形式,重构源代码,因为现有代码并不是划分到一个一个的模块里的。
- JEP 220: Modular Run-Time Images,重新设计JDK和JRE的结构,定义新的URI scheme操作模块,类和资源(jrt)。
- JEP 260: Encapsulate Most Internal APIs,按照模块化封装性的要求,将不希望暴露的内部API封装起来,如果确实证明是广泛需要的,就作为公共API公开出来。
- JEP 282: jlink: The Java Linker。新的link工具
所谓JEP(JDK Enhancement Proposals),是java 9 中为了管理新特性而引入的代号。为了快速了解Jigsaw 项目在java 9 中的具体体现,下面先看一张图:
java 9 模块依赖图这是jdk现在的模块依赖关系图,可以发现所有的模块都是依赖一个模块叫做java.base,而java.base中包含了java语言层面的一些基础包,具体的java.base的详情可以参考Module java.base。介于该部分内容是java 9中的主要特性,但过于复杂,以至于都有专门的书籍来介绍这部分内容,所以更为细节的内容可以参考书籍Java 9 Modularity。下面来看一看如何在我们自己的项目中使用模块化编程呢。
比如,我们有三个模块,一个模块叫做com.hujian.base,提供一些基础信息内容,而第二个模块叫做com.hujian.extra,提供一些扩展信息,第三个模块叫做com.hujian.info,来提供较为全面的信息,他们之间的依赖关系为:com.hujian.extra依赖于com.hujian.base,而com.hujian.info依赖了com.hujian.base和com.hujian.extra。在java 9中,我们需要在每个模块中写一个module-info.java文件,里面描述模块的依赖关系,并且只有在模块暴露了出去,其他模块才能引用该模块。现在来看看上面三个模块的module-info.java应该怎么写:
对于com.hujian.base来说,它没有依赖其他模块,所以不需要依赖,但是,它需要提供服务,也就是
其他模块会引用该模块,所以他需要将自己的模块暴露出去,下面是它的module-info.java文件的内容:
module com.hujian.base {
exports com.hujian.base; //将模块暴露出去
}
而对于com.hujian.extra来说,它需要依赖com.hujian.base模块,并且com.hujian.info模块
会引用该模块,所以它的module-info.java是这样的:
module com.hujian.extra {
requires com.hujian.base;
exports com.hujian.extra;
}
最后是com.hujian.info模块,它的module-info.java应该这样写:
module com.hujian.info {
requires com.hujian.base;
require com.hujian.extra;
}
当然,这只是编写了module-info.java文件,实际的工作要比这个更多,但是我们可以借助IDE来实现这种模块编程,比如在
IDEA中,就可以很方便的实现。java 9中的模块系统也没有这么简单,介于还没有完全理解,该特性的描述就到此结束吧。
Enhance Process API
Process API提供了一种java程序和操作系统交互的能力,我们可以在代码中来获取关于进程的一些信息,java 9中新增了一个类ProcessHandle,使用这个类可以很方便的查询进程的一些信息。下面是该类的使用示例:
package com.hujian.java9;
import java.util.function.Consumer;
/**
* Created by hujian06 on 2017/10/28.
*
* ProcessHandle demo
*/
public class ProcessHandleDemo {
public static void main(String ... args) {
long pid = ProcessHandle.current().pid();
ProcessHandle.allProcesses()
.forEach(new Consumer<ProcessHandle>() {
@Override
public void accept(ProcessHandle processHandle) {
if (processHandle.pid() == pid) {
System.out.println("Current Process:" + pid + "\n" +
processHandle.info());
}
}
});
}
}
上面的例子仅仅是获取了当前进程的进程id,更为有趣的应用可以参考ProcessHandle类的具体实现。
Enhance Collection API
在java 9中,分别为List、Set、Map增加了of静态工厂方法,来获取一个不可变的List、Set、Map。下面展示了java 9为List提供的of静态工厂方法集,Set和Map都是类似的就不赘述了:
static <E> List<E> of()
static <E> List<E> of(E e1)
static <E> List<E> of(E e1, E e2)
static <E> List<E> of(E e1, E e2, E e3)
static <E> List<E> of(E e1, E e2, E e3, E e4)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9)
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)
static <E> List<E> of(E... elements)
在java 9之前,如果我们想要获取一个不可变的List的话,可以使用下面的方法来实现:
List<String> emptyList = Collections.emptyList();
List<String> list = Collections.singletonList("java 9");
而在java 9中,只需要使用of就可以获取一个不可变的List,使用方法如下:
List<String> emptyStringList = List.of();
List<String> stringList = List.of("java 7", "java 8", "java 9");
Enhance Streams API
java 8中新增的Stream API可以让java开发者方便的使用计算机的并行能力来快速计算,关于java 8中的Stream的分析总结,可以参考文章Java Streams API,以及Java Stream的并行实现。Stream API的并行实现借助了Fork/Join并行框架,关于Fork/Join的介绍,可以参考文章Java Fork/Join并行框架。而在java 9中,Stream API新增了几个方法,下面展示了这些新增的方法:
default Stream<T> dropWhile(Predicate<? super T> predicate)
default Stream<T> takeWhile(Predicate<? super T> predicate)
static <T> Stream<T> ofNullable(T t)
static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
dropWhile方法和takeWhile方法是一对类似的方法,这两个方法适合用在有序流,而对于无序流来说,使用这两个方法会造成不可预料的结果。而ofNullable静态方法的作用非常有限,它将对元素T进行判断,如果是null,则返回一个空流,否则,返回一个只包含一个元素T的流。而新增的iterate方法类似于for循环,类似于下面的代码:
for (T index=seed; hasNext.test(index); index = next.apply(index)) {
//todo...
}
下面是使用iterate这个新方法来生成一个流的示例代码:
Stream.iterate( 0, n -> (n <= 100), n -> n + 1 )
.forEach(System.out::println);
需要注意的是,只要test返回false,那么就会停止向前,这一点需要特别注意。下面来看一下dropWhile方法和takeWhile方法的使用示例以及他们的注意事项:
Stream.of(1, 2, 3, 4)
.takeWhile(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer < 4;
}
}).forEach(System.out::println);
对于takeWhile来说,它会从第一个元素进行test,如果第一个元素test返回false,那么就会立刻返回整个流。
否则,会一直test到第一个返回false的元素,然后截取前面这一段返回。
Stream.of(1, 2, 3, 4)
.dropWhile(new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
return integer < 4;
}
}).forEach(System.out::println);
dropWhile类似于是takeWhile的互补操作,对于同一个有序流,同一个test条件来说,他们两个方法返回的内容正好
是整个流的内容,他们的区别在于,takeWhile是返回满足条件的流内容,而dropWhile将丢弃满足条件的流,返回的
正是不满足条件的流。
Enhance Optional API
Optional类也是java 8新增的类,在java 9中对他有一些增强,新增了三个方法:
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
public Stream<T> stream()
首先是ifPresentOrElse方法,这个方法是对原来的方法ifPresent的增强,下面是ifPresentOrElse方法的具体实现:
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
if (value != null) {
action.accept(value);
} else {
emptyAction.run();
}
}
可以看到,ifPresentOrElse方法在value为null的时候提供了例外的操作,这也是orElse的语义。接下来看新增的or方法,下面是整个方法的实现,以及对这个方法的描述:
/**
* If a value is present, returns an {@code Optional} describing the value,
* otherwise returns an {@code Optional} produced by the supplying function.
*
* @param supplier the supplying function that produces an {@code Optional}
* to be returned
* @return returns an {@code Optional} describing the value of this
* {@code Optional}, if a value is present, otherwise an
* {@code Optional} produced by the supplying function.
* @throws NullPointerException if the supplying function is {@code null} or
* produces a {@code null} result
* @since 9
*/
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
Objects.requireNonNull(supplier);
if (isPresent()) {
return this;
} else {
@SuppressWarnings("unchecked")
Optional<T> r = (Optional<T>) supplier.get();
return Objects.requireNonNull(r);
}
}
最为激动人心的应该是第三个方法stream,它提供了将Optional转换为Stream的实现,在java 9中,你已经可以将一个Optional直接转换为一个Stream了,下面是它的实现细节:
public Stream<T> stream() {
if (!isPresent()) {
return Stream.empty();
} else {
return Stream.of(value);
}
}
Reactive API Support(RxJava)
如果你接触过RxJava,那么就很好理解java 9中新增的Reactive内容了,下面首先展示了一个非常简单的使用示例:
package com.hujian.java9;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.Flow.Subscription;
import java.util.concurrent.SubmissionPublisher;
public class FlowDemo2 {
private static final ExecutorService SERVICE = Executors.newFixedThreadPool(1);
private static final int CAPATIAL = 1024;
public static void main(String[] args) {
// Create a publisher.
SubmissionPublisher<String> publisher = new SubmissionPublisher<>(SERVICE, CAPATIAL);
// Create a subscriber and register it with the publisher.
MySubscriber<String> subscriber = new MySubscriber<>();
publisher.subscribe(subscriber);
// Publish several data items and then close the publisher.
List.of("Java 7", "Java 8", "Java 9")
.forEach(item -> publisher.submit(item));
publisher.close();
SERVICE.submit(() -> {
//wait the publish job~
});
SERVICE.shutdownNow();
}
static class MySubscriber<T> implements Subscriber<T> {
@Override
public void onSubscribe(Subscription subscription) {
subscription.request(3);
System.out.println("<start to publish item>");
}
@Override
public void onNext(T item) {
System.out.println("<Received>:" + item);
}
@Override
public void onError(Throwable t) {
t.printStackTrace();
}
@Override
public void onComplete() {
System.out.println("<onComplete>");
}
}
}
下面是涉及Reactive的几个新增的类,需要说明的一点是,这些内容出自java并发大师Doug Lea,所以性能问题不需要担心,例外一点是,因为这些Reactive的内容添加到了java 语言中,所以比起第三方类库(比如RxJava)来说更为安全和稳定,来看看涉及的几个类:
Flow
Publisher
Subscriber
Subscription
Processor
SubmissionPublisher
更为具体和深入的分析将在例外的文章中进行。本文点到为止,还有,毕竟出自并发大师之手,日后值得研究一番啊!
Private Methods of Interface
在java 8中,接口中可以有默认方法和静态方法的实现,但是还不支持private方法的实现,而在java 9中,我们可以在接口中实现private方法了,先看下面的示例:
package com.hujian.java9;
/**
* Created by hujian06 on 2017/10/28.
*
* demo of interface
*
*/
public class InterfeaceDemo {
public static void main(String ... args) {
A a = new A() {
@Override
public void af() {
f();
}
};
a.af();
}
}
interface A {
String finalString = "Tag";
void af();
default void f() {
f1();
}
private void f1() {
System.out.println(finalString + ":f1");
}
}
f1是接口A中的默认实现。private方法和default和静态方法的区别在于,default方法和静态方法都是对外暴露的接口方法,而private方法是私有的,外部调用方是没有权限知道的。关于接口的不断更新,需要更为深入的讨论,再次先不做总结描述。
Java Shell Support(REPL)
现在,java语言也支持REPL了,就像下面展示的图片一样:
JShellEnhance Javadoc(search)
参考:Java® Platform, Standard Edition & Java Development Kit
Version 9 API Specification
以及java 8版本的javadoc:Java™ Platform, Standard Edition 8
API Specification
区别还是很明显的,java 9开始javadoc支持查找了,这一点是很明智的!下面看一下搜索效果图,这样是不是很快可以找到我们需要查找的类信息呢:
java 9 javadocOther Details
- 对CompletableFuture类的增强,细节看代码吧!
- "_"不再可以是一个标志符,在java 9中已经升级为一个关键字了!
- HTTP/2 Client API(孵化,目前来看还是用不了啊,好像需要重新编译openjdk?),据说比Apache的HttpClients性能还要好啊,这一点很有吸引力啊,还是比较期待的
- 其他更改,可以参考
本文简单分析了java 9的新特性,更为完整深入的java 9的特性应该搜集更多的资料来总结,关于是否要将目前的java版本升级到java 9,我的想法是,应该等等,一个原因是java 9和java 8比起来貌似没有太多性能优化方面的考虑(不包括我没有提到了jvm层面的优化,那些优化任何一个版本的java升级都会做大量的优化的),仅类库层面来说,java 9貌似没有太多的两点,新增的类好像不多,多数是对原有类的增强,但是貌似这些增强可有可无啊(个人觉得)。还有一个原因是,未来java的发版周期将更短,而java 9才刚发布不久,应该做一些深入的调研测试,等时机成熟再升级java 8到java 9也不迟~但是,如果目前还在使用java 8以前版本的javaer来说,我强烈建议升级到java 8,因为这个版本的java亮点很多,并且各方面表现都是很不错的,至于java 9,看各自的调研结论吧!