1 模块化系统
1.1 JDK9之前没有解决的问题
Java运行环境代码臃肿、效率低:JDK9之前,每一个runtime自带开箱即用的所有编译好的平台类,这些类被一起打包到一个JRE文件叫做rt.jar。我们只需要将我们应用的类放到classpath中,这样runtime就可以找到,而其他的平台类就可以简单粗暴的从rt.jar文件中寻找。尽管我们的应用只用到了这个庞大的rt.jar的一部分,但是,这对JVM管理来说,不仅增加了非必要类的体积,还增加了性能负载。JDK9的模块化可以按需自定义runtime,这也是JDK9文件夹下没有jre目录的原因。
无法隐藏内部API和类型:很难真正的对代码进行封装,系统对于不同部分的代码无法分离。在早起我们实现封装都是需要依赖权限修饰符,而权限修饰符只能修改类、成员变量、成员方法。权限修饰符不能帮助我们对包进行隐藏,JDK9我们可以通过隐藏包从而隐藏包中的所有类。
1.2 模块化的设计理念
- 模块独立、化繁为简:模块化将JDK分成一组模块,可以在编译时、运行时或构建时进行组合。
1.3 模块化的实现目标
主要目的在于减少内存的开销。
只需必要模块,而非全面JDK模块,可以简化各种类库和大型应用的开发和维护。
改进JavaSE平台,使其可以适用不同大小的计算设备。
改进其安全性,可维护性,提高性能。
1.4 应用示例
- 应用示例结构化图:

应用示例步骤:
新建jdk9工程,其pom如下:
<?xml version="1.0" encoding="UTF-8"?><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://maven.apache.org/POM/4.0.0"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.sunxiaping</groupId><artifactId>jdk9</artifactId><packaging>pom</packaging><version>1.0</version><modules><module>module1</module><module>module2</module></modules><build><plugins><!-- 设置JDK 9版本 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>9</source><target>9</target><encoding>UTF-8</encoding><showWarnings>true</showWarnings></configuration></plugin></plugins></build></project>
在jdk9工程下新建module1模块,其pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>jdk9</artifactId><groupId>com.sunxiaping</groupId><version>1.0</version></parent><modelVersion>4.0.0</modelVersion><artifactId>module1</artifactId></project>
在module1模块下新建Person.java ```java package com.sunxiaping.bean;
public class Person { private String name;
private Integer age;public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return this.name;}public void setName(String name) {this.name = name;}public Integer getAge() {return this.age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + this.name + '\'' +", age=" + this.age +'}';}
}
- 在module1中新建module-info.java```java//定义模块信息module module1 {//导出包:对外输出com.sunxiaping.bean包exports com.sunxiaping.bean;}

在jdk9工程下新建module2模块,其pom.xml如下: ```xml <?xml version=”1.0” encoding=”UTF-8”?> <project xmlns=”http://maven.apache.org/POM/4.0.0“
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
jdk9 com.sunxiaping 1.0 4.0.0 module2
- 在module2中新建module-info.java```javamodule module2 {requires module1;}

在module2中添加module1的依赖,其pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://maven.apache.org/POM/4.0.0"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>jdk9</artifactId><groupId>com.sunxiaping</groupId><version>1.0</version></parent><modelVersion>4.0.0</modelVersion><artifactId>module2</artifactId><dependencies><dependency><groupId>com.sunxiaping</groupId><artifactId>module1</artifactId><version>1.0</version><scope>compile</scope></dependency></dependencies></project>
在module2中新建ModuleTest.java ```java package com.sunxiaping.java;
import com.sunxiaping.bean.Person;
public class ModuleTest { public static void main(String[] args) { Person person = new Person(“张三”, 23);
System.out.println("person = " + person);}
}
<a name="0b904450"></a># 2 交互式编程:JShell工具<a name="663a898c"></a>## 2.1 什么是交互式编程?- 像Python和Scala之类的语言早就有了交互式编程环境REPL,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码,就可以在编译前获取对程序的反馈。- 而JDK9之前的版本要想执行代码,必须创建文件、声明类、提供测试方法方可实现。<a name="2cabffea"></a>## 2.2 交互式编程的作用- 即时反馈。<a name="18309c2c"></a>## 2.3 应用示例- 调用JShell:```shell# 前提需要配置JDK9的环境变量jshell

