jdk9新特性

摘抄自:Java 9 新特性介绍 | 未读代码 (wdbyte.com)

Java 9 中最大的亮点是 Java 平台模块化的引入,以及模块化 JDK。但是 Java 9 还有很多其他新功能,这篇文字会将重点介绍开发人员特别感兴趣的几种功能。

1. 模块化

Java 9 中的模块化是对 Java 的一次重大改进。但是模块化并不是最近才提出来的,我们经常使用的 maven 构建工具,就是典型的模块化构建工具。模块化不仅让模块命名清晰,写出高内聚低耦合的代码,更可以方便处理模块之间的调用关系。

在 Oracle 官方中为 Java 9 中的模块系统的定义如下:

the module, which is a named, self-describing collection of code and data. This module system.

直白翻译:模块是一个命名的,自我描述的代码和数据的集合。

Java 9 不仅支持了模块化开发,更是直接把 JDK 自身进行了模块化处理。JDK 自身的模块化可以带来很多好处,比如:

  • 方便管理,越来越大的 JDK 在模块化下结构变得更加清晰。
  • 模块化 JDK 和 JRE 运行时镜像可以提高性能、安全性、维护性。
  • 可以定制 JRE,使用更小的运行时镜像,比如网络应用不需要 swing 库,可以在打包时选择不用,减少性能消耗。
  • 清晰明了的模块调用关系,避免调用不当出来的各种问题。

上面提到了 JDK 自身的模块化,我们通过浏览 JDK 9 的目录结构也可以发现一些变化。

当然,这篇文章主要介绍 Java 9 的新特性,而模块化是一个巨大改变,结合示例介绍下来篇幅会比较长,这里就不占用太多篇幅了。

2. 集合工厂方法

在 Java 9 中为集合的创建增加了静态工厂创建方式,也就是 of 方法,通过静态工厂 of 方法创建的集合是只读集合,里面的对象不可改变。并在不能存在 null 值,对于 set 和 map 集合,也不能存在 key 值重复。这样不仅线程安全,而且消耗的内存也更小

下面是三种集合通过静态工厂创建的方式。

1
2
3
4
5
6
7
8
9
10
11
12
// 工厂方法创建集合
List<String> stringList = List.of("a", "b", "c", "d");
Set<String> stringSet = Set.of("a", "b", "c", "d");
Map<String, Integer> stringIntegerMap = Map.of("key1", 1, "key2", 2, "key3", 3);
Map<String, Integer> stringIntegerMap2 = Map.ofEntries(Map.entry("key1", 1), Map.entry("key2", 2));

// 集合输出
System.out.println(stringList);
System.out.println(stringSet);
System.out.println(stringIntegerMap);
System.out.println(stringIntegerMap2);

这种只读集合在 Java 9 之前创建是通过 Collections.unmodifiableList 修改集合操作权限实现的。

1
2
3
4
5
List<String> arrayList = new ArrayList<>();
arrayList.add("达西");
arrayList.add("程序猿阿朗");
// 设置为只读集合
arrayList = Collections.unmodifiableList(arrayList);

静态工厂 of 方法创建的集合还有一个特性,就是工厂内部会自由复用已有实例或者创建新的实例,所以应该避免对 of 创建的集合进行判等或者 haseCode 比较等操作。

像下面这样,创建两个 List,你会发现两个 List 的 hashCode 是一样的。

1
2
3
4
5
6
7
8
// 工厂可以自由创建新的实例或者复用现有实例,所以 使用 of 创建的集合,避免 == 或者 hashCode 判断操作
List<String> stringList = List.of("a", "b", "c", "d");
List<String> stringList2 = List.of("a", "b", "c", "d");
System.out.println(stringList.hashCode());
System.out.println(stringList2.hashCode());
// 输出结果
// 3910595
// 3910596

这也是使用 of 方法创建集合的优势之一,消耗更少的系统资源。这一点也体现在 of 创建的集合的数据结构实现上,有兴趣的同学可以自行研究下。

3. Stream API

