Skip to content

Latest commit

 

History

History
745 lines (637 loc) · 42.8 KB

File metadata and controls

745 lines (637 loc) · 42.8 KB

Java9新特性

jdk9新特性一览

Java 9 正式发布于 2017 年 9 月 21 日 。作为 Java8 之后 3 年半才发布的新版本,Java 9 带 来了很多重大的变化。其中最重要的改动是 Java 平台模块系统的引入。除此之外,还有一些新的特性。 本文对 Java9 中包含的新特性做了概括性的介绍,可以帮助你快速了解 Java 9。 java 9 提供了超过150项新功能特性,包括备受期待的模块化系统、可交互的 REPL 工具:jshell,JDK 编译工具,Java 公共 API 和私有代码,以及安全增强、扩展提升、性能管理改善等。可以说Java 9是一个庞大的系统工程,完全做了一个整体改变。

此版本为“功能性”版本,并非长期支持版本。Oracle 宣布这些版本只有短期支持(六个月)。目前最新的LTC版本为java11。目前官网已经不提供java9的下载。 详情参考java 11新特性

资料来源: http://openjdk.java.net/projects/jdk9/

新特性 翻译
102: Process API Updates API更新过程
110: HTTP 2 Client HTTP2 客户端
143: Improve Contended Locking 改善竞争锁
158: Unified JVM Logging 统一JVM日志记录
165: Compiler Control 编译器控制
193: Variable Handles 变量处理
197: Segmented Code Cache 分段代码缓存
199: Smart Java Compilation, Phase Two 聪明的Java编译,第二阶段
200: The Modular JDK 模块化JDK
201: Modular Source Code 模块化的源代码
211: Elide Deprecation Warnings on Import Statements 导入语句时省略警告信息
212: Resolve Lint and Doclint Warnings 解决Lint和Doclint警告
213: Milling Project Coin 研磨项目Coin
214: Remove GC Combinations Deprecated in JDK 8 移除JDK 8过时的GC组合
215: Tiered Attribution for javac javac分层归因
216: Process Import Statements Correctly 正确处理导入语句
217: Annotations Pipeline 2.0 注解管道
219: Datagram Transport Layer Security (DTLS) 数据报传输层安全性(DTLS)
220: Modular Run-Time Images 模块化运行时图像
221: Simplified Doclet API 简化Doclet API
222: jshell: The Java Shell (Read-Eval-Print Loop) jshell:Java Shell(Read-Eval-Print-Loop)
223: New Version-String Scheme 字符串新版本方案
224: HTML5 Javadoc Javadoc 支持HTML5
225: Javadoc Search Javadoc搜索
226: UTF-8 Property Files utf - 8属性文件
227: Unicode 7.0 Unicode 7.0
228: Add More Diagnostic Commands 添加更多的诊断命令
229: Create PKCS12 Keystores by Default 创建PKCS12默认密钥存储库
231: Remove Launch-Time JRE Version Selection 删除启动JRE版本选择
232: Improve Secure Application Performance 提高安全应用程序性能
233: Generate Run-Time Compiler Tests Automatically 自动生成运行时编译器测试
235: Test Class-File Attributes Generated by javac 测试javac生成的类文件属性
236: Parser API for Nashorn Nashorn解析器API
237: Linux/AArch64 Port Linux / AArch64端口
238: Multi-Release JAR Files Multi-Release JAR文件
240: Remove the JVM TI hprof Agent 删除JVM TI hprof代理
241: Remove the jhat Tool 删除jhat工具
243: Java-Level JVM Compiler Interface java级别JVM编译器接口
244: TLS Application-Layer Protocol Negotiation Extension TLS应用层协议谈判扩展
245: Validate JVM Command-Line Flag Arguments JVM命令行标记参数进行验证
246: Leverage CPU Instructions for GHASH and RSA 利用CPU指令GHASH和RSA
247: Compile for Older Platform Versions 老平台版本编译
248: Make G1 the Default Garbage Collector 设置G1为默认的垃圾收集器
249: OCSP Stapling for TLS OCSP装订TLS
250: Store Interned Strings in CDS Archives CDS档案中存储实际字符串
251: Multi-Resolution Images 多分辨率图像
252: Use CLDR Locale Data by Default 使用系统默认语言环境数据
253: Prepare JavaFX UI Controls & CSS APIs for Modularization 准备JavaFX UI控件和CSS api用于模块化
254: Compact Strings 紧凑的字符串
255: Merge Selected Xerces 2.11.0 Updates into JAXP 合并选定的Xerces 2.11.0更新到JAXP
256: BeanInfo Annotations BeanInfo注解
257: Update JavaFX/Media to Newer Version of GStreamer 更新JavaFX /Media 到GStreamer的新版本
258: HarfBuzz Font-Layout Engine HarfBuzz文字编排引擎
259: Stack-Walking API 提供Stack – Walking API
260: Encapsulate Most Internal APIs 封装内部api
261: Module System 模块系统
262: TIFF Image I/O TIFF图像I/O
263: HiDPI Graphics on Windows and Linux Windows和Linux上的HiDPI图形
264: Platform Logging API and Service 日志API和服务平台
265: Marlin Graphics Renderer Marlin图形渲染器
266: More Concurrency Updates 更多的并发更新
267: Unicode 8.0 Unicode 8.0
268: XML Catalogs XML目录
269: Convenience Factory Methods for Collections 方便的集合工厂方法
270: Reserved Stack Areas for Critical Sections 保留堆栈领域至关重要的部分
271: Unified GC Logging 统一的GC日志记录
272: Platform-Specific Desktop Features 特定于平台的桌面功能
273: DRBG-Based SecureRandom Implementations 基于DRBG 的SecureRandom实现
274: Enhanced Method Handles 增强的方法处理
275: Modular Java Application Packaging 模块化Java应用程序包装
276: Dynamic Linking of Language-Defined Object Models 语言定义对象模型的动态链接
277: Enhanced Deprecation 增强的弃用
278: Additional Tests for Humongous Objects in G1 为G1的极大对象提供额外的测试
279: Improve Test-Failure Troubleshooting 改善测试失败的故障排除
280: Indify String Concatenation Indify字符串连接
281: HotSpot C++ Unit-Test Framework 热点c++的单元测试框架
282: jlink: The Java Linker jlink:Java连接器
283: Enable GTK 3 on Linux 在Linux上启用GTK 3
284: New HotSpot Build System 新热点的构建系统
285: Spin-Wait Hints 循环等待提示
287: SHA-3 Hash Algorithms SHA-3散列算法
288: Disable SHA-1 Certificates 禁用sha - 1证书
289: Deprecate the Applet API 标记过时的Applet API
290: Filter Incoming Serialization Data 过滤传入的序列化数据
291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector 反对并发标记清理垃圾收集器(CMS)
292: Implement Selected ECMAScript 6 Features in Nashorn 实现选定的ECMAScript Nashorn 6特性
294: Linux/s390x Port Linux / s390x端口
295: Ahead-of-Time Compilation 提前编译
297: Unified arm32/arm64 Port 统一的arm32 / arm64端口
298: Remove Demos and Samples 删除演示和样本
299: Reorganize Documentation 整理文档