- 获取帮助:
/help intro

- 基础使用:
//直接定义变量和方法,并使用定义的变量和方法System.out.println("aa");int a = 10;int b= 20;int c = a + b;System.out.println(c);public int add(int a,int b){return a + b;}int k = add(3,4);System.out.println(k);

- 导入指定的包:
import java.util.*;

- 默认已经导入如下的所有包:
/imports

- 列出当前session里面的所有有效代码片段:
/list

- 列出当前session下所有创建过的变量:
/var

- 列出当前session下所有创建过的方法:
/methods

- 使用外部编辑器来编写Java代码:
/edit

- 从外部加载源代码(需要在指定的目录下提供java文件):
/open 指定目录下的Java文件绝对路径

- 没有受检异常(编译时异常):JShell在后台为我们隐藏了

- 退出:
/exit

3 多版本兼容jar
3.1 概述
- 当一个新版本的Java出现的时候,你的库用户要花费数年时间才会切换到新的版本。这就意味着库必须要兼容你要支持的最老的Java版本(通常是Java6或Java7)。这实际上意味着未来的很长一段时间,你都不能在库中运用Java9所提供的新特性。
- 幸运的是,多版本兼容jar功能让你创建仅在特定版本的Java环境中运行库程序选择使用的class版本。
- 举例:在下述场景中,root.jar可以在Java9中使用,不过A或B类使用的不是顶层的root.A或root.B这两个class,而是处在“META-INF/versions/9”下面的这两个。这是特别为Java9准备的class版本,可以运用Java9所提供的特性和库。同时,在早期的Java版本中使用这个jar也是能运行的,因为较老版本的Java只会看到顶层的A类或B类。
jar root- A.class- B.class- C.class- D.class- META-INF- versions- 9- A.class- B.class
3.2 应用示例
- 提供必要的类:
- 在src/main/java下新建Generator和Application: ```java package com.sunxiaping;
import java.util.HashSet; import java.util.Set;
/**
- @author 许大仙
- @version 1.0
@since 2020-11-30 15:29 */ public class Generator {
public Set
createStrings(){ Set<String> set = new HashSet<String>();set.add("java");set.add("8");return set;
} }
java package com.sunxiaping;
/**
- @author 许大仙
- @version 1.0
@since 2020-11-30 15:31 */ public class Application { public static void main(String[] args) {
com.sunxiaping.Generator generator = new com.sunxiaping.Generator();System.out.println("generator = " + generator.createStrings());
} } ```
- 在src/main/java-9下新建Generator: ```java package com.sunxiaping;
import java.util.Set;
/**
- @author 许大仙
- @version 1.0
- @since 2020-11-30 15:32
*/
public class Generator {
public Set
createStrings(){
} } ```return Set.of("Java", "9");
- 打包:
javac -d build --release 8 src/mian/java/com/sunxiaping/*.java
javac -d build --release 9 src/mian/java-9/com/sunxiaping/*.java
jar --create --main-class=Application --file multijar.jar -C build . --release 9 -c build9 .
4 接口中定义私有方法
4.1 概述
- Java8中规定接口中的方法除了抽象方法之外,还可以定义静态方法和默认的方法。一定程度上,扩展了接口的功能,此时的接口更像是一个抽象类。
- 在Java9中,接口更加的灵活和强大,连方法的访问权限修饰符都可以声明为private了,此时方法将不会成为你对外暴露的API的一部分。
4.2 应用示例
- 示例:
package com.sunxiaping;/*** JDK9支持私有方法** @author 许大仙* @version 1.0* @since 2020-11-30 16:11*/public interface DemoInterface {String name = "静态常量";/*** 抽象方法*/void method();/*** 默认方法*/default void method2(){method4();}/*** 静态方法*/static void method3(){System.out.println("静态方法,访问修饰符是public");}/*** 私有方法*/private void method4(){System.out.println("这是一个私有方法");}/*** 私有静态方法*/private static void method5(){System.out.println("私有静态方法");}}
5 钻石操作符的升级
5.1 概述
- JDK9允许匿名内部类和
<>一起使用。
5.2 应用示例
- 示例:
package com.sunxiaping;import java.util.ArrayList;import java.util.List;/*** 钻石标识符的升级:JDK允许钻石标识符和匿名内部类一起使用** @author 许大仙* @version 1.0* @since 2020-12-01 08:39*/public class Demo {public static void main(String[] args) {//创建继承于ArrayList的匿名内部类List<String> list = new ArrayList<>() {};list.add("aa");list.add("bb");list.add("cc");list.add("dd");list.forEach(System.out::println);}}
6 异常处理升级
- JDK8之前的写法:
package com.sunxiaping;import org.junit.Test;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.Scanner;/*** @author 许大仙* @version 1.0* @since 2020-12-01 08:51*/public class TryDemo {/*** JDK8之前的写法*/public static void main(String[] args) {InputStreamReader inputStreamReader = null;BufferedReader bufferedReader = null;try {System.out.println("请输入:");bufferedReader = new BufferedReader(new InputStreamReader(System.in));String str;while (null != (str = bufferedReader.readLine())) {System.out.println(str);}} catch (Exception e) {e.printStackTrace();} finally {if (inputStreamReader != null) {try {inputStreamReader.close();} catch (IOException e) {e.printStackTrace();}}if (bufferedReader != null) {try {bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}}}}
- JDK8的写法:
package com.sunxiaping;import java.io.BufferedReader;import java.io.InputStreamReader;/*** @author 许大仙* @version 1.0* @since 2020-12-01 08:51*/public class TryDemo {/*** JDK8的写法:也被称为try-with-resources* 要求资源对象的实例化,必须放在try的一对()内*/public static void main(String[] args) {System.out.println("请输入:");try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in))) {String str;while (null != (str = bufferedReader.readLine())) {System.out.println(str);}} catch (Exception e) {e.printStackTrace();}}}
- JDK9的写法:
package com.sunxiaping;import java.io.BufferedReader;import java.io.InputStreamReader;/*** @author 许大仙* @version 1.0* @since 2020-12-01 08:51*/public class TryDemo {/*** JDK9:可以在try()中调用已经实例化的资源对象*/public static void main(String[] args) {System.out.println("请输入:");BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));try (bufferedReader) {String str;while (null != (str = bufferedReader.readLine())) {System.out.println(str);}} catch (Exception e) {e.printStackTrace();}}}
7 下划线命名标识符的限制
- 在JDK8之前,标识符可以独立使用”_”来命名:
package com.sunxiaping;import org.junit.Test;/*** @author 许大仙* @version 1.0* @since 2020-12-01 10:08*/public class VariableDemo {/*** JDK8中,标识符是可以使用"_"来命名的*/@Testpublic void test() {String _ = "hello";System.out.println("_ = " + _);}}
- JDK9中规定”_”不可以单独命名标识符,如果使用,会报错:
package com.sunxiaping;import org.junit.Test;/*** @author 许大仙* @version 1.0* @since 2020-12-01 10:08*/public class VariableDemo {/*** JDK9中规定"_"不可以单独命名标识符,如果使用,会报错*/@Testpublic void test() {String _ = "world";System.out.println("_ = " + _);}}
正常情况下,脑残才会使用”_”来命名标识符。
8 String底层存储结构的变化
- String不再用
char[]来存储,而是改成了byte[]加上编码标记,节约了一些空间。
9 创建只读集合
9.1 概述
- 要创建一个只读的、不可改变的集合,必须构造和分配它,然后添加元素,最后包装成一个不可修改的集合。
package com.sunxiaping;import org.junit.Test;import java.util.*;/*** @author 许大仙* @version 1.0* @since 2020-12-01 17:29*/public class CollectionMapDemo {/*** 创建一个只读特点的集合*/@Testpublic void test() {List<String> list = new ArrayList<>();list.add("aa");list.add("bb");list.add("cc");List<String> newList = Collections.unmodifiableList(list);// newList.add("ee"); //不能执行,会抛出异常newList.forEach(System.out::println);}/*** 创建一个只读特点的集合*/@Testpublic void test2() {List<String> list = Collections.unmodifiableList(Arrays.asList("aa", "bb", "cc"));// list.add("ee"); //不能执行,会抛出异常list.forEach(System.out::println);Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("aa", "bb", "cc")));// set.add("ee"); //不能执行,会抛出异常set.forEach(System.out::println);Map<Object, Object> map = Collections.unmodifiableMap(new HashMap<>() {{put("aa", "aa");put("bb", "bb");put("cc", "cc");}});// map.put("ee", "ee"); //不能执行,会抛出异常map.forEach((k, v) -> System.out.println(k + ":" + v));}}
- JDK9因此引入了方便的方法,使得类似的事情更容易表达。

- 调用集合中的静态方法of(),可以将不同数量的参数传输到此工厂方法中。此功能可英语Set和List,也可用于Map的类似形式。此时得到的集合,是不可变得,继续添加元素到这些集合中,会抛出异常。
- 由于Java8中接口方法的实现,可以直接在List、Set和Map的接口内定义这些方法,便于调用。
9.2 应用示例
- 示例:
package com.sunxiaping;import org.junit.Test;import java.util.*;/*** @author 许大仙* @version 1.0* @since 2020-12-01 17:29*/public class CollectionMapDemo {/*** 创建一个只读特点的集合*/@Testpublic void test() {List<String> list = List.of("aa", "bb", "cc");// list.add("ee"); //不能执行,会抛出异常list.forEach(System.out::println);}}
10 增强的Stream API
10.1 概述
- Java的Stream API是Java标准库最好的改进之一,让开发者能够快速运算,从而能够有效的利用数据并行计算。Java8提供的Stream能够利用多核架构实现声明式的数据处理。
- 在Java9中,Stream API变得更好,Stream接口中添加了4个新的方法:dropWhile、takeWhile、ofNullable,还有iterate方法的新重载方法,可以让你提供一个Predicate来指定什么时候结束迭代。
- 除了对Stream本身的扩展,Optional和Stream之间的结合也得到了改进。现在可以通过Optional的新方法stream()将一个Optional对象转换为一个Stream对象。
10.2 应用示例
- 示例:
package com.sunxiaping;import org.junit.Test;import java.util.List;import java.util.stream.Stream;/*** @author 许大仙* @version 1.0* @since 2020-12-01 19:05*/public class StreamAPITest {/*** takeWhile:用于从Stream中获取一部分数据,接收一个Predicate来进行选择。在有序的Stream中,takeWhile返回从开头开始的尽量多的元素。*/@Testpublic void test() {List<Integer> list = List.of(45, 43, -9, 99, 67, 88, 22, 49);list.stream().takeWhile((x) -> x <= 50).forEach(System.out::println); //45 43 -9list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);list.stream().takeWhile((x) -> x <= 5).forEach(System.out::println); //1 2 3 4 5}/*** dropWhile:和takeWhile相反,返回剩余的元素*/@Testpublic void test2() {List<Integer> list = List.of(45, 43, -9, 99, 67, 88, 22, 49);list.stream().dropWhile((x) -> x <= 50).forEach(System.out::println); //99 67 88 22 49list = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);list.stream().dropWhile((x) -> x <= 5).forEach(System.out::println); //6 7 8 9 10}/*** ofNullable:Java8中的Stream不能完全为null,否则会报空指针异常。而Java9中的ofNullable方法允许我们创建一个氮元素的Stream,可以包含一个非空元素,也可以创建一个空的Stream*/@Testpublic void test3() {Stream<Integer> stream1 = Stream.of(1, 2, 3, null);stream1.forEach(System.out::println);System.out.println("------------------");//如果只有单个元素,此元素不能为null,否则会抛出NullPointerException// Stream<Object> stream2 = Stream.of(null);//// stream2.forEach(System.out::println); //NullPointerException//Java9新增ofNullable,允许单元素为nullStream<Object> stream = Stream.ofNullable(null);stream.forEach(System.out::println);}/*** iterate*/@Testpublic void test4() {Stream.iterate(0, x -> x + 1).limit(5).forEach(System.out::println);Stream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);}}
- 示例:
package com.sunxiaping;import org.junit.Test;import java.util.List;import java.util.Optional;import java.util.stream.Stream;/*** @author 许大仙* @version 1.0* @since 2020-12-01 19:25*/public class OptionalTest {@Testpublic void test() {List<String> list = List.of("aa", "bb", "cc", "dd");Stream<List<String>> stream = Optional.ofNullable(list).stream();stream.forEach(System.out::println);Stream<String> stream2 = Optional.ofNullable(list).stream().flatMap(x -> x.stream());stream2.forEach(System.out::println);}}
11 多分辨率图像API
11.1 概述
- 在Mac上,JDK已经支持视网膜显示,但是在Linux和Windows上,它并没有。在那里,Java程序在当前的高分辨率的屏幕上可能看起来非常小,不能使用它们。这是因为像素用于这些系统的大小计算(无论像素实际有多大)。毕竟,高分辨率显示器的有效部分是像素非常小。
- JEP 263以这样的方式扩展了JDK,即 Windows 和 Linux 也考虑到像素的大小。为此,使用比现在更多的现代 API: Direct2D for Windows和 GTK +, 而不是 Xlib for Linux。图形,窗口和文本由此自动缩放。
- JEP 251 还提供处理多分辨率图像的能力,即包含不同分辨率的相同图像的文件。根据相应屏幕的 DPI 度量,然后以适当的分辨率使用图像。
11.2 产生背景
- 新的 API 定义在 java.awt.image 包下。
- 将不同分辨率的图像封装到一张(多分辨率的)图像中,作为它的变体。
- 获取这个图像的所有变体。
- 获取特定分辨率的图像变体-表示一张已知分辨率单位为 DPI 的特定尺寸大小的逻辑图像,并且这张图像是最佳的变体。
- 基于当前屏幕分辨率大小和运用的图像转换算法,java.awt.Graphics 类可以从接口 MultiResolutionImage 获取所需的
变体。 - MultiResolutionImage 的基础实现是java.awt.image.BaseMultiResolutionImage。
12 全新的HTTP客户端API
12.1 概述
- HTTP,用于传输网页的协议,早在 1997 年就被采用在目前的 1.1版本中。直到 2015 年, HTTP2 才成为标准。
- 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
中。 - 全新的 HTTP 客户端 API 可以从 jdk.incubator.httpclient 模块中获取。因为在默认情况下,这个模块是不能根据 classpath 获取的,需要使用 add modules 命令选项配置这个模块,将这个模块添加到 classpath
中。
12.2 应用示例
- 示例:
- module-info.java
/*** @author 许大仙* @version 1.0* @since 2020-12-01 20:07*/module jdk9 {requires jdk.incubator.httpclient;requires junit;}
- HttpClientTest.java
package com.sunxiaping;import jdk.incubator.http.HttpClient;import jdk.incubator.http.HttpRequest;import jdk.incubator.http.HttpResponse;import java.io.IOException;import java.net.URI;/*** @author 许大仙* @version 1.0* @since 2020-12-01 19:55*/public class HttpClientTest {public static void main(String[] args) {HttpClient httpClient = HttpClient.newHttpClient();HttpRequest request = HttpRequest.newBuilder(URI.create("https://www.baidu.com")).GET().build();HttpResponse<String> response = null;try {response = httpClient.send(request, HttpResponse.BodyHandler.asString());} catch (IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(response.statusCode());System.out.println(response.version().name());System.out.println(response.body());}}
13 Deprecated的相关API
- Java9废弃或者移除了几个不常用的功能。其中最主要的是Applet API,现在是标记为废弃的。
- 随着对安全要求的提高,主流浏览器已经取消对Java浏览器插件的支持。HTML5的出现也进一步加速了它的消亡。开发者现在可以使用Java Web Start这样的技术来代替Applet,它可以实现从浏览器启动应用程序或者安装应用程序。
- 同时,appletviewer工具也被标记为废弃。
14 智能Java编译工具
- 智能Java编译工具(sjavac)的第一个阶段始于JEP139这个项目,用于在多核处理器情况下提升JDK的编译速度。如今,这个项目已经进入第二阶段,即JEP199,其目的是改进Java编译工具,并取代目前JDK编译工具javac,继而成为Java环境默认的通用的智能编译工具。
- JDK9还更新了javac编译器以便能够将Java9代码编译运行在低版本的Java中。
15 统一的JVM日志系统
- 日志是解决问题的唯一有效途径:曾经很难知道导致JVM性能问题和导致JVM崩溃的根本原因。不同的JVM日志的碎片化和日志选项(例如:JVM组件对于日志使用的是不同的机制和规则),这使得JVM难以进行调试。
- 解决该问题最佳方法:对所有的JVM组件引入一个单一的系统,这些JVM组件支持细粒度的和易配置的JVM日志。
16 javadoc的HTML5支持
- Java8:生成的Java帮助文档是在HTML4中,而HTML4已经是很早的标准了。
- Java9:javadoc的输出,现在是兼容HTML5的标准。
17 JavaScript引擎升级: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 代码。
18 Java动态编译器
18.1 产生背景
- Oracle 一直在努力提高 Java 启动和运行时性能,希望其能够在更广泛的场景达到或接近本地语言的性能。但是,直到今天,谈到Java,很多 C/C++ 开发者还是会不屑地评价为启动慢,吃内存。
- 简单说,这主要是因为 Java 编译产生的类文件是 Java 虚拟机可以理解的二进制代码,而不是真正的可执行的本地代码, 需要 Java虚拟机进行解释和编译,这带来了额外的开销。
18.2 使用说明
- 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 以一种另类的方式成为现实。
19 总结和展望
19.1 在Java9中看不到什么?
19.1.1 一个标准化和轻量级的JSON API
- 一个标准化和轻量级的 JSON API 被许多 java 开发人员所青睐。但是由于资金问题无法在 Java 9 中见到,但并不会削减掉。 Java 平台首席架构师 Mark Reinhold 在 JDK 9 邮件列中说:“这个 JEP 将是平台上的一个有用的补充,但是在计划中,它并不像 Oracle 资助的其他功能那么重要,可能会重新考虑 JDK 10 或更高版本中实现。”
19.1.2 新的货币API
- 对许多应用而言货币价值都是一个关键的特性,但 JDK 对此却几乎没有任何支持。严格来讲,现有的 java.util.Currency 类只是代表了当前 ISO 4217 货币的一个数据结构,但并没有关联的值或者自定义货
币。 JDK 对货币的运算及转换也没有内建的支持,更别说有一个能够代表货币值的标准类型了。 - 此前, Oracle 公布的 JSR 354 定义了一套新的 Java 货币 API:JavaMoney,计划会在 Java 9 中正式引入。但是目前没有出现在 JDK 9中。
- 不过,如果你用的是 Maven 的话,可以做如下的添加,即可使用相关的 API 处理货币:
<dependency><groupId>org.javamoney</groupId><artifactId>moneta</artifactId><version>0.9</version></dependency>
19.2 展望
- 随着云计算和 AI 等技术浪潮,当前的计算模式和场景正在发生翻天覆地的变化,不仅对 Java 的发展速度提出了更高要求,也深刻影响着 Java 技术的发展方向。 传统的大型企业或互联网应用,正在被云端、 容器化应用、模块化的微服务甚至是函数(FaaS,Function-as-a-Service) 所替代。
- Java虽然标榜面向对象编程,却毫不顾忌的加入面向接口编程思想,又扯出匿名对象之概念,每增加一个新的东西,对Java的根本所在的面向对象思想的一次冲击。反观Python,抓住面向对象的本质,又能在函数编程思想方面游刃有余。 Java对标C/C++,以抛掉内存管理为卖点,却又陷入了JVM优化的噩梦。选择比努力更重要,选择Java的人更需要对它有更清晰的认识。
- Java 需要在新的计算场景下,改进开发效率。 这话说的有点笼统,我谈一些自己的体会, Java 代码虽然进行了一些类型推断等改进,更易用的集合 API 等,但仍然给开发者留下了过于刻板、形式主义的印象,这是一个长期的改进方向。
