Java8 引入 Lambda 表达式和流以后,带来了函数式编程风格。往后的Java 版本发布节奏加快,切换每六个月一个新版本,很多新特性可能不少人都不清楚。尽管如今大部分场景都是Java 8,但了解行业变化也是每个 Java 程序员需要的。本文主要介绍 Java 9 到 17的新增的一些语言特性,涉及一些底层实现的变化会简单列举。
Java9
Java10
Java11
Java12
Java13
Java14
Java15
Java16
Java17
Java9 是Java8后一个比较大的更新,包含新特性比较多
module 是新增的Java代码访问权限级别,每个module可以包含多个package。
通过module-info.java文件来声明该文件夹及其子文件夹为一个模块,exports 关键字可以用来控制模块内哪些包对外暴露。
module store.api{ exports com.dingtalk.store.api; }
使用module后,即使包中的类是public的,如果未通过exports
显式导出其程序包,则外部模块是不可调用的。
如果一个模块想要使用被另一个模块导出的package包中的类,可以用requires
关键字在其module-info.java文件中来导入(读取)目标模块的package包。
module store.service { requires com.dingtalk.store.api; }
Java9 module 和 Maven module 很像,但功能完全不一样,后者是作为依赖构建来方便管理应用代码,而Java Module是在于安全性、访问性控制,通过exports/requires 控制模块内需要暴露和依赖的具体包。
interface DemoI { private void privateMethod() { System.out.println("private"); } }
try-with-resources
语法的 try()
可以包含变量,多个变量用分号隔开。
改进后让语义更加清晰,将资源创建和资源回收的语法拆分。
public void testTryWithResources(String fileName) { FileOutputStream fos = new FileOutputStream(fileName); OutputStreamWriter osw = new OutputStreamWriter(fos); BufferedWriter bw = new BufferedWriter(osw); try (bw;osw;fos) { bw.write("something"); bw.flush(); } /* 对比Java7: try(FileOutputStream fos = new FileOutputStream(fileName); OutputStreamWriter osw = new OutputStreamWriter(fos); BufferedWriter bw = new BufferedWriter(osw);){ bw.write("something"); bw.flush(); } */ }
1、Stream.takeWhile(Predicate):处理流中的数据直到条件 predicate
返回 false 则终止执行。
public void test() { Stream.of(1, 2, 3, 4, 5) .takeWhile(i -> i != 3) .forEach(System.out::println); /* 输出结果: 1 2 */ }
2、Stream.dropWhile(Predicate):与 taskWhile
相反,直到条件 predicate
返回 false 才开始处理其后面流中的数据。
public void test() { Stream.of(1, 2, 3, 4, 5) .dropWhile(i -> i != 3) .forEach(System.out::println); /* 输出结果: 4 5 */ }
3、Stream.iterate: 会循环遍历流中的数据,直到条件返回 false 时停止
方法定义如下:
/** * @param seed 初始化循环变量 * @param hasNext 循环条件 * @param next 下一次循环值 **/ static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
示例:
public void test() { IntStream.iterate(1, x -> x < 10, x -> x + 5).forEach(System.out::println); /* 输出结果: 1 6 */ }
4、Stream.ofNullable:对Stream.of的扩展,创建流时对空入参返回空Stream
REPL,全称 Read Eval Print Loop ,「 交互式解释器 」,一种支持代码所见即所得的即时编译器。
一些简单代码逻辑可以直接通过 Jshell 执行,不需要同故宫 javac 编译了。
在同一个Jar包可以包含多个Java版本的class文件,在不同Jdk环境下使用对应该 jdk 版本的 jar。
(这对算是用户很友好的功能)
新增关键字var
使得 Java 可以像 js 一样,自动推断数据类型。仅限于局部变量,算是一个语法糖,底层还没有变,在编译期推断出变量的实际类型。
var str = "show the var";
其他关于底层的优化不展开了,直接列举
继 Java8 后的又一个 LTS (long-term support)版本,自 Java 11 起,Oracle JDK 将不再免费提供商业用途。
1、String.isBlank():去前后空白字符的字符串判空
2、lines():按行分割字符串,得到一个字符串流
3、repeat():复制字符串
4、strip():去除前后空白字符(区别于 trim() 只能去除半角空格)
允许在lambda表达式中使用 var
list.stream() .map((@NotNull var x) -> x.toUpperCase()));
在 Java 11 中 Http Client API 得到了标准化的支持。且支持 HTTP/1.1 和 HTTP/2 ,也支持 websockets。
HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://www.dingtalk.com")) .build(); HttpClient client = HttpClient.newHttpClient(); // 异步 client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) .thenAccept(System.out::println) .join(); // 同步 HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
关于底层的优化
原先 switch 总需要使用 break 来做到每个 case 间隔离,Java 12 后通过 case L ->
来实现。并且支持了返回值,可进行赋值。
String season = switch (day) { case "march", "april", "may" -> "春天"; case "june", "july", "august" -> "夏天"; case "september", "october", "november" -> "秋天"; case "december", "january", "february" -> "冬天"; default -> { break "unknown"; } };
文本块目前还是预览功能,方便创建多行字符串
以往在代码中声明一个长长的字符串常量,通过需要换行使用 +
连接。
String html ="<html>\n" + " <body>\n" + " <p>Hello, World</p>\n" + " </body>\n" + "</html>\n";
采用文本块的方式,示例:
String html = """ <html> <body> <p>Hello, World</p> </body> </html> """;
目前还是预览功能,作用是在使用 instanceof 判断是所属类型后,自动类型转换。
// java14之前 if (obj instanceof String) { String str = (String) obj; ... } // java14之后 if (obj instanceof String str) { ... }
Java 14 引入了打包工具,命令是 jpackage,使用 jpackage 命令可以把 JAR 包打包成不同操作系统支持的软件格式。
主要的几个平台格式如下:
deb
和 rpm
pkg
和 dmg
msi
和 exe
原先的空指针异常只能直到是第几行报错,无法确定这一行中具体哪个字段。Java 14 中的空指针会清晰地提示出具体字段。
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "test" is null
Record
是新的类型,和枚举类似的受限形式,本质上是一个 final 类。
// 定义 public record Person(String name, String gender) { } // 使用 Person tom = new Person("Tom", "male"); Person june = new Person("June", "female");
这个可以让开发者引入无法被其他地方发现使用,且生命周期有限的类。这对于运行时动态生成类(反射)的使用方式十分有利,可以减少内存占用,应该比较适合框架开发中使用。
Java15中还是预览特性。密封类用来控制允许继承它的子类,不是随便子类想继承就能继承
public sealed interface Animal permits Dog, Cat { //... }
synchronized
同步时的锁膨胀机制中有一个就是偏向锁,这个的引入增加了JVM的复杂度,但是对性能提升程度一般,于是默认禁用了。
ZGC 作为可扩展低延迟垃圾收集器,在Java11引入后,Java15正式投入使用了。
Java 16 在语法上的新特性更新比较多,主要是一些之前版本引入的预览特性正式发布。比如Java14中的打包工具、instanceof、Record类
Java 17 在 2021 年 9 月 14 日正式发布,Java 17 是一个 LTS 版本。
Java 1.2 在中引入了Java对默认浮点语义的更改,牺牲一点浮点精度来提高JVM性能,并引入了strictfp
来手动启用严格浮点语义。Java17恢复了严格浮点语义作为默认语义。
Applet 是使用 Java 编写的可以嵌入到 HTML 中的小应用程序,嵌入方式是通过普通的 HTML 标记语法,由于早已过时,几乎没有场景在使用了,在Java17中彻底删除。
预览特性。和instanceof一样,switch 增加了类型匹配自动转换功能
static String test(Object o) { return switch (o) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> o.toString(); }; }
其他一些特性简单列举如下:
可以看到Java快速迭代的版本参考了其他语言、类库的语法设计,带来不少语法糖。随着每 6 个月一次的快速发布周期,更多的变化被引入,保持关注 Java 平台的变化就更加重要了。目前 Java 中的 Lambda 对于函数式的支持还比较单薄,期待后续更新能有这方面的增强。