Hello World

吞风吻雨葬落日 欺山赶海踏雪径

0%

Java 9新特性

jdk 9 主要的新特性参考:
openjdk JDK 9
Oracle官方 Overview of What’s New in JDK 9
baeldung New Features in Java 9

Java 模块化

JPMS(Java Platform Module System)是Java 9发行版的核心亮点。它也被称为Jigshaw项目
项目目标是

  • 使Java SE平台和JDK 缩小尺寸能够在小型计算设备和密集云部署中使用;
  • 提高Java SE平台实现(特别是JDK)的安全性和可维护性;
  • 使开发人员更容易构建和维护库和大型应用程序;
  • 提高应用程序性能;

JDK被拆分的模块 http://cr.openjdk.java.net/~mr/jigsaw/jdk9-module-summary.html

模块化的新手引导 https://openjdk.org/projects/jigsaw/quick-start

模块是多个package的集合,一个jar可以有多个module,一个module可以有多个package。代码上看
jar > module > package > class/inteface

说明
Java9的模块通过requiresexports关键字,对自身所依赖(requires)的模块和自身暴露(exports)出去的内容(package)进行了声明。
本模块只能使用其他模块暴露(exports)出来的内容(package),其他模块也只能使用本模块暴露(exports)出去的内容(package)。

jshell

JShell 是 Java 9 新增的一个交互式的编程环境工具。
它允许你无需使用类或者方法包装来执行 Java 语句。它与 Python 的解释器类似,可以直接 输入表达式并查看其执行结果。
jshell还提供了供其他应用使用的API. 具体使用文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
➜  ~ jshell
| 欢迎使用 JShell -- 版本 17.0.2
| 要大致了解该版本, 请键入: /help intro

jshell> /help intro
|
| intro
| =====
|
| 使用 jshell 工具可以执行 Java 代码,从而立即获取结果。
| 您可以输入 Java 定义(变量、方法、类等等),例如:int x = 8
| 或 Java 表达式,例如:x + x
| 或 Java 语句或导入。
| 这些小块的 Java 代码称为“片段”。
|
| 这些 jshell 工具命令还可以让您了解和
| 控制您正在执行的操作,例如:/list
|
| 有关命令的列表,请执行:/help

jshell>
jshell> "This is my long string. I want a part of it".substring(8,19);
$5 ==> "my long string"

jshell> /save c:\develop\JShell_hello_world.txt
jshell> /open c:\develop\JShell_hello_world.txt
Hello JShell!

jshell> /exit
| 再见

进程API

改进了用于控制和管理操作系统进程的API https://openjdk.org/jeps/102

Java 9 向 Process API 添加了一个 java.lang.ProcessHandle 的接口来增强 java.lang.Process 类。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.time.ZoneId;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.io.IOException;

public class Tester {
public static void main(String[] args) throws IOException {
ProcessBuilder pb = new ProcessBuilder("notepad.exe");
String np = "Not Present";
Process p = pb.start();
ProcessHandle.Info info = p.info();
System.out.printf("Process ID : %s%n", p.pid());
System.out.printf("Command name : %s%n", info.command().orElse(np));
System.out.printf("Command line : %s%n", info.commandLine().orElse(np));

System.out.printf("Start time: %s%n",
info.startInstant().map(i -> i.atZone(ZoneId.systemDefault())
.toLocalDateTime().toString()).orElse(np));

System.out.printf("Arguments : %s%n",
info.arguments().map(a -> Stream.of(a).collect(
Collectors.joining(" "))).orElse(np));

System.out.printf("User : %s%n", info.user().orElse(np));
}
}

输出

1
2
3
4
5
6
Process ID : 5800
Command name : C:\Windows\System32\notepad.exe
Command line : Not Present
Start time: 2017-11-04T21:35:03.626
Arguments : Not Present
User: administrator

关闭子进程

1
2
3
4
childProc = ProcessHandle.current().children();
childProc.forEach(procHandle -> {
assertTrue("Could not kill process " + procHandle.getPid(), procHandle.destroy());
});

一些小语法的变更

具体参见 https://openjdk.org/jeps/213

Try-With-Resources

java 7中引入的try-with-resources 需要定一个新的变量来代表每个资源。

1
2
3
4
try (Resource r1 = resource1;
Resource r2 = resource2) {
...
}

在java 9 中如果资源变量是final或者表现成final(An “effectively final” variable is one whose value is never changed after it is initialized.)的,就可以在try-with-resources中直接使用这个变量。

1
2
3
4
5
6
7
8
9
10
// A final resource
final Resource resource1 = new Resource("resource1");
// An effectively final resource
Resource resource2 = new Resource("resource2");

// 多资源用分号隔开
// New and improved try-with-resources statement in Java SE 9
try (resource1; resource2) {
...
}

棱形运算符<>

棱形运算符是java 7中引入的,可以让带吗更易读。但是他不能用于匿名内部类。