模块化系统 Jigsaw > Modularity

官方Feature

  • 200: The Modular JDK 201: Modular Source Code

  • 220: Modular Run-Time Images

  • 260: Encapsulate Most Internal APIs

  • 261: Module System

  • 282: jlink: The Java Linker

概述

Java 平台模块系统,也就是 Project Jigsaw,把模块化开发实践引入到了 Java 平台中。在引入了模块系统之后,JDK 被重新组织成 94 个模块。Java 应用可以通过新增的 jlink 工具,创建出只包含所依赖的 JDK 模块的自定义运行时镜像。这样可以极大的减少 Java 运行时环境的大小。这对于目前流行的不可变基础设施的实践来说,镜像的大小的减少可以节省很多存储空间和带宽资源 。

模块化开发的实践在软件开发领域并不是一个新的概念。Java 开发社区已经使用这样的模块化实践有相当长的一段时间。主流的构建工具,包括 Apache Maven 和 Gradle 都支持把一个大的项目划分成若干个子项目。子项目之间通过不同的依赖关系组织在一起。每个子项目在构建之后都会产生对应的 JAR 文件。 在 Java9 中 ,已有的这些项目可以很容易的升级转换为 Java 9 模块 ,并保持原有的组织结构不变。

Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。这个文件由根目录中的源代码文件 module-info.java 编译而来。该模块声明文件可以描述模块的不同特征。模块声明文件中可以包含的内容如下:

  • 模块导出的包:使用 exports 可以声明模块对其他模块所导出的包。包中的 public 和 protected 类型,以及这些类型的 public 和 protected 成员可以被其他模块所访问。没有声明为导出的包相当于模块中的私有成员,不能被其他模块使用。

  • 模块的依赖关系:使用 requires 可以声明模块对其他模块的依赖关系。使用 requires transitive 可 以把一个模块依赖声明为传递的。传递的模块依赖可以被依赖当前模块的其他模块所读取。 如果一个模块所导出的类型的型构中包含了来自它所依赖的模块的类型,那么对该模块的依赖应该声明为传递的。

  • 服务的提供和使用:如果一个模块中包含了可以被 ServiceLocator 发现的服务接口的实现 ,需要使用 provides with 语句来声明具体的实现类 ;如果一个模块需要使用服务接口,可以使用 uses 语句来声明。

