作者:「Rambabu Posa」,一个具有 11 年丰富开发经验的技术牛人。
英文出处:Java 9 Features with Examples
- Java 9 REPL (JShell)
- Factory Methods for Immutable List, Set, Map and Map.Entry
- Private methods in Interfaces
- Moduar System - Jigsaw Project
- Process API Improvements
- Try With Resources Improvement
- CompletableFuture API Improvement
- Reactive Streams
- Diamond Operator for Anonymous Inner Class
- Optional Class Improvements
- Stream API Improvements
- Enhanced @Deprecated annotation
- HTTP 2 Client
- Мulti-Resolution Image API
1、Java 9 REPL (JShell)
Oracle 公司(Java Library 开发者)新引进一个代表 Java Shell 的称之为 “jshell” 或者 REPL(Read Evaluate Print Loop)的新工具。该工具可以被用来执行和测试任何 Java 中的结构,如 class,interface,enum,object,statements 等。使用非常简单。
$ jshell
| Welcome to JShell -- Version 9.0.1
| For an introduction type: /help intro
jshell> System.out.println("Hello World!");
Hello World!
我们同样可以定义和执行类的方法
jshell> class Hello {
...> public static void sayHello() {
...> System.out.print("Hello");
...> }
...> }
| created class Hello
jshell> Hello.sayHello()
Hello
想要了解更多的功能的话可以执行"/help",它会告诉你许多你想知道的,另外退出命令是"/exit"。
2、Factory Methods for Immutable List, Set, Map and Map.Entry
Oracle 公司引入一些方便使用的工厂方法,用于创建不可变集合 List,Set,Map 和 Map.Entry 对象。这些高效实用的方法可用来创建空或者非空集合对象。
在 Java SE 8 和更早版本中,我们常用类似 unmodifiableXXX 的集合类方法创建不可变集合对象。举个例子,比如我们想创建一个不可变的 List 对象,可能使用到 Collections.unmodifiableList 方法。
然而,这些 Collections.unmodifiableXXX 方法显得非常冗长乏味。为了克服这些缺陷,Oracle 公司给 List、Set 和 Map 接口分别添加了两个更加实用的方法。
List 和 Set 接口使用 of() 方法创建一个空或者非空的不可变 List 或 Set 对象
List immutableList = List.of();
List immutableList = List.of("one","two","three");
Map<Integer,String> emptyMap = new HashMap<>();
Map<Integer,String> immutableEmptyMap = Collections.unmodifiableMap(emptyMap);
jshell> Map emptyImmutableMap = Map.of()
emptyImmutableMap ==> {}
Map<Integer,String> nonemptyMap = new HashMap<>();
nonemptyMap.put(1,"one")
nonemptyMap.put(2,"two")
nonemptyMap.put(3,"three")
Map<Integer,String> immutableNonEmptyMap = Collections.unmodifiableMap(nonemptyMap);
jshell> Map nonemptyImmutableMap = Map.of(1, "one", 2, "two", 3, "three")
nonemptyImmutableMap ==> {2=two, 3=three, 1=one}
jshell> import static java.util.Map.entry
jshell> Map<Integer,String> nonemptyMap = Map.ofEntries(entry(1,"one"),entry(2,"two"));
nonemptyMap ==> {2=two, 1=one}
3、Private methods in Interfaces
在 Java SE 8 中,我们可以在接口中使用默认或者静态方法提供一些实现方式,但是不能创建私有方法。
为了避免冗余代码和提高重用性,Oracle 公司准备在 Java SE 9 接口中引入私有方法。也就是说从 Java SE 9 开始,我们也能够在接口类中使用 ‘private’ 关键字写私有化方法和私有化静态方法。
接口中的私有方法与 class 类中的私有方法在写法上并无差异,如:
public interface Card{
private Long createCardID(){
// Method implementation goes here.
}
private static void displayCardDetails(){
// Method implementation goes here.
}
}
Java9接口中可以存在的东西:
- 常量
- Abstract methods
- Default methods
- Static methods
- Private methods
- Private Static methods
4、Java9 Module System
Java SE 9 新特性中最大的一个变化就是 Module System。Oracle 公司将引入如下特性:
- Modular JDK
- Modular Java Source Code
- Modular Run-time Images
- Encapsulate Java Internal APIs
- Java Platform Module System
Java SE 9 版本之前,我们使用整体的 Jars 来开发基于 Java 语言的应用程序。这种体系架构有许多局限性和缺点。为了避免这些缺陷,Java SE 9 迎来了 Module System。
模块化的优点:
JDK,JRE,JARs都变成了更小的模块,我们可以运用任何我们想要的
易于测试与维护
支持更好的性能
更强的封装
我们无法访问内部非关键的api
模块可以非常安全地隐藏不需要的和内部的细节,我们可以获得更好的安全性。
jdk的对比:
JDK8文件结构:
JDK9文件结构:
所有的JDK模块以"jdk"开头,所有Java SE规范模块以"java"开头,"java.base"模块是“The Mother of Java 9 Module"。
- 每个模块有唯一一个名字
- 每个模块在源文件中都有一些描述
- 模块描述在一个名为“module-info.java”的源文件中表示,"module-info.java"描述一个模块。
- 模块描述符是一个Java文件。它不是XML,文本文件或属性文件
- 按照约定,模块描述符文件放置在模块的顶层目录中
- 每个模块可以有任意数量的包和类型
- 一个模块可以依赖于任意数量的模块
“module”, “requires”, and “exports”并不是java关键字。
5、Process API Improvements
Java SE 9 迎来一些 Process API 的改进,通过添加一些新的类和方法来优化系统级进程的管控。
Process API 中的两个新接口:
- java.lang.ProcessHandle
- java.lang.ProcessHandle.Info
ProcessHandle currentProcess = ProcessHandle.current();
System.out.println("Current Process Id: = " + currentProcess.getPid());
6、Try With Resources Improvement
我们知道,Java SE 7 引入了一个新的异常处理结构:Try-With-Resources,来自动管理资源。这个新的声明结构主要目的是实现“Automatic Better Resource Management”(“自动资源管理”)。
Java SE 9 将对这个声明作出一些改进来避免一些冗长写法,同时提高可读性。
Java SE 7中:
- 任何资源(预定义的Java API类或用户定义的类)都必须实现Java.lang.autocloseable
- 资源对象必须引用final或有效的final变量
- 如果资源已经在try-with-Resource语句之外声明,那么我们应该重新引用局部变量
- 新创建的本地变量在try-with-resources语句中是有效的
void testARM_Before_Java9() throws IOException{
BufferedReader reader1 = new BufferedReader(new FileReader("test.txt"));
try (BufferedReader reader2 = reader1) {
System.out.println(reader2.readLine());
}
}
void testARM_Java9() throws IOException{
BufferedReader reader1 = new BufferedReader(new FileReader("test.txt"));
try (reader1) {
System.out.println(reader1.readLine());
}
}
7、CompletableFuture API Improvement
在 Java SE 9 中,Oracle 公司将改进 CompletableFuture API 来解决一些 Java SE 8 中出现的问题。这些被添加的 API 将用来支持一些延时和超时操作,实用方法和更好的子类化。
Executor exe = CompletableFuture.delayedExecutor(50L, TimeUnit.SECONDS);
这里的 delayedExecutor() 是静态实用方法,用来返回一个在指定延时时间提交任务到默认执行器的新 Executor 对象。
8、Reactive Streams
现在,Reactive Programming 由于其便利性在应用程序开发中变得非常流行。Scala、Play、Akka 等框架已经集成 Reactive Streams 并且受益良多。Oracle 公司也在 Java SE 9 中引入了一个新的 Reactive Streams API。
Java SE 9 Reactive Streams API 是一个发布订阅型框架,使我们能够非常简单地使用 Java 语言就能实现异步的、可拓展的和并行的应用。
Java SE 9 引进下面这些 API 来在基于 Java 语言的应用中开发 Reactive Streams:
- java.util.concurrent.Flow
- java.util.concurrent.Flow.Publisher
- java.util.concurrent.Flow.Subscriber
- java.util.concurrent.Flow.Processor
9、Diamond Operator for Anonymous Inner Class
我们知道,Java SE 7 引入了一个新的特性:Diamond Operator,来避免冗长代码和提升可读性。然而在 Java SE 8 中,Oracle 公司发现在 Diamond 操作器和匿名内部类的使用中存在一些局限性,后来修复了这些问题并准备将其作为 Java 9 的一部分发布出去
List<String> list = new ArrayList <>(){};
10、Optional Class Improvements
在 Java SE 9 中,Oracle 公司添加了一些新的实用方法到 java.util.Optional 类里面。这里我将使用一些简单的示例来描述其中的一个:stream 方法。
如果一个值出现在给定 Optional 对象中,stream() 方法可以返回包含该值的一个顺序 Stream 对象。否则,将返回一个空 Stream。
stream() 方法已经被添加,并用来在 Optional 对象中使用,如
- stream()
Stream<Optional> emp = getEmployee(id)
Stream empStream = emp.flatMap(Optional::stream)
- ifPresentOrElse()
public void ifPresentOrElse(Consumerl<? super Tl> action, Runnable emptyAction)
jshell> Optional<Integer> opt1 = Optional.of(4)
opt1 ==> Optional[4]
jshell> opt1.ifPresentOrElse( x -> System.out.println("Result found: " + x), () -> System.out.println("Not Found."))
Result found: 4
jshell> Optional<Integer> opt2 = Optional.empty()
opt2 ==> Optional.empty
jshell> opt2.ifPresentOrElse( x -> System.out.println("Result found: " + x), () -> System.out.println("Not Found."))
Not Found.
- or()
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier)
jshell> Optional<String> opStr = Optional.of("Rams")
opStr ==> Optional[Rams]
jshell> import java.util.function.*
jshell> Supplier<Optional<String>> supStr = () -> Optional.of("No Name")
supStr ==> $Lambda$67/222624801@23faf8f2
jshell> opStr.or(supStr)
$5 ==> Optional[Rams]
jshell> Optional<String> opStr = Optional.empty()
opStr ==> Optional.empty
jshell> Supplier<Optional<String>> supStr = () -> Optional.of("No Name")
supStr ==> $Lambda$67/222624801@23faf8f2
jshell> opStr.or(supStr)
$7 ==> Optional[No Name]
11、Stream API Improvements
在 Java SE 9 中,Oracle 公司添加了四个非常有用的新方法到 java.util.Stream 接口里面。正如 Stream 作为一个接口类,所有这些新的实现方法都是默认方法。其中有两个方法非常重要:dropWhile 和 takeWhile。
如果你熟悉 Scala 语言或者其它函数编程语言的话,你一定知道这些方法。他们在写一些功能样式代码时非常有用。
这个 takeWhile() 方法使用一个断言作为参数,返回给定 Stream 的子集直到断言语句第一次返回 false。如果第一个值不满足断言条件,将返回一个空的 Stream。
- takeWhile
default Stream<T> takeWhile(Predicate<? super T> predicate)
有序
jshell> Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9,10)
stream ==> java.util.stream.ReferencePipeline$Head@55d56113
jshell> stream.takeWhile(x -> x < 4).forEach(a -> System.out.println(a))
1
2
3
无序
jshell> Stream<Integer> stream = Stream.of(1,2,4,5,3,6,7,8,9,10)
stream ==> java.util.stream.ReferencePipeline$Head@55d56113
jshell> stream.takeWhile(x -> x < 4).forEach(a -> System.out.println(a))
1
2
- dropWhile
在Stream API中,dropWhile()方法删除了与断言匹配的最长的前缀元素,并返回其他元素。
default Stream<T> dropWhile(Predicate<? super T> predicate)
jshell> Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9,10)
stream ==> java.util.stream.ReferencePipeline$Head@55d56113
jshell> stream.dropWhile(x -> x < 4).forEach(a -> System.out.println(a))
4
5
6
7
8
9
10
- iterate
在 Stream API中,iterate()返回以initialValue(第一个参数)开头的元素流,匹配断言(第二个参数),并使用第三个参数生成下一个元素。
static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
jshell> IntStream.iterate(2, x -> x < 20, x -> x * x).forEach(System.out::println)
2
4
16
jshell> IntStream.iterate(2, x -> x < 20, x -> x * x).forEach(System.out::println)
2
4
16
Java SE 9's iterate() = Java SE 8's iterate() + Java SE 8's filter()
jshell> IntStream.iterate(2, x -> x * x).filter(x -> x < 20).forEach(System.out::println)
2
4
16
- ofNullable
在Stream API中,ofNullable()返回包含单个元素的顺序流,如果非空,则返回一个空流。
jshell> Stream<Integer> s = Stream.ofNullable(1)
s ==> java.util.stream.ReferencePipeline$Head@1e965684
jshell> s.forEach(System.out::println)
1
jshell> Stream<Integer> s = Stream.ofNullable(null)
s ==> java.util.stream.ReferencePipeline$Head@3b088d51
jshell> s.forEach(System.out::println)
jshell>
12、Enhanced @Deprecated annotation
在 Java SE 8 和更早版本上,@Deprecated 注解只是一个没有任何方法的标记类接口。它的作用是标记一个 Java API,可以是 calss,field,method,interface,constructor 等。
注释@Deprecated可以标记Java API。注释@Deprecated有很多种含义,例如它可以表示在不远的将来的某个时间,被标记的API将会被移除。它也可以表示这个API已经被破坏了,并不应该再被使用。它还有其它很多含义。为了提供更多有关@Deprecated的信息,@Deprecated添加了forRemoval元素和since元素。
Java SE 9 中也提供了扫描jar文件的工具jdeprscan。这款工具也可以扫描一个聚合类,这个类使用了Java SE中的已废弃的API元素。 这个工具将会对使用已经编译好的库的应用程序有帮助,这样使用者就不知道这个已经编译好的库中使用了那些已废弃的API。
13、HTTP 2 Client
在 Java SE 9 中,Oracle 公司将发布新的 HTTP 2 Client API 来支持 HTTP/2 协议和 WebSocket 特性。现有的 HTTP Client API 存在很多问题(如支持 HTTP/1.1 协议但是不支持 HTTP/2 协议和 WebSocket,仅仅作用在 Blocking 模式中,并存在大量性能问题),他们正在被使用新的 HTTP 客户端的 HttpURLConnection API 所替代。
Oracle 公司准备在 “java.net.http” 包下引入新的 HTTP 2 Client API。它将同时支持 HTTP/1.1 和 HTTP/2 协议,也同时支持同步(Blocking Mode)和异步模式,支持 WebSocket API 使用中的异步模式。
jshell> import java.net.http.*
jshell> import static java.net.http.HttpRequest.*
jshell> import static java.net.http.HttpResponse.*
jshell> URI uri = new URI("http://rams4java.blogspot.co.uk/2016/05/java-news.html")
uri ==> http://rams4java.blogspot.co.uk/2016/05/java-news.html
jshell> HttpResponse response = HttpRequest.create(uri).body(noBody()).GET().response()
response ==> java.net.http.HttpResponseImpl@79efed2d
jshell> System.out.println("Response was " + response.body(asString()))
14、Мulti-Resolution Image API
在 Java SE 9 中,Oracle 公司将引入一个新的 Мulti-Resolution Image API。这个 API 中比较重要的接口是 MultiResolutionImage,在 java.awt.image 包下可获取到。
MultiResolutionImage 封装不同高度和宽度图片(不同解决方案)到一个集合中,并允许我们按需查询使用
15、Miscellaneous Java 9 Features
- GC增强
- 统一的JVM日志
- HTML5风格的Java帮助文档
- 保留下划线字符。变量不能被命名为_;
- 废弃Applet API;
- javac不再支持Java1.4以及之前的版本;
- 废弃Java浏览器插件;
- 栈遍历API–栈遍历API能过滤和迟访问在堆
- 栈跟踪中的信息