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了,就像下面展示的图片一样:
JShell
Enhance 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 javadoc
Other 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,看各自的调研结论吧!