使用

module com.jcohy.sample { 
    exports com.jcohy.sample; 
    requires com.jcohy.common; 
    provides com.jcohy.common.DemoService with
        com.mycompany.sample.DemoServiceImpl; 
}

模块系统中增加了模块路径的概念。模块系统在解析模块时,会从模块路径中进行查找。为了保持与之前 Java 版本的兼容性,CLASSPATH 依然被保留。所有的类型在运行时都属于某个特定的模块。对于从 CLASSPATH 中加载的类型,它们属于加载它们的类加载器对应的未命名模块。可以通过 Class 的 getModule()方法来获取到表示其所在模块的 Module 对象。

在 JVM 启动时,会从应用的根模块开始,根据依赖关系递归的进行解析,直到得到一个表示依赖关系的图。如果解析过程中出现找不到模块的情况,或是在模块路径的同一个地方找到了名称相同的模块,模块解析过程会终止,JVM 也会退出。Java 也提供了相应的 API 与模块系统进行交互。

Jshell

官方Feature

  • 222: jshell: The Java Shell (Read-Eval-Print Loop)

jshell 是 Java 9 新增的一个实用工具。jshell 为 Java 增加了类似 NodeJS 和 Python 中的读取-求值-打印循环( Read-Evaluation-Print Loop ) 。 在 jshell 中 可以直接 输入表达式并查看其执行结果。当需要测试一个方法的运行效果,或是快速的对表达式进行求值时,jshell 都非常实用。只需要通过 jshell 命令启动 jshell,然后直接输入表达式即可。每个表达式的结果会被自动保存下来 ,以数字编号作为引用,类似 $1 和$2 这样的名称 。可以在后续的表达式中引用之前语句的运行结果。 在 jshell 中 ,除了表达式之外,还可以创建 Java 类和方法。jshell 也有基本的代码完成功能。

使用举例

1、调出jshell

jshell

2、获取帮助

jshell> /help intro

3、基本使用

jshell> int add(int x, int y) { 
    ...> return x + y; 
    ...> } 
 | 已创建 方法 add(int,int)

接着就可以在 jshell 中直接使用这个方法

jshell> add(1, 2) 
$19 ==> 3

4、导入包

jshell> import java.util.*

5、查看默认导入的包

jshell> /imports

6、代码补全 TAB键 7、列出当前 session 里所有有效的代码片段

jshell> /list

8、查看当前 session 下所有创建过的变量

jshell> /var

9、查看当前 session 下所有创建过的方法

jshell> /methods

10、从外部文件加载源代码

jshell> /open E:\hello.java

11、没有受检异常(编译时异常)

jshell> URL url = new URL("http://www.baidu.com");
url ==> http://www.baidu.com

说明:本来应该强迫我们捕获一个IOException,但却没有出现。因为jShell在后台为我们隐藏了。 12、退出Jshell

jshell> /exit
 | 再见

多版本兼容jar包

官方Feature

  • 238: Multi-Release JAR Files

当一个新版本的Java出现的时候,你的库用户要花费数年时间才会切换到这个新的版本。这就意味着库得去向后兼容你想要支持的最老的Java版本(许多情况下就是Java 6 或者 Java7)。这实际上意味着未来的很长一段时间,你都不能在库中运用Java 9所提供的新特性。幸运的是,多版本兼容jar功能能让你创建仅在特定版本的Java环境中运行库程序选择使用的class版本。

概述

jar root
  - A.class
  - B.class
  - C.class
  - D.class
  - META-INF
     - versions
        - 9
           - A.class
           - B.class
        - 10
           - A.class

说明: 在上述场景中, root.jar 可以在 Java 9 中使用, 不过 A或B 类使用的不是顶层的 root.A或root.B 这两个class, 而是处在“META-INF/versions/9”下面的这两个。这是特别为 Java 9 准备的 class 版本,可以运用 Java 9 所提供的特性和库。在将来的支持Java 10 JDK上,它将看到A的jdk 10特定版本和B的jdk 9特定版本;同时,在早期的 Java 诸版本中使用这个 JAR 也是能运行的,因为较老版本的 Java 只会看到顶层的A类或 B 类。

使用

1、创建一个类,使用java 9 版本语法

import java.util.Set;
/**
 * Created by jiac on 2017/12/28 0028.
 */
public class Generator {
        
    public Set<String> createStrings() {
        return Set.of("Java", "9");
    }

}

2、创建一个同名类,使用java 8版本语法

import java.util.Set;
import java.util.HashSet;

public class Generator {
    public Set<String> createStrings() {
        Set<String> strings = new HashSet<String>();
        strings.add("Java");
        strings.add("8");
        return strings;
    }
}