在 Java 9 中,又对 Stream 进行了增强,主要增加了 4 个新的操作方法:_dropWhile,takeWhile,ofNullable,iterate_。

  1. takeWhile: 从头开始筛选,遇到不满足的就结束了。

    1
    2
    3
    4
    5
    6
    7
    8
    // takeWhile ,从头开始筛选,遇到不满足的就结束了
    List<Integer> list1 = List.of(1, 2, 3, 4, 5);
    List<Integer> listResult = list1.stream().takeWhile(x -> x < 3).collect(Collectors.toList());
    System.out.println(listResult);
    // takeWhile ,从头开始筛选,遇到不满足的就结束
    List<Integer> list2 = List.of(1, 2, 3, 4, 3, 0);
    List<Integer> listResult2 = list2.stream().takeWhile(x -> x < 3).collect(Collectors.toList());
    System.out.println(listResult2);

    输出结果。

    1
    2
    [1, 2]
    [1, 2]
  2. dropWhile: 从头开始删除,遇到不满足的就结束了。

    1
    2
    3
    4
    5
    6
    7
    8
    // dropWhile ,从头开始删除,遇到不满足的就结束了
    List<Integer> list1 = List.of(1, 2, 3, 4, 5);
    List<Integer> listResult = list1.stream().dropWhile(x -> x < 3).collect(Collectors.toList());
    System.out.println(listResult);
    // dropWhile ,从头开始删除,遇到不满足的就结束
    List<Integer> list2 = List.of(1, 2, 3, 4, 3, 0);
    List<Integer> listResult2 = list2.stream().dropWhile(x -> x < 3).collect(Collectors.toList());
    System.out.println(listResult2);

    输出结果。

    1
    2
    [3, 4, 5]
    [3, 4, 3, 0]
  3. ofNullable: 创建支持全 null 的 Stream.

    1
    2
    3
    4
    5
    6
    7
    Stream<Integer> stream = Stream.of(1, 2, null);
    stream.forEach(System.out::print);
    System.out.println();
    // 空指针异常
    // stream = Stream.of(null);
    stream = Stream.ofNullable(null);
    stream.forEach(System.out::print);

    输出结果。 12null

  4. iterate: 可以重载迭代器。

    1
    IntStream.iterate(0, x -> x < 10, x -> x + 1).forEach(System.out::print);

    输出结果。 0123456789

在 Stream 增强之外,还增强了 Optional ,Optional 增加了可以转换成 Stream 的方法。

1
2
Stream<Integer> s = Optional.of(1).stream();
s.forEach(System.out::print);

4. 接口私有方法

在 Java 8 中增加了默认方法,在 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
27
28
29
30
31
32
33
34
35
/**
* @author 达西 - 公众号:程序猿阿朗
*/
public class Jdk9Interface {
public static void main(String[] args) {
ChinaPeople chinaPeople = new ChinaPeople();
chinaPeople.sleep();
chinaPeople.eat();
chinaPeople.doXxx();
}

}

class ChinaPeople implements People {
@Override
public void sleep() {
System.out.println("躺着睡");
}
}

interface People {
void sleep();

default void eat() {
drink();
}

default void doXxx() {
drink();
}

private void drink() {
System.out.println("喝水");
}
}

例子中的接口 people 中的 eat() 和 doXxx() 默认行为一致,使用私有方法可以方便的抽取一个方法出来。

输出结果。

1
2
3
躺着睡
喝水
喝水

5. HTTP / 2 Client

Java 9 内置了新的 HTTP/2 客户端,请求更加方便。

随便访问一个不存在的网页。

1
2
3
4
5
6
HttpClient client = HttpClient.newHttpClient();
URI uri = URI.create("http://www.tianqiapi.com/api/xxx");
HttpRequest req = HttpRequest.newBuilder(uri).header("User-Agent", "Java").GET().build();
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandler.asString());
String body = resp.body();
System.out.println(body);

输出得到的结果,这里是这个网站的报错信息。

1
There is no method xxxAction in ApiController

可能你运行的时候会报找不到 httpClient 模块之类的问题,这时候需要你在你项目代码目录添加 httpClient 模块 才能解决,添加方式看下面的图。

Java 9 导入导出模块

export 写自己的包路径,requires 写引入的模块名。

6. Java REPL - JShell

交互式的编程环境在其他语言如 Python 上早就有了,而 Java 上的交互式语言只到 Java 9 才出现。交互式的编程可以让开发者在输入代码的时候就获取到程序的运行结果,而不用像之前一样新建文件、创建类、导包、测试一系列流程。

7. 其他更新

Java 9 中增加或者优化的功能远不止这些,上面只是列举了常用的一些新特性,更多的新特性如:

  • 不能使用下划线 _ 作为变量名,因为它是一个关键字。
  • Javadoc 支持 HTML5 并且支持搜索功能。
  • Nashorn 引擎升级,更好的支持 Javascript.
  • String 存储结构变更从 char -> byte.
  • ………

新特性很多,感兴趣的可以自己了解下。