为什么要了解Java 17?
之所以关心Java 17是因为和Java 8、Java 11一样它是下一个LTS版本。
Spring的官宣
kafka 3.0版本之后也会弃用java 8,升级已经是一个趋势,未来更多框架和中间件会弃用java 8,作为开发人员也不能停止脚步
使用前的准备
-
IDE升级
检查 IDE 是否支持 java 17,例如 IDEA 在 file -> project structure -> project -> language level,查看是否支持17,如果最高不支持则需要升级IDEA。
-
依赖项的升级
-
java 11
java 11删除了这些原本在jdk中的包:
1.javaFX:JavaFX是取代Swing和AMT,用于开发富GUI(图形用户界面)应用的框架
2.删除java EE和CORBA模块:SE中删除java EE相关的包是因为这些包已经由java EE提供,而且由于oracle的政策,一些包的命名空间也改变了,例如JAXB包下的 javax.xml.bind.* 更改为 jakarta.xml.bind.* ,下图列举了包名的改动,如果项目使用了这些包,需要在代码和pom.xml中更改相应包名
...
-
java 14
删除了CMS GC,对于老项目或针对CMS专门调优过的项目,建议升级后使用G1 GC
-
java 16
java 16对jdk内部的很多api做了强封装,默认情况下不可访问(可以通过选项 --illegal-access 更改这个行为,但官方不建议),这个主要影响一些工具,比如lombok,而lombok在java 16发布后不久更新了版本解决这个问题。
-
java 17
1.删除applet API:Applet 是一种 Java 程序。它一般运行在支持 Java 的 Web 浏览器内。因为它有完整的 Java API支持,所以Applet 是一个全功能的 Java 应用程序
2.AOT和JIT被删除:AOT 编译是在程序运行之前,便将字节码转换为机器码。JIT是在程序的运行过程中,将字节码转换为可在硬件上直接运行的机器码。
8到17 (累积)语法新特性
-
新增类型推断
import java.util.ArrayList;
import java.util.List;
public class Var {
// var a = "a";
void get() {
var b = "b";
// 泛型为Object
var list = List.of("a", 1);
var list2 = new ArrayList<>();
list2.add("a");
list2.add(1);
// 无法推断匿名类
Runnable runnable = () -> System.out.println("interface var");
// var runnable2 = () -> System.out.println("interface var");
}
}
-
Stream 增强
import java.util.*;
import java.util.stream.Stream;
public class StreamJava {
// max 10
Map<String, Integer> map = Map.of("a", 1, "b", 2, "c", 3);
// List<String> list = List.of("a", 1);
// toList()
List<Integer> list = Stream.of(1, 2, 3).toList();
// 多元素替换一个元素
public static void main(String[] args) {
Stream.of(1, 2, 3)
//.flatMap(num -> Stream.of(num + num, num * num, " "))
.mapMulti((num, downstream) -> {
downstream.accept(num + num);
downstream.accept(num * num);
downstream.accept(" ");
})
.forEach(System.out::println);
}
// optional
long count = Stream.of(
Optional.of(1),
Optional.empty(),
Optional.of(2)).flatMap(Optional::stream).count();
}
-
instance of 增强
public class InstanceOf {
void get() {
Object obj = "string value";
//传统写法
if (obj instanceof String) {
String str = (String) obj;
if (str.contains("val")) {
}
}
//新写法
if (obj instanceof String s) {
}
//还可以做其他操作
if (obj instanceof String s && s.contains("val")) {
}
}
}
-
接口支持 private 方法
public interface Interface {
private void get() {
System.out.println();
}
}
-
新增封闭类
public sealed class Sealed permits A {
}
final class A extends Sealed {} // OK
// final class B extends Sealed {} // error
-
switch 语句增强
public class Switch {
public void switchNowTest() {
var c = 'B';
var res = switch (c) {
case 'A' -> "优秀";
case 'B' -> "良好";
case 'C' -> "及格";
default -> "未知等级";
};
}
public void formatTest() {
Object o = 100;
String formatted = switch (o) {
//相当于 if (o instanceof Integer && (int)o > 10)
case Integer i && i > 10 -> String.format("a large Integer %d", i);
case Integer i -> String.format("a small Integer %d", i);
case Long l -> String.format("a Long %d", l);
default -> o.toString();
};
System.out.println(formatted);
}
}
-
try 语句增强
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class Try {
void get() {
var reader = new InputStreamReader(System.in);
var writer = new OutputStreamWriter(System.out);
try (reader; writer) {
//reader是final的,不可再被赋值
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
新增文本块
public class Text {
// String a = """abc
// """;
static String text = """
abc
b
c
def
""";
static String text2 = """
abc
b
c
def
ghi
""";
static String text3 = """
{
"name": "abc",
"age": 123
/
//
\n
}
""";
public static void main(String[] args) {
System.out.println(Text.text2);
}
}
-
新增 record 基本引用类型
public record Record(String name, Integer age) {
// public int a;
// Record(int a) {
// this.a = a;
// }
// public static String b = "b";
public Record {
if (age < 0) {
throw new RuntimeException("年龄不正确");
}
if (age > 100) {
age = 100;
}
}
public static void main(String[] args) {
Record a = new Record("a", 200);
System.out.println(a);
System.out.println(a.age);
// System.out.println(Record.b);
}
}
-
新增 http client 工具
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class Http {
HttpClient client;
public void init() {
client = HttpClient.newBuilder().connectTimeout(Duration.ofMillis(20000L)).build();
}
public void reqTest() throws IOException, InterruptedException {
var request = HttpRequest.newBuilder(URI.create("https://baidu.com/")).build();
/**
* 没有指定协议默认是 GET
*/
String body = client.send(request, HttpResponse.BodyHandlers.ofString()).body();
System.out.println(body);
}
public void getTest() {
var request = HttpRequest.newBuilder()
.uri(URI.create("https://baidu.com/"))
.header("Cookie", cookie)
.timeout(Duration.ofSeconds(10000L))
.GET()
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.whenCompleteAsync((res, exp) -> {
System.out.println(res.body());
}).join();
}
public void postTest() {
var requestBody = "{'key':'val'}";
var request = HttpRequest.newBuilder()
.uri(URI.create("http://baidu.com/"))
.header("Contend-Type","application/json")
.timeout(Duration.ofSeconds(10000L))
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.whenCompleteAsync((res, exp) -> {
System.out.println(res.body());
}).join();
}
public void Http2Test() throws URISyntaxException {
HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NEVER)
.version(HttpClient.Version.HTTP_2)
.build()
.sendAsync(HttpRequest.newBuilder()
.uri(new URI("https://baidu.com/"))
.GET()
.build(),
HttpResponse.BodyHandlers.ofString())
.whenComplete((resp, t) -> {
if (t != null) {
t.printStackTrace();
} else {
System.out.println(resp.version());
System.out.println(resp.statusCode());
}
}).join();
}
}
其他重要更新
-
恢复始终严格的浮点语义(曾需使用strictfp关键字)
过于严格的浮点计算在当初流行的 x86 架构和 x87 浮点协议处理器上运行,需要大量的额外的指令开销,所以java1.2之后去掉严格浮点,使用strictfp关键字
但是非严格浮点数计算会导致不同平台的计算结果可能不一致
-
使用新的 macOS 渲染库,支持 macOS/AArch64 架构
···
-
指定上下文的反序列化过滤器
反序列化危险的一个原因是,有时候我们不好验证将要进行反序列化的内容是否存在风险,而传入的数据流可以自由引用对象,很有可能这个数据流就是攻击者精心构造的恶意代码。
public class JEP415 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Dog dog = new Dog("哈士奇");
dog.setPoc(new Poc());
// 序列化 - 对象转字节数组
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);) {
objectOutputStream.writeObject(dog);
}
byte[] bytes = byteArrayOutputStream.toByteArray();
// 反序列化 - 字节数组转对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
// 允许 com.wdbyte.java17.Dog 类,允许 java.base 中的所有类,拒绝其他任何类
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
"com.wdbyte.java17.Dog;java.base/*;!*");
objectInputStream.setObjectInputFilter(filter);
Object object = objectInputStream.readObject();
System.out.println(object.toString());
}
}
class Dog implements Serializable {
private String name;
private Poc poc;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" + "name='" + name + '\'' + '}';
}
// get...set...
}
class Poc implements Serializable{
}
GC优化 - G1收集器
-
简介
G1收集器最早出现在Java 7中,但需要手动开启,从Java 9开始将G1收集器作为默认垃圾收集器,以取代CMS收集器。
G1垃圾收集器可以给你设定一个你希望Stop the world停顿时间,G1会根据这个时间尽量满足你。
-
内存模型
在G1垃圾收集器中,堆的划分不再是物理形式,而是以逻辑的形式进行划分
-
GC过程
-
Minor GC
扫描、处理跨Region引用,收集至CSet,复制清除、处理引用 -
Mixed GC
当堆空间的占用率达到一定阈值后会触发Mixed GC(默认45%,由参数决定)。Mixed GC会依赖全局并发标记统计后的Region数据
初始标记(STW)、并发标记、最终标记(STW)、清理(STW) -
Full GC
特殊场景会发生full GC
-
性能
性能测试