1
2
3
4
5
6
7
8
9
FooClass<Integer> fc = new FooClass<>(1) { // anonymous inner class
};

FooClass<? extends Integer> fc0 = new FooClass<>(1) {
// anonymous inner class
};

FooClass<?> fc1 = new FooClass<>(1) { // anonymous inner class
};

接口私有方法

java 9 允许接口中使用私有方法(目的是为了拆分冗长的默认方法 default methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface InterfaceWithPrivateMethods {
// 可以是静态
private static String staticPrivate() {
return "static private";
}
// 也可以不是静态
private String instancePrivate() {
return "instance private";
}

default void check() {
String result = staticPrivate();
InterfaceWithPrivateMethods pvt = new InterfaceWithPrivateMethods() {
// anonymous class
};
result = pvt.instancePrivate();
}
}}

String 存储结构变更

JEP 254
String 使用了更节省空间的内部存储结构。

java 8 String

1
private final char value[];

java 9 String

1
2
@Stable
private final byte[] value;

集合增强

JEP 269
Java 9 List,Set 和 Map 接口中,新的静态工厂方法可以创建这些集合的不可变实例(Immutable)。提供了更简洁的方式创建集合。

旧的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Tester {
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);
}
}

输出

1
2
3
[A, B, C]
[A, B, C]
{A=Apple, B=Boy, C=Cat}

Java 9 中创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.AbstractMap;
import java.util.Map;
import java.util.Set;

public class Tester {

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);
}
}

输出

1
2
3
4
[A, B, C]
[A, B, C]
{A=Apple, B=Boy, C=Cat}
{A=Apple, B=Boy, C=Cat}

Stream API 增强

Java 9 改进的 Stream API 添加了一些便利的方法,使流处理更容易,并使用收集器编写复杂的查询。
Java 9 为 Stream 新增了几个方法:dropWhiletakeWhileofNullable,为 iterate 方法新增了一个重载方法。

takeWhile

takeWhile() 方法使用一个断言作为参数,返回给定 Stream 的子集直到断言语句第一次返回 false。

1
2
3
4
5
6
7
8
9
10
import java.util.stream.Stream;

public class Tester {
public static void main(String[] args) {
Stream.of("a","b","c","","e","f").takeWhile(s->!s.isEmpty())
.forEach(System.out::print);
}
}

//abc 遇到满足条件的直接中断流, 注意与filter的区别

dropWhile

dropWhile 方法和 takeWhile 作用相反的,使用一个断言作为参数,直到断言语句第一次返回 false 才返回给定 Stream 的子集。

1
2
3
4
5
6
7
8
9
10
import java.util.stream.Stream;

public class Tester {
public static void main(String[] args) {
Stream.of("a","b","c","","e","f").dropWhile(s-> !s.isEmpty())
.forEach(System.out::print);
}
}

// ef 包含第一个不满足的元素

ofNullable

1
static <T> Stream<T> ofNullable(T t)

ofNullable 方法可以预防 NullPointerException 异常, 可以通过检查流来避免 null 值。
如果指定元素为非 null,则获取一个元素并生成单个元素流,元素为 null 则返回一个空流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.stream.Stream;

public class Tester {
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

iterate 方法

1
static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)

方法允许使用初始种子值的迭代来创建一个顺序的(可能是无限)流 。 当指定的 hasNext 的 predicate 返回 false 时,迭代停止。

1
2
3
4
5
6
7
8
9
java.util.stream.IntStream;

public class Tester {
public static void main(String[] args) {
IntStream.iterate(3, x -> x < 10, x -> x+ 3).forEach(System.out::println);
}
}

// 3 6 9

@Deprecated

注解 @Deprecated 可以标记 Java API 状态,可以是以下几种:

  • 使用它存在风险,可能导致错误
  • 可能在未来版本中不兼容
  • 可能在未来版本中删除
  • 一个更好和更高效的方案已经取代它

Java 9 中注解增加了两个新元素:sinceforRemoval

  • (String) since: 指定已注解的API元素已被弃用的版本。
  • (boolean) forRemoval: 表示是否在将来的版本中删除带注释的元素。默认值为false。

使用参见 System文档

改进Optional类

Optional 类在 Java 8 中引入,Optional 类的引入很好的解决空指针异常。在 Java 9 中, 添加了三个方法来改进它的功能:

  • stream()
  • ifPresentOrElse()
  • or()

stream()

stream 方法的作用就是将 Optional 转为一个 Stream,如果该 Optional 中包含值,那么就返回包含这个值的 Stream,否则返回一个空的 Stream(Stream.empty())。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Tester {
public static void main(String[] args) {
List<Optional<String>> list = Arrays.asList (
Optional.empty(),
Optional.of("A"),
Optional.empty(),
Optional.of("B"));

//filter the list based to print non-empty values

//if optional is non-empty, get the value in stream, otherwise return empty
List<String> filteredList = list.stream()
.flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
.collect(Collectors.toList());

//Optional::stream method will return a stream of either one
//or zero element if data is present or not.
List<String> filteredListJava9 = list.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());

System.out.println(filteredList);
System.out.println(filteredListJava9);
}
}

