JDK9
1 模块化系统
1.1 JDK9之前没有解决的问题
- 1️⃣Java运行环境代码臃肿、效率低:JDK9之前,每一个runtime自带开箱即用的所有编译好的平台类,这些类被一起打包到一个JRE文件叫做rt.jar。我们只需要将我们应用的类放到classpath中,这样runtime就可以找到,而其他的平台类就可以简单粗暴的从rt.jar文件中寻找。尽管我们的应用只用到了这个庞大的rt.jar的一部分,但是,这对JVM管理来说,不仅增加了非必要类的体积,还增加了性能负载。JDK9的模块化可以按需自定义runtime,这也是JDK9文件夹下没有jre目录的原因。
- 2️⃣无法隐藏内部API和类型:很难真正的对代码进行封装,系统对于不同部分的代码无法分离。在早起我们实现封装都是需要依赖权限修饰符,而权限修饰符只能修改类、成员变量、成员方法。权限修饰符不能帮助我们对包进行隐藏,JDK9我们可以通过隐藏包从而隐藏包中的所有类。
1.2 模块化的设计理念
- 模块独立、化繁为简:模块化将JDK分成一组模块,可以在编译时、运行时或构建时进行组合。
1.3 模块化的实现目标
- 1️⃣主要目的在于减少内存的开销。
- 2️⃣只需必要模块,而非全面JDK模块,可以简化各种类库和大型应用的开发和维护。
- 3️⃣改进JavaSE平台,使其可以适用不同大小的计算设备。
- 4️⃣改进其安全性,可维护性,提高性能。
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
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; } @Override public String toString() { return "Person{" + "name='" + this.name + '\'' + ", age=" + this.age + '}'; } }
- 在module1中新建module-info.java
//定义模块信息 module module1 { //导出包:对外输出com.sunxiaping.bean包 exports com.sunxiaping.bean; }
- 在jdk9工程下新建module2模块,其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>module2</artifactId> </project>
- 在module2中新建module-info.java
module 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
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); } }
2 交互式编程:JShell工具
2.1 什么是交互式编程?
- 像Python和Scala之类的语言早就有了交互式编程环境REPL,以交互式的方式对语句和表达式进行求值。开发者只需要输入一些代码,就可以在编译前获取对程序的反馈。
- 而JDK9之前的版本要想执行代码,必须创建文件、声明类、提供测试方法方可实现。
2.2 交互式编程的作用
- 即时反馈。
2.3 应用示例
- 调用JShell:
# 前提需要配置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:
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<String> createStrings(){ Set<String> set = new HashSet<String>(); set.add("java"); set.add("8"); return set; } }
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:
package com.sunxiaping; import java.util.Set; /** * @author 许大仙 * @version 1.0 * @since 2020-11-30 15:32 */ public class Generator { public Set<String> 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中,标识符是可以使用"_"来命名的
*/
@Test
public 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中规定"_"不可以单独命名标识符,如果使用,会报错
*/
@Test
public 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 {
/**
* 创建一个只读特点的集合
*/
@Test
public 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);
}
/**
* 创建一个只读特点的集合
*/
@Test
public 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 {
/**
* 创建一个只读特点的集合
*/
@Test
public 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返回从开头开始的尽量多的元素。
*/
@Test
public 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 -9
list = 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相反,返回剩余的元素
*/
@Test
public 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 49
list = 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
*/
@Test
public 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,允许单元素为null
Stream<Object> stream = Stream.ofNullable(null);
stream.forEach(System.out::println);
}
/**
* iterate
*/
@Test
public 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 {
@Test
public 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 等,但仍然给开发者留下了过于刻板、形式主义的印象,这是一个长期的改进方向。