3、创建测试类

public class Application {
   public static void testMultiJar(){
      Generator gen = new Generator();
      System.out.println("Generated strings: " + gen.createStrings());
   }
}

4、打包

javac -d build --release 8 src/main/java/com/jcohy/study/*.java
javac -d build9 --release 9 src/main/java9/com/jcohy/study/*.java
jar --create --main-class=Application --file multijar.jar -C build . --release 9 -C build9 .

语法改进

接口的私有方法

官方Feature

213: Milling Project Coin

Java 8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是一个抽象类。 在Java 9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private的了,此时方法将不会成为你对外暴露的API的一部分。

钻石操作符

我们将能够与匿名实现类共同使用钻石操作符(diamond operator) 在java 8中如下的操作是会报错的:

    private List<String> flattenStrings(List<String>... lists) { 
        Set<String> set = new HashSet<>(){}; 
        for(List<String> list : lists) { 
            set.addAll(list); 
        } 
        return new ArrayList<>(set); 
    }

编译报错信息:'<>' cannot be used with anonymous classes

try语句

在java 8 之前,我们习惯于这样处理资源的关闭:

InputStreamReader reader = null; 
try{ 
    reader = new InputStreamReader(System.in); 
    //流的操作 
    reader.read(); 
}catch (IOException e){ 
    e.printStackTrace(); 
}finally{ 
    if(reader != null){
        try {
            reader.close(); 
        } catch (IOException e) {
            e.printStackTrace(); 
        } 
    } 
}

java 8 中,可以实现资源的自动关闭,但是要求执行后必须关闭的所有资源必须在try子句中初始化,否则编译不通过。如下例所示:

try(InputStreamReader reader = new InputStreamReader(System.in)){

}catch (IOException e){
    e.printStackTrace(); 
}

java 9 中,用资源语句编写try将更容易,我们可以在try子句中使用已经初始化过的资源,此时的资源是final的:

public void test3(){
    //jdk 1.9
    InputStreamReader reader = new  InputStreamReader(System.in);
    OutputStreamWriter writer = new OutputStreamWriter(System.out);
    try(reader;writer){
        //reader是final的,不可再被赋值
        //reader = null;
    }catch (IOException e){
        e.printStackTrace();
    }
}

UnderScope(下划线使用的限制)

在java 8 中,标识符可以独立使用“_”来命名:

String _ = "hello"; 
System.out.println(_);

但是,在java 9 中规定“_”不再可以单独命名标识符了,如果使用,会报错

String存储结构变更

官方Feature

  • JEP 254: Compact Strings

动机

String类的在jdk8之前的实现是采用的char数组来存储的,每个字符使用两个字节(十六位)。然而, 从许多不同的应用程序收集到的数据表明,字符串是堆使用的主要组成部分,而且,大多数String对象仅包含Latin-1这样的拉丁字符。 这样的字符仅需要一个字节的存储空间,因此此类String对象的内部char数组中的一半空间都没有使用。

实现

我们建议将String类的内部表示形式从UTF-16字符数组更改为字节数组,再加上一个encoding-flag字段。新的String类将存储基于字符串内容编码为ISO-8859-1 / Latin-1(每个字符一个字节)或UTF-16(每个字符两个字节)的字符。encoding-flag字段将指示使用哪种编码。 与字符串相关的类(例如AbstractStringBuilder,StringBuilder和StringBuffer)将更新为使用相同的表示形式,HotSpot VM的固有字符串操作也将使用相同的表示形式。 这纯粹是实现更改,不更改现有的公共接口。没有计划添加任何新的公共API或其他接口。

集合工厂方法

官方Feature

  • 269: Convenience Factory Methods for Collections 在集合上,Java 9 增加 了 List.of()、Set.of()、Map.of() 和 M ap.ofEntries()等工厂方法来创建不可变集合 ,如 下 所示。
List.of(); 
List.of("Hello", "World"); 
List.of(1, 2, 3);
Set.of(); 
Set.of("Hello", "World"); 
Set.of(1, 2, 3);
Map.of();
Map.of("Hello", 1, "World", 2);

增强的StreamAPI

Stream 中增加了新的方法 ofNullable、dropWhile、takeWhile 和 iterate。在 如下代码中,流中包含了从 1 到 5 的 元素。断言检查元素是否为奇数。第一个元素 1 被删除,结果流中包含 4 个元素。

@Test 
public void testDropWhile() throws Exception { 
    final long count = Stream.of(1, 2, 3, 4, 5) 
        .dropWhile(i -> i % 2 != 0) 
        .count(); 
    assertEquals(4, count); 
}

Collectors 中增加了新的方法 filtering 和 flatMapping。在 如下代码中,对于输入的 String 流 ,先通过 flatMapping 把 String 映射成 Integer 流 ,再把所有的 Integer 收集到一个集合中。

@Test 
public void testFlatMapping() throws Exception { 
    final Set<Integer> result = Stream.of("a", "ab", "abc") 
        .collect(Collectors.flatMapping(v -> v.chars().boxed(), 
            Collectors.toSet())); 
    assertEquals(3, result.size()); 
}

Optiona l 类中新增了 ifPresentOrElse、or 和 stream 等方法。在 如下代码中,Optiona l 流中包含 3 个 元素,其中只有 2 个有值。在使用 flatMap 之后,结果流中包含了 2 个值。

@Test 
public void testStream() throws Exception { 
    final long count = Stream.of( 
        Optional.of(1), 
        Optional.empty(), 
        Optional.of(2) 
    ).flatMap(Optional::stream) 
        .count(); 
    assertEquals(2, count); 
}

多分辨率图像API

官方Feature

  • 251: Multi-Resolution Images
  • 263: HiDPI Graphics on Windows and Linux

在Mac上,JDK已经支持视网膜显示,但在Linux和Windows上,它并没有。在那里,Java程序在当前的高分辨率屏幕上可能看起来很小,不能使用它们。这是因为像素用于这些系统的大小计算(无论像素实际有多大)。毕竟,高分辨率显示器的有效部分是像素非常小。 JEP 263以这样的方式扩展了JDK,即Windows和Linux也考虑到像素的大小。为此,使用比现在更多的现代API:Direct2D for Windows和GTK +,而不是Xlib for Linux。图形,窗口和文本由此自动缩放。 JEP 251还提供处理多分辨率图像的能力,即包含不同分辨率的相同图像的文件。根据相应屏幕的DPI度量,然后以适当的分辨率使用图像。

使用

  • 新的API定义在java.awt.image包下
  • 将不同分辨率的图像封装到一张(多分辨率的)图像中,作为它的变体
  • 获取这个图像的所有变体
  • 获取特定分辨率的图像变体-表示一张已知分辨率单位为DPI的特定尺寸大小的逻辑图像,并且这张图像是最佳的变体。
  • 基于当前屏幕分辨率大小和运用的图像转换算法,java.awt.Graphics类可以从接口MultiResolutionImage获取所需的变体。
  • MultiResolutionImage的基础实现是java.awt.image.BaseMultiResolutionImage

全新的HTTP客户端API

官方Feature

  • 110: HTTP 2 Client

HTTP/1.1和HTTP/2的主要区别是如何在客户端和服务器之间构建和传输数据。HTTP/1.1依赖于请求/响应周期。 HTTP/2允许服务器“push”数据:它可以发送比客户端请求更多的数据。 这使得它可以优先处理并发送对于首先加载网页至关重要的数据。 Java 9中有新的方式来处理HTTP调用。它提供了一个新的HTTP客户端(HttpClient),它将替代仅适用于blocking模式的HttpURLConnection (HttpURLConnection是在HTTP 1.0的时代创建的,并使用了协议无关的方法),并提供对WebSocket 和 HTTP/2的支持。 此外,HTTP客户端还提供API来处理HTTP/2的特性,比如流和服务器推送等功能。 全新的HTTP客户端API可以从jdk.incubator.httpclient模块中获取。因为在默认情况下,这个模块是不能根据classpath获取的,需要使用add modules命令选项配置这个模块,将这个模块添加到classpath中。

使用

Deprecated的相关API

官方Feature

  • 211: Elide Deprecation Warnings on Import Statements
  • 214: Remove GC Combinations Deprecated in JDK 8
  • 277: Enhanced Deprecation
  • 289: Deprecate the Applet API
  • 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector Java 9 废弃或者移除了几个不常用的功能。其中最主要的是 Applet API,现在是标记为废弃的。随着对安全要求的提高,主流浏览器已经取消对 Java 浏览器插件的支持。HTML5 的出现也进一步加速了它的消亡。开发者现在可以使用像 Java Web Start 这样的技术来代替 Applet,它可以实现从浏览器启动应用程序或者安装应用程序。 同时,appletviewer 工具也被标记为废弃。

智能Java编译工具

官方Feature

  • 139: Enhance javac to Improve Build Speed.
  • 199: Smart Java Compilation, Phase Two 智能java编译工具( sjavac )的第一个阶段始于JEP139这个项目,用于在多核处理器情况下提升JDK的编译速度。如今,这个项目已经进入第二阶段,即JEP199,其目的是改进Java编译工具,并取代目前JDK编译工具javac,继而成为Java环境默认的通用的智能编译工具。 JDK 9 还更新了javac 编译器以便能够将 java 9 代码编译运行在低版本 Java 中。

统一的JVM日志系统

官方Feature

  • 158: Unified JVM Logging

  • 271: Unified GC Logging 日志是解决问题的唯一有效途径:曾经很难知道导致JVM性能问题和导致JVM崩溃的根本原因。不同的JVM日志的碎片化和日志选项(例如:JVM组件对于日志使用的是不同的机制和规则),这使得JVM难以进行调试。 解决该问题最佳方法:对所有的JVM组件引入一个单一的系统,这些JVM组件支持细粒度的和易配置的JVM日志。

    Java 9 中 ,JVM 有了统一的日志记录系统,可以使用新的命令行选项-Xlog 来控制 JVM 上 所有组件的日志记录。该日志记录系统可以设置输出的日志消息的标签、级别、修饰符和输出目标等。Java 9 移除了在 Java 8 中 被废弃的垃圾回收器配置组合,同时 把 G1 设为默认的垃圾回收器实现。另外,CMS 垃圾回收器已经被声明为废弃。Java 9 也增加了很多可以通过 jcmd 调用的诊断命令。

javadoc的HTML5支持

官方Feature

  • 224: HTML5 Javadoc
  • 225: Javadoc Search jdk 8 :生成的java帮助文档是在HTML 4 中,而HTML 4 已经是很久的标准了。 jdk 9 :javadoc的输出,现在符合兼容HTML 5 标准。

Javascript引擎升级:Nashorn

官方Feature

  • 236: Parser API for Nashorn
  • 292: Implement Selected ECMAScript 6 Features in Nashorn Nashorn 项目在 JDK 9 中得到改进,它为 Java 提供轻量级的 Javascript 运行时。Nashorn 项目跟随 Netscape 的 Rhino 项目,目的是为了在 Java 中实现一个高性能但轻量级的 Javascript 运行时。Nashorn 项目使得 Java 应用能够嵌入 Javascript。它在 JDK 8 中为 Java 提供一个 Javascript 引擎。 JDK 9 包含一个用来解析 Nashorn 的 ECMAScript 语法树的 API。这个 API 使得 IDE 和服务端框架不需要依赖 Nashorn 项目的内部实现类,就能够分析 ECMAScript 代码。

java的动态编译器

官方Feature

  • 243: Java-Level JVM Compiler Interface

  • 295: Ahead-of-Time Compilation

    Oracle 一直在努力提高 Java 启动和运行时性能,希望其能够在更广泛的场景达到或接近本地语言的性能。但是,直到今天,谈到 Java,很多 C/C++ 开发者还是会不屑地评价为启动慢,吃内存。 简单说,这主要是因为 Java 编译产生的类文件是 Java 虚拟机可以理解的二进制代码,而不是真正的可执行的本地代码,需要 Java 虚拟机进行解释和编译,这带来了额外的开销。 JIT(Just-in-time)编译器可以在运行时将热点编译成本地代码,速度很快。但是 Java 项目现在变得很大很复杂,因此 JIT 编译器需要花费较长时间才能热身完,而且有些 Java 方法还没法编译,性能方面也会下降。AoT 编译就是为了解决这些问题而生的。 在 JDK 9 中, AOT(JEP 295: Ahead-of-Time Compilation)作为实验特性被引入进来,开发者可以利用新的 jaotc 工具将重点代码转换成类似类库一样的文件。虽然仍处于试验阶段,但这个功能使得 Java 应用在被虚拟机启动之前能够先将 Java 类编译为原生代码。此功能旨在改进小型和大型应用程序的启动时间,同时对峰值性能的影响很小。 但是 Java 技术供应商 Excelsior 的营销总监 Dmitry Leskov 担心 AoT 编译技术不够成熟,希望 Oracle 能够等到 Java 10 时有个更稳定版本才发布。 另外 JVMCI (JEP 243: Java-Level JVM Compiler Interface)等特性,对于整个编程语言的发展,可能都具有非常重要的意义,虽然未必引起了广泛关注。目前 Graal Core API 已经被集成进入 Java 9,虽然还只是初始一小步,但是完全用 Java 语言来实现的可靠的、高性能的动态编译器,似乎不再是遥不可及,这是 Java 虚拟机开发工程师的福音。 与此同时,随着 Truffle 框架和 Substrate VM 的发展,已经让个别信心满满的工程师高呼“One VM to Rule Them All!”, 也许就在不远的将来 Ploygot 以一种另类的方式成为现实。

进程 API

Java 9 增加了 ProcessHandle 接口,可以对原生进程进行管理,尤其适合于管理长时间运行的进程。在使用 P rocessBuilder 来启动一个进程之后,可以通过 Process.toHandle()方法来得到一个 ProcessHandl e 对象的实例。通过 ProcessHandle 可以获取到由 ProcessHandle.Info 表 示的进程的基本信息,如命令行参数、可执行文件路径和启动时间等。ProcessHandle 的 onExit()方法返回一个 C ompletableFuture对象,可以在进程结束时执行自定义的动作。 下面代码 中给出了进程 API 的使用示例。

final ProcessBuilder processBuilder = new ProcessBuilder("top") 
    .inheritIO(); 
final ProcessHandle processHandle = processBuilder.start().toHandle(); 
processHandle.onExit().whenCompleteAsync((handle, throwable) -> { 
    if (throwable == null) { 
        System.out.println(handle.pid()); 
    } else { 
        throwable.printStackTrace(); 
    } 
});

平台日志 API 和 服务

Java 9 允许为 JDK 和应用配置同样的日志实现。新增的 System.LoggerFinder 用来管理 JDK 使 用的日志记录器实现。JVM 在运行时只有一个系统范围的 LoggerFinder 实例。LoggerFinder 通 过服务查找机制来加载日志记录器实现。默认情况下,JDK 使用 java.logging 模块中的 java.util.logging 实现。通过 LoggerFinder 的 getLogger()方法就可以获取到表示日志记录器的 System.Logger 实现。应用同样可以使用 System.Logger 来记录日志。这样就保证了 JDK 和应用使用同样的日志实现。我们也可以通过添加自己的 System.LoggerFinder 实现来让 JDK 和应用使用 SLF4J 等其他日志记录框架。 如下代码中给出了平台日志 API 的使用示例。

public class Main { 
    private static final System.Logger LOGGER = System.getLogger("Main"); 
    public static void main(final String[] args) { 
        LOGGER.log(Level.INFO, "Run!");
    } 
}

反应式流

反应式编程的思想最近得到了广泛的流行。 在 Java 平台上有流行的反应式 库 RxJava 和 R eactor。反应式流规范的出发点是提供一个带非阻塞负压( non-blocking backpressure ) 的异步流处理规范。反应式流规范的核心接口已经添加到了 Java9 中的 java.util.concurrent.Flow 类中。

Flow 中包含了 Flow.Publisher、Flow.Subscriber、Flow.Subscription 和 F low.Processor 等 4 个核心接口。Java 9 还提供了 SubmissionPublisher 作为 Flow.Publisher 的一个实现。RxJava 2 和 Reactor 都可以很方便的 与 Flow 类的核心接口进行互操作。

变量句柄

变量句柄是一个变量或一组变量的引用,包括静态域,非静态域,数组元素和堆外数据结构中的组成部分等。变量句柄的含义类似于已有的方法句柄。变量句柄由 J ava 类 java.lang.invoke.VarHandle 来表示。可以使用类 j ava.lang.invoke.MethodHandles.Looku p 中的静态工厂方法来创建 VarHandle 对 象。通过变量句柄,可以在变量上进行各种操作。这些操作称为访问模式。不同的访问模式尤其在内存排序上的不同语义。目前一共有 31 种 访问模式,而每种访问模式都 在 VarHandle 中 有对应的方法。这些方法可以对变量进行读取、写入、原子更新、数值原子更新和比特位原子操作等。VarHandle 还 可以用来访问数组中的单个元素,以及把 byte[]数组 和 ByteBuffer 当成是不同原始类型的数组来访问。

在如下代码 中,我们创建了访问 HandleTarget 类中的域 count 的变量句柄,并在其上进行读取操作。

public class HandleTarget { 
    public int count = 1; 
} 
public class VarHandleTest {
    private HandleTarget handleTarget = new HandleTarget(); 
    private VarHandle varHandle; 
    @Before 
    public void setUp() throws Exception { 
        this.handleTarget = new HandleTarget(); 
        this.varHandle = MethodHandles 
            .lookup() 
            .findVarHandle(HandleTarget.class, "count", int.class); 
    } 
    @Test 
    public void testGet() throws Exception { 
        assertEquals(1, this.varHandle.get(this.handleTarget)); 
        assertEquals(1, this.varHandle.getVolatile(this.handleTarget)); 
        assertEquals(1, this.varHandle.getOpaque(this.handleTarget)); 
        assertEquals(1, this.varHandle.getAcquire(this.handleTarget)); 
    } 
}

类 java.lang.invoke.MethodHandles 增加了更多的静态方法来创建不同类型的方法句柄。

  • arrayConstructor:创建指定类型的数组。
  • arrayLength:获取指定类型的数组的大小。
  • varHandleInvoker 和 varHandleExactInvoker:调用 VarHandle 中的访问模式方法。
  • zero:返回一个类型的默认值。
  • empty:返 回 MethodType 的返回值类型的默认值。
  • loop、countedLoop、iteratedLoop、whileLoop 和 doWhileLoop:创建不同类型的循环,包括 * for 循环、while 循环 和 do-while 循环。
  • tryFinally:把对方法句柄的调用封装在 try-finally 语句中。
  • 在 下面代码中,我们使用 iteratedLoop 来创建一个遍历 S tring 类型迭代器的方法句柄,并计算所有字符串的长度的总和。
public class IteratedLoopTest { 
    static int body(final int sum, final String value) { 
        return sum + value.length(); 
    } 
    @Test 
    public void testIteratedLoop() throws Throwable { 
        final MethodHandle iterator = MethodHandles.constant( 
            Iterator.class, 
            List.of("a", "bc", "def").iterator()); 
        final MethodHandle init = MethodHandles.zero(int.class); 
        final MethodHandle body = MethodHandles 
            .lookup() 
            .findStatic( 
                IteratedLoopTest.class, 
                "body", 
                MethodType.methodType( 
                    int.class, 
                    int.class, 
                    String.class)); 
        final MethodHandle iteratedLoop = MethodHandles 
            .iteratedLoop(iterator, init, body); 
        assertEquals(6, iteratedLoop.invoke()); 
    } 
}

并发

在并发方面,类 CompletableFuture 中增加了几个新的方法。completeAsync 使用一个异步任务来获取结果并完成该 CompletableFuture。orTimeout 在 CompletableFuture 没有在给定的超时时间之前完成,使用 TimeoutException 异常来完成 CompletableFuture。completeOnTimeout 与 o rTimeout 类似,只不过它在超时时使用给定的值来完成 CompletableFuture。新的 Thread.onSpinWai t 方法在当前线程需要使用忙循环来等待时,可以提高等待的效率。

I/O 流新特性

类 java.io.InputStream 中增加了新的方法来读取和复制 InputStream 中包含的数据。

  • readAllBytes:读取 InputStream 中的所有剩余字节。
  • readNBytes: 从 InputStream 中读取指定数量的字节到数组中。
  • transferTo:读取 InputStream 中的全部字节并写入到指定的 OutputStream 中 。 如下代码中给出了这些新方法的使用示例。
public class TestInputStream {
    private InputStream inputStream; 
    private static final String CONTENT = "Hello World"; 
    @Before 
    public void setUp() throws Exception { 
        this.inputStream = 
            TestInputStream.class.getResourceAsStream("/input.txt"); 
    }
    @Test 
    public void testReadAllBytes() throws Exception { 
        final String content = new String(this.inputStream.readAllBytes()); 
        assertEquals(CONTENT, content); 
    } 
    @Test 
    public void testReadNBytes() throws Exception { 
        final byte[] data = new byte[5]; 
        this.inputStream.readNBytes(data, 0, 5); 
        assertEquals("Hello", new String(data)); 
    } 
    @Test 
    public void testTransferTo() throws Exception { 
        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 
        this.inputStream.transferTo(outputStream); 
        assertEquals(CONTENT, outputStream.toString()); 
    } 
}

ObjectInputFilter 可以对 ObjectInputStream 中 包含的内容进行检查,来确保其中包含的数据是合法的。可以使用 ObjectInputStream 的方法 setObjectInputFilter 来设置。ObjectInputFilter 在 进行检查时,可以检查如对象图的最大深度、对象引用的最大数量、输入流中的最大字节数和数组的最大长度等限制,也可以对包含的类的名称进行限制。

改进应用安全性能

Java 9 新增了 4 个 SHA- 3 哈希算法,SHA3-224、SHA3-256、SHA3-384 和 S HA3-512。另外也增加了通过 java.security.SecureRandom 生成使用 DRBG 算法的强随机数。 如下代码中给出了 SHA-3 哈希算法的使用示例

import org.apache.commons.codec.binary.Hex; 
public class SHA3 { 
    public static void main(final String[] args) throws NoSuchAlgorithmException { 
        final MessageDigest instance = MessageDigest.getInstance("SHA3-224"); 
        final byte[] digest = instance.digest("".getBytes()); 
        System.out.println(Hex.encodeHexString(digest)); 
    } 
}

用户界面

类 java.awt.Desktop 增加了新的与桌面进行互动的能力。可以使用 addAppEventListener 方法来添加不同应用事件的监听器,包括应用变为前台应用、应用隐藏或显示、屏幕和系统进入休眠与唤醒、以及 用户会话的开始和终止等。还可以在显示关于窗口和配置窗口时,添加自定义的逻辑。在用户要求退出应用时,可以通过自定义处理器来接受或拒绝退出请求。在 A WT 图像支持方面,可以在应用中使用多分辨率图像。

最后附上所有代码地址 Java8 Code