//[A, B]
//[A, B]

多分辨率图像 API

java.awt.image 包下新增了支持多分辨率图片的API,用于支持多分辨率的图片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.awt.Image;
import java.awt.image.MultiResolutionImage;
import java.awt.image.BaseMultiResolutionImage;

import javax.imageio.ImageIO;

public class Tester {
public static void main(String[] args) throws IOException, MalformedURLException {

List<String> imgUrls = List.of("https://www.aaa.com/1.png",
"https://www.aaa.com/2.png",
"https://www.bbb.com/3.png");

List<Image> images = new ArrayList<Image>();

for (String url : imgUrls) {
images.add(ImageIO.read(new URL(url)));
}

// 读取所有图片
MultiResolutionImage multiResolutionImage =
new BaseMultiResolutionImage(images.toArray(new Image[0]));

// 获取图片的所有分辨率
List<Image> variants = multiResolutionImage.getResolutionVariants();

System.out.println("Total number of images: " + variants.size());

for (Image img : variants) {
System.out.println(img);
}

// 根据不同尺寸获取对应的图像分辨率
Image variant1 = multiResolutionImage.getResolutionVariant(156, 45);
System.out.printf("\nImage for destination[%d,%d]: [%d,%d]",
156, 45, variant1.getWidth(null), variant1.getHeight(null));

Image variant2 = multiResolutionImage.getResolutionVariant(311, 89);
System.out.printf("\nImage for destination[%d,%d]: [%d,%d]", 311, 89,
variant2.getWidth(null), variant2.getHeight(null));

Image variant3 = multiResolutionImage.getResolutionVariant(622, 178);
System.out.printf("\nImage for destination[%d,%d]: [%d,%d]", 622, 178,
variant3.getWidth(null), variant3.getHeight(null));

Image variant4 = multiResolutionImage.getResolutionVariant(300, 300);
System.out.printf("\nImage for destination[%d,%d]: [%d,%d]", 300, 300,
variant4.getWidth(null), variant4.getHeight(null));
}
}

HTTP/2 Client (Incubator)

JEP 110

A long-awaited replacement of the old HttpURLConnection.

It should support both HTTP/2 protocol and WebSocket handshake, with performance that should be comparable with the Apache HttpClient, Netty and Jetty.

Let have a look at this new functionality by creating and sending a simple HTTP request.

1
2
3
4
5
6
7
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/get"))
.GET()
.build();

HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandler.asString());

The HTTP Client JEP is being moved to Incubator module, so it is no longer available in the package java.net.http and instead is available under jdk.incubator.http.

全新的 HTTP 客户端 API 可以从 jdk.incubator.httpclient 模块中获取。因为在默认情况下,这个模块是不能根据 classpath 获取的,需要使用 add modules 命令选项配置这个模块,将这个模块添加到 classpath中。

CompletableFuture API

JEP 266

Java 8 引入了 CompletableFuture<T> 类,支持 future 完成时触发一些依赖的函数和动作。
Java 9 引入了一些 CompletableFuture 的改进 (支持延迟和超时,抽象出一个工具类和一些实用方法):

  • Time-based enhancements are added that enable a future to complete with a value or exceptionally after a certain duration, see methods orTimeout and completeTimeout. In addition, a complementary Executor returned by the static methods named delayedExecutor allow a task to execute after a certain duration. This may be combined with Executor receiving methods on CompletableFuture to support operations with time-delays.
  • Subclass enhancements are added making it easier to extend from CompletableFuture, such as to provide a subclass that supports an alternative default executor.

代码方面,API提供了八种新方法和五种新的静态方法。根据Open JDK描述,为了实现这样的添加,大约2400个代码行中的1500处被更改。

支持超时和延迟

1
public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)

上面的方法已用于指定任务是否在一定时间内未完成,程序会停止并抛出TimeoutException。

1
public CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)

上面的方法用提供的value完成了CompletableFuture 。如果没有,它将在给定的超时之前完成。

改进了对子类的支持

1
public Executor defaultExecutor()

上面的方法返回用于不显示执行程序的异步方法的默认 执行程序 。在子类中可以重写它,以返回执行器以提供至少一个独立 线程。

1
public <U> CompletableFuture<U> newIncompleteFuture()

上面的方法返回要由CompletionStage 方法返回的规范的新的不完整的 CompletableFuture

新工厂方法

1
public static <U> CompletableFuture<U> completedFuture(U value)

上面的工厂方法返回一个新的CompletableFuture ,它已经使用提供的值完成了。

1
public static <U> CompletionStage<U> completedStage(U value)

上面的工厂方法返回一个新的CompletionStage,该新的CompletionStage用提供的值之前完成 ,并且仅与接口CompletionStage中可用的那些方法兼容。