1 模块系统
1.1 产生背景
- 解决越来越臃肿的java运行环境,主要目的在于减少内存的开销,只须必要模块,而非全部jdk模块。
- 每个公共类都可以被该项目中别的类访问,这样会导致使用了并不想访问的API
- 本质上来讲,模块的概念是在package外再裹上一层,用模块来管理各个package的暴露和隐藏。使得代码组织上更安全
1.2 具体用用
项目结构如下
1.2.1 模块提供方
- 在src目录下添加module-info.java文件
module demo {
import com.demo;
}
1.2.2 模块需求方
module test {
requires com.demo;
}
2 JShell命令使用
REPL(Read Eval Print Loop)意为交互式的编程环境。
JShell 是 Java 9 新增的一个交互式的编程环境工具。它允许你无需使用类或者方法包装来执行 Java 语句。它与 Python 的解释器类似,可以直接 输入表达式并查看其执行结果。
3 多版本兼容 jar 包
新版本java出现的时候,你的库用户需要花费多年时间才会切换到新版本。这就意味着库得去向后兼容你想要支持的最老java版本,很长时间不能使用新版本特性。多版本兼容jar功能能让你创建仅在特定版本的java环境中运行库程序选择使用的class版本
具体使用可参考:https://www.runoob.com/java/java9-multirelease-jar.html
4 语法改进
4.1 私有接口方法
在接口中使用private私有方法。我们可以使用 private 访问修饰符在接口中编写私有方法
4.2 钻石操作符的使用升级
钻石操作符,是官方给出的说法,其实就是之前版本的泛型操作符,图中的代码在java8中是编译不通过的,但是java9可以,这个主要涉及到匿名子类的时候
4.3 改进的 try-with-resources
try-with-resources 是 JDK 7 中一个新的异常处理机制,它能够很容易地关闭在 try-catch 语句块中使用的资源。所谓的资源(resource)是指在程序完成后,必须关闭的对象。try-with-resources 语句确保了每个资源在语句结束时关闭。所有实现了 java.lang.AutoCloseable 接口(其中,它包括实现了 java.io.Closeable 的所有对象),可以使用作为资源。
try-with-resources 声明在 JDK 9 已得到改进。如果你已经有一个资源是 final 或等效于 final 变量,您可以在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class Tester {
public static void main(String[] args) throws IOException {
System.out.println(readData("test"));
}
static String readData(String message) throws IOException {
Reader inputString = new StringReader(message);
BufferedReader br = new BufferedReader(inputString);
try (BufferedReader br1 = br) {
return br1.readLine();
}
}
}
以上实例中我们需要在 try 语句块中声明资源 br1,然后才能使用它。
在 Java 9 中,我们不需要声明资源 br1 就可以使用它,并得到相同的结果。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class Tester {
public static void main(String[] args) throws IOException {
System.out.println(readData("test"));
}
static String readData(String message) throws IOException {
Reader inputString = new StringReader(message);
BufferedReader br = new BufferedReader(inputString);
BufferedWritter bw = new BufferedWritter(new StringWritter());
// 如果有多个资源则通过分号分隔
try (br; bw) {
return br.readLine();
}
}
}
5 String底层存储结构变化
String的实现底层由char[] 改为byte[],存储效率变高,调用效率同样变高。StringBuffer和StringBuilder同样改变。
JDK9 之前的库的 String 类的实现使用了 char 数组来存放字符串,char 占用16位,即两字节。
这种情况下,如果我们要存储字符 A ,则为 0x00 0x41 ,此时前面的一个字节空间浪费了。但如果保存中文字符则不存在浪费的情况,也就是说如果保存 ISO-8859-1 编码内的字符则浪费,之外的字符则不会浪费。
而 JDK9 后 String 类的实现使用了 byte 数组存放字符串,每个 byte 占用8位,即1字节。
在JDK9的String类中,维护了这样的一个属性coder,它是一个编码格式的标识,使用LATIN1还是UTF-16,这个是在String生成的时候自动的,如果字符串中都是能用LATIN1就能表示的就是0,否则就是UTF-16.
6 集合工厂方法
Java 9 List,Set 和 Map 接口中,新的静态工厂方法可以创建这些集合的不可变实例。
这些工厂方法可以以更简洁的方式来创建集合。
- 旧方法创建集合
public static void main(String []args) {
Set<String> set = new HashSet<>();
set.add("A");
set.add("B");
set.add("C");
set = Collections.unmodifiableSet(set);
System.out.println(set);
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list = Collections.unmodifiableList(list);
System.out.println(list);
Map<String, String> map = new HashMap<>();
map.put("A","Apple");
map.put("B","Boy");
map.put("C","Cat");
map = Collections.unmodifiableMap(map);
System.out.println(map);
}
- 新方法创建集合
public static void main(String []args) {
Set<String> set = Set.of("A", "B", "C");
System.out.println(set);
List<String> list = List.of("A", "B", "C");
System.out.println(list);
Map<String, String> map = Map.of("A","Apple","B","Boy","C","Cat");
System.out.println(map);
Map<String, String> map1 = Map.ofEntries (
new AbstractMap.SimpleEntry<>("A","Apple"),
new AbstractMap.SimpleEntry<>("B","Boy"),
new AbstractMap.SimpleEntry<>("C","Cat"));
System.out.println(map1);
}
7 改进的 Stream API
java 9 改进的 Stream API 添加了一些便利的方法,使流处理更容易,并使用收集器编写复杂的查询。
Java 9 为 Stream 新增了几个方法:dropWhile、takeWhile、ofNullable,为 iterate 方法新增了一个重载方法。
7.1 takeWhile
有序的 Stream 中,takeWhile 返回从开头开始的尽量多的元素;在无序的 Stream 中,takeWhile 返回从开头开始的符合 Predicate 要求的元素的子集。
测试代码:
public static void main(String[] args) {
Stream.of("a","b","c","","e","f").takeWhile(s->!s.isEmpty())
.forEach(System.out::print);
}
输出:abc
7.2 dropWhile
和 takeWhile 作用相反的,使用一个断言作为参数,直到断言语句第一次返回 false 才返回给定 Stream 的子集。
测试代码:
public static void main(String[] args) {
Stream.of("a","b","c","","e","f").dropWhile(s-> !s.isEmpty())
.forEach(System.out::print);
}
输出:ef
7.3 iterate
方法允许使用初始种子值创建顺序(可能是无限)流,并迭代应用指定的下一个方法。 当指定的 hasNext 的 predicate 返回 false 时,迭代停止。
测试代码:
public static void main(String[] args) {
IntStream.iterate(3, x -> x < 10, x -> x+ 3).forEach(System.out::println);
}
输出:3 6 9
7.4 ofNullable
可以预防 NullPointerExceptions 异常, 可以通过检查流来避免 null 值。如果指定元素为非 null,则获取一个元素并生成单个元素流,元素为 null 则返回一个空流。
测试代码
public static void main(String[] args) {
long count = Stream.ofNullable(100).count();
System.out.println(count);
count = Stream.ofNullable(null).count();
System.out.println(count);
}
输出:1 0
8 改进的 Optional 类
Java 8 中引入,Optional 类的引入很好的解决空指针异常。在 java 9 中, 添加了三个方法来改进它的功能:
stream()
ifPresentOrElse()
or()
8.1 stream
将 Optional 转为一个 Stream,如果该 Optional 中包含值,那么就返回包含这个值的 Stream,否则返回一个空的 Stream(Stream.empty())。
测试代码:
public static void main(String[] args) {
List<Optional<String>> list = Arrays.asList (
Optional.empty(),
Optional.of("A"),
Optional.empty(),
Optional.of("B"));
List<String> filteredList = list.stream()
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
.collect(Collectors.toList());
List<String> filteredListJava9 = list.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
System.out.println(filteredList);
System.out.println(filteredListJava9);
}
输出:
[A, B]
[A, B]
8.2 ifPresentOrElse
ifPresentOrElse 方法的改进就是有了 else,接受两个参数 Consumer 和 Runnable。
ifPresentOrElse 方法的用途是,如果一个 Optional 包含值,则对其包含的值调用函数 action,即 action.accept(value),这与 ifPresent 一致;与 ifPresent 方法的区别在于,ifPresentOrElse 还有第二个参数 emptyAction —— 如果 Optional 不包含值,那么 ifPresentOrElse 便会调用 emptyAction,即 emptyAction.run()。
测试代码:
public static void main(String[] args) {
Optional<Integer> optional = Optional.of(1);
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
System.out.println("Not Present."));
optional = Optional.empty();
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
System.out.println("Not Present."));
}
输出:
Value: 1
Not Present.
8.3 or
如果值存在,返回 Optional 指定的值,否则返回一个预设的值。
测试代码
public static void main(String[] args) {
Optional<String> optional1 = Optional.of("Mahesh");
Supplier<Optional<String>> supplierString = () -> Optional.of("Not Present");
optional1 = optional1.or( supplierString);
optional1.ifPresent( x -> System.out.println("Value: " + x));
optional1 = Optional.empty();
optional1 = optional1.or( supplierString);
optional1.ifPresent( x -> System.out.println("Value: " + x));
}
输出
Value: Mahesh
Value: Not Present
9 多分辨率图像 API
Java 9 定义多分辨率图像 API,开发者可以很容易的操作和展示不同分辨率的图像了。
以下是多分辨率图像的主要操作方法:
- Image getResolutionVariant(double destImageWidth, double destImageHeight) − 获取特定分辨率的图像变体-表示一张已知分辨率单位为DPI的特定尺寸大小的逻辑图像,并且这张图像是最佳的变体。。
- List getResolutionVariants() − 返回可读的分辨率的图像变体列表。
10 HTTP 2 客户端
JDK9之前提供HttpURLConnection API来实现Http访问功能,但是这个类基本很少使用,一般都会选择Apache的Http Client,此次在Java 9的版本中引入了一个新的package:java.net.http,里面提供了对Http访问很好的支持,不仅支持Http1.1而且还支持HTTP2,以及WebSocket,性能比较好
测试代码
public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
HttpClient httpClient = HttpClient.newHttpClient();
HttpResponse httpResponse = httpClient.send(
HttpRequest
.newBuilder(new URI("http://transport.opendata.ch/v1/connections?from=Bern&to=z%C3%BCrich&datetime=2017-09-21T17%3A00")
).GET().build(),
HttpResponse.BodyHandler.asString()
);
int code = httpResponse.statusCode();
System.out.println(code);
System.out.println(httpResponse.body().toString());
}
11 改进的 @Deprecated 注解
注解 @Deprecated 可以标记 Java API 状态,可以是以下几种:
使用它存在风险,可能导致错误
可能在未来版本中不兼容
可能在未来版本中删除
一个更好和更高效的方案已经取代它。
Java 9 中注解增加了两个新元素:since 和 forRemoval。
since: 元素指定已注解的API元素已被弃用的版本。
forRemoval: 元素表示注解的 API 元素在将来的版本中被删除,应该迁移 API。
12 JS引擎升级: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 代码。