从JDK8到JDK17
JDK9
模块系统
JDK9引入了一个新的特性叫做JPMS(Java Platform Module System),也可以叫做Project Jigsaw。模块化的本质就是将一个大型的项目拆分成为一个一个的模块,每个模块都是独立的单元,并且不同的模块之间可以互相引用和调用。
在module中会有元数据来描述该模块的信息和该模块与其他模块之间的关系。这些模块组合起来,构成了最后的运行程序。
class
是字段和方法的集合,package
是class
的集合,而module
是package
的集合
没有怎么看明白。。。有时间再补
多版本兼容jar
接口私有方法
可以在接口中写私有方法了,私有方法不需要覆写,别的实现类也无法调用。个人感觉主要是给默认方法使用的,避免重复代码。
快速创建只读集合
Collection
下的各个类均可以使用of
方法快速创建只读集合。利用该方法创建出来的集合,不能进行增、删、改操作,均会抛出异常。
示例:
Set<String> set = Set.of("a", "b", "c");
Map<String, String> map = Map.of("k1", "v1", "k2", "v2", "k3", "v3");
List<String> list = List.of("A", "B", "C");
String类底层数据结构变更
String、StringBuffer、StringBuilder类,
由
final char[] value
==>final byte[] value
限制单独使用 _ 特殊字符
在JDK8
之前可以使用_
单独的下划线作为标识符。
String _ = "Hello";
System.out.println(_);
输出:
Hello
JDK9中则编译不通过。
try-with-resource改进
// 可以在try外进行初始化,在括号内引用,即可实现资源自动关闭
InputStreamReader reader = new InputStreamReader(System.in);
OutputStreamWriter writer = new OutputStreamWriter(System.out);
// 多资源用分号隔开
try (reader; writer) {
// ...
} catch (IOException e) {
// ...
}
Stream API 增强
takeWhile()
takeWhile
可以用于从 Stream
中获取一部分数据,接受一个 Predicate
来进行选择,在有序的 Stream
中,takeWhile
返回从头开始的尽可能多的元素。
List<Integer> list = Arrays.asList(45,43,76,87,42,77,90,73,67,88);
list.stream().takeWhile(x -> x < 80 ).forEach(System.out::println);
返回结果:
45
43
76
从返回结果可以看出,takeWhile
将会按照list
集合有序的从45开始到第一个不符合条件为止的所有结果。
看起来takeWhile
和filter
功能很类似,但还是有一些区别的。
Stream.of(1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1)
.filter(i -> i < 4 )
.forEach(System.out::print);
输出:
123321
Stream.of(1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1)
.takeWhile(i -> i < 4 )
.forEach(System.out::print);
输出:
123
由此可以看出
filter
将从流中删除不满足条件的所有项目,即允许所有符合条件的元素通过。
takeWhile
将在第一次出现不满足条件的项目时中止流(一定要注意这个,否则容易写bug),同样允许符合条件的元素通过,但是一旦遇到第一个不满足条件的,则后续的都不允许通过了,不处理了。
dropWhile()
dropWhile
的方法刚好与takeWhile
想法,返回剩余的元素。
List<Integer> list = Arrays.asList(45,43,76,87,42,77,90,73,67,88);
list.stream().dropWhile((x) -> x < 80 ).forEach(System.out::println);
输出:
87
42
77
90
73
67
88
从返回结果可以看出,dropWhile
方法刚好和takeWhile
方法形成互补,按照list
集合有序的返回从第一个不满足条件元素开始到最后为止的所有结果。
ofNullable()
在JDK8
中 Stream
不能完全为null
,否则会报空指针异常。而在JDK9
中 ofNullable
方法允许创建一个为空的 Stream
。
// JDK8 NullPointerException
// Stream<Object> stream1 = Stream.of(null);
// System.out.println(stream1.count());
// 不报异常 允许这样写
Stream<String> stringStream = Stream.of("AA", "BB", null);
System.out.println(stringStream.count());
// 不报异常 允许这样写
List<String> list = new ArrayList<>();
list.add("A");
list.add(null);
System.out.println(list.stream().count());
// ofNullable()允许值为 null
Stream<Object> stream = Stream.ofNullable(null);
System.out.println(stream.count());
Stream<String> stream2 = Stream.ofNullable("Hello World");
System.out.println(stream2.count());
输出:
3
2
0
1
iterate()重载方法
// JDK8 使用iterate方法,需配合limit截止。
Stream.iterate(1, (x) -> x + 1).limit(10).forEach(System.out::print);
// JDK9 使用iterate的重载方法可以直接使用Predicate来截止。
Stream.iterate(1,(x) -> x <= 10, (x) -> x + 1).forEach(System.out::print);
Optional类优化
Optional
类是在JDK8
中新增的类,主要是为了解决空指针异常。在JDK9
中对这个类进行了改进,主要是新增了三个方法:stream()
,ifPresentOrElse()
和 or()
。
stream()
stream
方法将Optional
转为一个 Stream
,如果Optional
没有值就返回一个 Stream.empty
。
// stream 方法源码
public Stream<T> stream() {
if (!isPresent()) {
return Stream.empty();
} else {
return Stream.of(value);
}
}
示例:
List<String> list = List.of("A", "B", "C", "D", "E", "F");
Optional<List<String>> optional = Optional.of(list);
optional.stream().forEach(System.out::println);
Optional<Object> optional1 = Optional.empty();
System.out.println(optional.stream().count());
输出:
[A, B, C, D, E, F]
1
ifPresentOrElse()
// ifPresentOrElse 方法源码
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
if (value != null) {
action.accept(value);
} else {
emptyAction.run();
}
}
接收两个参数,一个是Consumer
, 另一个是Runnable
,当value
不为null
时,则执行Consumer
的accept
方法,否则执行Runnable
的run
方法,此处并没有使用多线程。
示例:
// 如果optional包含值,执行action.accept方法。
Optional<Integer> optional = Optional.of(1);
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
System.out.println("没有值."));
optional = Optional.empty();
// 如果optional不包含值,执行emptyAction.run方法。
optional.ifPresentOrElse( x -> System.out.println("Value: " + x),() ->
System.out.println("没有值."));
输出:
Value: 1
没有值.
or()
如果Optional
有值,返回 Optional
指定的值,否则返回一个预设的值。
// or 方法源码,注意这里的返回值类型是一个Optional
public Optional<T> or(Supplier<? extends Optional<? extends T>> supplier) {
Objects.requireNonNull(supplier);
if (isPresent()) {
return this;
} else {
@SuppressWarnings("unchecked")
Optional<T> r = (Optional<T>) supplier.get();
return Objects.requireNonNull(r);
}
}
JDK10
局部变量类型推断
JDK10
可以使用var
作为局部变量类型推断标识符,此符号仅适用于局部变量,增强for
循环的索引,以及传统for
循环的本地变量;
它不能使用于方法形式参数,构造函数形式参数,方法返回类型,字段,catch
形式参数或任何其他类型的变量声明。
并行Full GC 的G1
JDK10
通过并行Full GC
,改善G1
的延迟。G1
垃圾收集器在JDK 9
中是默认的。以前的默认值并行收集器中有一个并行的Full GC
。为了尽量减少对使用GC
用户的影响,G1
的Full GC
也应该并行。
G1
垃圾收集器的设计目的是避免Full
收集,但是当集合不能足够快地回收内存时,就会出现完全GC
。目前对G1
的Full GC
的实现使用了单线程标记-清除-压缩算法。JDK10
使用并行化标记-清除-压缩算法,并使用Young
和Mixed
收集器相同的线程数量。线程的数量可以由-XX:ParallelGCThreads
选项来控制,但是这也会影响用Young
和Mixed
收集器的线程数量。
JDK11
从Java8到Java17(四) - 掘金 (juejin.cn)
Lambda表达式中支持var变量
示例:
Arrays.asList("Java", "Python", "Ruby")
.forEach((var s) -> {
System.out.println("Hello, " + s);
});
全新的HTTP client API
这个API
首次出现在JDK9
之中,当时并非一个稳定版本,在JDK11
中正式发布。原来JDK
的HTTP
客户端真的难用,这也给了很多像okhttp
、restTemplate
和feign
这样的第三方库极大的发挥空间,几乎是没人用原生的HTTP
客户端的。但现在不一样了,确实感觉是新时代的API
了。
示例:
// 创建client
HttpClient client = HttpClient.newBuilder()
.version(Version.HTTP_1_1)
.followRedirects(Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(20))
.proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80)))
.authenticator(Authenticator.getDefault())
.build
// 构建request
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://foo.com/"))
.timeout(Duration.ofMinutes(2))
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofFile(Paths.get("file.json")))
.build();
// 同步请求
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
// 异步请求
client.sendAsync(request, BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
新的String方法
repeat
:重复生成字符串isBlank
:不用在引入第三方库就可以实现字符串判空了strip
:去除字符串两边的空格,支持全角和半角,之前的trim只支持半角lines
:能根据一段字符串中的终止符提取出行为单位的流
读写文件
对Files
类增加了writeString
和readString
两个静态方法,可以直接把String
写入文件,或者把整个文件读出为一个String
,这两个方法可以大大简化读取配置文件之类的问题。
Files.writeString(
Path.of("./", "tmp.txt"), // 路径
"hello, jdk11 files api", // 内容
StandardCharsets.UTF_8); // 编码
String s = Files.readString(
Paths.get("./tmp.txt"), // 路径
StandardCharsets.UTF_8); // 编码
ZGC(试验性的功能)
JDK12
JDK12的五大重要新特性 - 知乎 (zhihu.com)
从Java8到Java17(五) - 掘金 (juejin.cn)
String新增方法
indent
:给字符串做缩进,接受一个int
型的输入transform
:接受一个转换函数,实现字符串的转换
示例:
var result = "foo"
.transform(input -> input + " bar")
.transform(String::toUpperCase);
//output: FOO BAR
全新的switch表达式
JDK8
的写法:
switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
System.out.println(6);
break;
case TUESDAY:
System.out.println(7);
break;
case THURSDAY:
case SATURDAY:
System.out.println(8);
break;
case WEDNESDAY:
System.out.println(9);
break;
}
// 假如忘记在每个case中写break,那就都会走到default中
新的写法;
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
}
// 还可以有返回值
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> 9;
};
引入了
->
,这里不是lambda,是会执行后面的语句的意思。
NumberFormat增加了对以紧凑格式格式化数字的支持
NumberFormat
增加了以紧凑格式格式化数字的支持。 紧凑的数字格式是指数字的简短形式或易于理解的形式。 例如,在en_US
语言环境中,根据NumberFormat.Style
指定的样式,可以将1000
格式化为1K
,将1000000
格式化为1M
。
示例:
NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US,NumberFormat.Style.SHORT);
String result = fmt.format(1000); // 输出结果为 1k
JDK13
JDK13的六大重要新特性 - 知乎 (zhihu.com)
从Java8到Java17(六) - 掘金 (juejin.cn)
字符串块(文本块)
旧的:
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>
""";
JDK14
从Java8到Java17(六) - 掘金 (juejin.cn)
instanceof增加模式匹配
旧的:
if (obj instanceof String) {
String s = (String) obj;
...
}
新的:
if (obj instanceof String) {
String s = obj;
...
}
注意点:
- 需要确定的类型判断才能使用
- 作用域的范围需要注意
Record
官方提供的类似lombok
插件的功能,Record
类会生成get/set/hashcode/equals
和构造方法,没有多余的注解和代码。
示例:
public record EmployeeRecord(Long id,
String firstName,
String lastName,
String email,
int age) {
}
这就是一个Record
类,甚至可以不用写class
关键字。不仅可以是一个单独的类文件,也可以是一个成员类
public class EmployeeRecord {
public record User(long id,
String name,
int age) {}
}
还可以出现在方法里:
public class EmployeeRecord {
public void test() {
record Mail (long id, String content){}
Mail mail = new Mail(10, "content");
}
}
注意点:
- 构造函数 --- 默认record类具有全部属性的构造函数,不具备无参构造
- 属性 --- 属性最好在类声明时就写在括号中,如果想在body中单独声明,仅支持static属性,不建议这样
- 不能被继承
JEP 363:删除并发标记扫描(CMS)垃圾回收器
JDK15
JDK 15带来的14个新特性介绍 - 知乎 (zhihu.com)
从Java8到Java17(八) - 掘金 (juejin.cn)
密封类
package com.example.geometry;
// 只允许指定的子类继承
public abstract sealed class Shape
permits com.example.polar.Circle,
com.example.quad.Rectangle,
com.example.quad.simple.Square { ... }
主要作用:
- 让类的作者可以精确的控制子类,保持不被滥用
- 扩展了原有的权限修饰符,限制父类的使用不再一刀切的开放或者私有
- 为未来的模式匹配打好基础,指定有限子类之后使模式匹配的分析更简单
其实第三点才是重点,就是为了switch模式匹配开发的。
注意:
子类声明必须为
final
JDK17
JDK16的新特性 - 腾讯云开发者社区-腾讯云 (tencent.com)
一文总结JDK 17发布的新特性-阿里云开发者社区 (aliyun.com)
标签:String,Stream,JDK17,System,JDK8,println,Optional,out From: https://www.cnblogs.com/strive-for-life/p/16810980.html