Java新特性

该笔记根据学习b站视频制作


Java 9

语法变化

在接口中可以写入私有方法,但是只能在接口内部被default调

image-20240314152911495

从Java 9开始,变量不允许使用_来命名

image-20240314153316044

注解变化

@Deprecated注解的变化

该注解用于标识废弃的内容,在jdk9中新增了两个内容

  • String since() default “” : 标识是从哪个版本开始废弃的
  • boolean forRemoval() default false : 标识该废弃的内容将来是否会移除,true表示会移除

image-20240316134132449

字符串底层变化

在jdk9之前,String底层是用char数组存储的,而在jdk9,改为了用byte数组存储

image-20240316134530731

String中增加了两个成员变量

  • COMPACT_STRINGS: 判断是否压缩,默认是true,如果为false不压缩,就使用UTF-16编码
  • coder: 用来区分使用的字符编码,分别为LATIN1(0)和UTF16(1)

当Spring字符串存储英文时,一个英文字符会占用一个字节

当Spring字符串存储中文时,一个中文汉字或占用两个字节,一个字节存储低八位(unicode二进制),一个字节存储高八位

当Spring字符串存储中英文混合时,一个英文字符会占用两个字节


模块化

模块是Java9中新增的一个组件,可以简单理解为是package的上级容器,是多个package的集合,一个jar可以有多个module,一个module可以有多个package。从代码结构上看,jar > module > package > class, interface


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


模块化的优点

  • 精简jvm加载的class类,提升加载速度
  • 对包更精细的控制,提高安全

使用案例

创建两个模块,并创建相关的测试类

image-20240317113620063

Cat类

1
2
3
4
5
public class Cat {
public void eat() {
System.out.println("猫喜欢吃鱼");
}
}

Dog类

1
2
3
4
5
public class Dog {
public void eat() {
System.out.println("狗喜欢吃骨头");
}
}

测试类

1
2
3
4
5
public class Test {
public static void main(String[] args) {

}
}

这时我们尝试在测试类中创建Cat类对象和Dog类对象,会发现找不着对象

image-20240317113825805 image-20240317113846566

因为每个模块对外都是封闭的,我们需要把Cat类和Dog类所在的包对外暴露,在Test模块导入develop模块,才能使用Cat类和Dog类

在devlop模块中创建module-info.java文件

image-20240317114120958

在module-info.java文件暴露Cat类和Dog类所在的包

1
2
3
4
module develop {
exports zheng.dev1; // 导出包
opens zheng.dev2; // 导出包,只能通过反射的方式访问
}

在test模块也创建module-info.java文件,然后导入develop模块

1
2
3
module test {
requires develop; // 导入模块
}

再次进行测试

image-20240317115134033

通过反射创建Dog类对象,再测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

Cat cat = new Cat();
cat.eat();
System.out.println("-----------------");

Class<?> aClass = Class.forName("zheng.dev2.Dog");

// 获取dog对象并打印
Object dog = aClass.getDeclaredConstructor().newInstance();
System.out.println(dog);
System.out.println("-----------------");

// 获取dot对象的eat方法
Method eat = aClass.getDeclaredMethod("eat");
System.out.println(eat);
System.out.println("-----------------");

// 打印dog的eat方法
String result = (String) eat.invoke(dog);
}
}

image-20240317120323688

Java 10

局部类型变量推断

定义变量时可以使用var来定义,再通过=右边的数据来推断数据类型

1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
var Name = "zheng";
var age = 18;
var Id = 11111111L;
var new Obje = new Object();
}
}

var的使用是有限制的,仅适用于局部变量,增强for循环的索引,以及普通for循环的本地变量,他不能用于方法的形参,构造方法形参,方法返回类型等



Java 11

新增:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {

public static void main(String[] args) {
// 以前去除字符串前后的空格使用trim方法,现在可以使用strip方法
char c = '\u2000'; // Unicode空白字符
String str = c + "abc" + c;
System.out.println(str.trim()); // 无法去除
System.out.println(str.strip()); // 成功去除空格
System.out.println("-----------------");

// isBlank()方法,是否为空
String name = " ";
System.out.println(name.isBlank());
System.out.println("-----------------");

// repeat()方法,重复输出
String abc = "zheng";
System.out.println(abc.repeat(5)); // 参数5表示重复输出5次

}
}
image-20240317134830389

Java 12

升级了Swatch语法

当多个case条件所生产的结果相同时,可以缩写

1
2
3
4
5
6
7
8
9
10
11
12
13
// 判断当前月份所属季节
public class Test() {
public static void main(String[] args) {
int month = 3;
switch(month) {
case 3,4,5 -> System.out.println("spring");
case 6,7,8 -> System.out.println("summer");
case 9,10,11 -> System.out.println("autumn");
case 12,1,2 -> System.out.println("winter");
default -> System.out.println("请输入正确的月份");
}
}
}

注意: 这个功能在Java 12中是预览功能,在Java 14才正式上线,如果要在Java 12和Java 13中使用要执行下面命令

1
2
编译: javac --enable-preview -source 12 Test.Java
运行: java --enable-preview Test


Java 13

升级了Swatch语法,Swatch语句可以有返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 判断当前月份所属季节
public class Test() {
public static void main(String[] args) {
int month = 3;
String result = switch(month) {
case 3,4,5 -> "spring";
case 6,7,8 -> "summer";
case 9,10,11 -> "autumn";
case 12,1,2 -> "winter";
default -> "请输入正确的月份";
};
System.out.println(result);
}
}

文本块变化

以前输出多行内容时要换行

1
String s1 = "床前明月光\n疑是地上霜\n举头望明月\n低头思故乡";

现在可以输出

1
2
3
4
5
6
String s2 = """
床前明月光
疑是地上霜
举头望明月
低头思故乡
""";


Java 14

instanceof模式匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test() {
public static void main(String[] args) {
Object obj = 1; // 多态,Integer类型赋值给Object类型
// 强转之前的判断
if (obj instanceof Integer) {
Integer i = (Integer) obj;
System.out.println(i);
}

// 新写法,如果 obj instanceof Integer 为 true, 则注解赋值给 i(变量名)
if (obj instanceof Integer i) {
System.out.println(i);
}
}
}

友好的空指针提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestNull {
public static void main(String[] args) {
new A().b.test();
}

}

class A {
public B b;
}

class B {
public void test() {
System.out.println("BBBBBBBB");
}
}

image-20240317142941449

record

image-20240317143340694

在创建类时可以创建record类型的,这个类型的类可以省略get方法,构造方法,toString方法,hashcode方法,equals方法等方法,在jdk 14中预览增加了record类型,16正式上线

record类型的特点

  • 它是一个final类
  • 成员变量均为public属性
  • 自动实现equals、hashCode、toString函数

1
2
3
4
5
public record Student(String name, int age) {
public void study() {
System.out.println("我爱学Java...")
}
}

现在都是用lombok插件来实现这个效果



Java 15

Sealed Classes

密封类和接口,作用是限制一个类可以由哪些之类继承或者实现

  • 如果指定模块的话,sealed class和其之类必须在同一个模块下,如果没用指定模块,则需要在同一个包下
  • sealed class指定的子类必须直接继承sealed class
  • sealed class的子类要用final修饰
  • 如果子类也要被继承,则之类也要用sealed修饰


Java 16

包装类构造方法的警告

使用包装类的构造方法在编译时会出现警告,不在建议使用包装类的构造方法,下面代码在javac编译之后会出现警告

image-20240317145253424

锁对象不要使用包装类

会有警告,包装类底层会有缓冲池,如果锁对象使用包装类,可以会造成不同的锁代码块争抢同一个锁对象的情况


新增日期字段

1
2
3
public static void main(String[] args) {
System.out.println(DateTimeFormatter.ofPattern("B").format(LocalDateTime.now()));
}
image-20240317150208741

传入不同的字符会打印出不同的效果

Symbol Meaning Presentation Examples
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
g modified-julian-day number 2451334
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F day-of-week-in-month number 3
a am-pm-of-day text PM
B period-of-day text in the morning
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-day (1-24) number 24
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
v generic time-zone name zone-name Pacific Time; PT
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00
X zone-offset ‘Z’ for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15
Z zone-offset offset-Z +0000; -0800; -08:00
p pad next pad modifier 1
escape for text delimiter
‘’ single quote literal
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use


Java 17

参考文章

Sealed Classes成为正式版


伪随机数发生变化

增加了伪随机数相关的类和接口来让开发者使用stream流进行操作

  • 新接口RandomGenerator:它为所有现有的和新的 PRNG 提供了一个统一的 API。 RandomGenerators 提供名为 ints、longs、doubles、nextBoolean、nextInt、nextLong、nextDouble 和 nextFloat 的方法,以及它们当前的所有参数的变化。从而更容易在应用程序中互换使用各种 PRNG 算法
  • 新类RandomGeneratorFactory

switch语法变化:可以在swatch中减少强转的操作

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
// Old code
static String formatter(Object o) {
String formatted = "unknown";
if (o instanceof Integer i) {
formatted = String.format("int %d", i);
} else if (o instanceof Long l) {
formatted = String.format("long %d", l);
} else if (o instanceof Double d) {
formatted = String.format("double %f", d);
} else if (o instanceof String s) {
formatted = String.format("String %s", s);
}
return formatted;
}

// New code
static String formatterPatternSwitch(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();
};
}

去除了AOT和JIT



Java 18

默认使用UTF-8字符编码

从jdk 18开始,默认使用UTF-8字符编码,可以通过下面的参数修改为其它字符编码

1
-Dfile.encoding-UTF-8

被移除的方法

在jdk 18中,标记了Object中的finalize方法,Thread中的stop方法将在未来被移除


@snippet注解

以前在文档注释中写代码需要添加code标签,现在通过@snippet注解可以更方便的将文档注解中的代码展示在api文档中



Java 19

参考文章

虚拟线程(预览)

它是轻量级线程,可显著减少编写、维护和观察高吞吐量并发应用程序的工作量。 其目标包括以简单的请求线程样式编写的服务器应用程序,使得能够接近最佳的硬件利用率,现有代码能够以最小的更改采用虚拟线程,并启用故障排除、调试和使用现有 JDK 工具分析虚拟线程


外部函数和API

Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行互操作。通过高效地调用外部函数(即 JVM 之外的代码)和安全地访问外部内存(即不受 JVM 管理的内存),该 API 使 Java 程序能够调用本机库并处理本机数据,而不会像 JNI 那样危险和脆弱



Java 20

参考文章

作用域值

作用域值(Scoped Values)它可以在线程内和线程间共享不可变的数据,优于线程局部变量,尤其是在使用大量虚拟线程时。

1
2
3
4
5
6
7
8
final static ScopedValue<...> V = new ScopedValue<>();

// In some method
ScopedValue.where(V, <value>)
.run(() -> { ... V.get() ... call methods ... });

// In a method called directly or indirectly from the lambda expression
... V.get() ...

作用域值允许在大型程序中的组件之间安全有效地共享数据,而无需求助于方法参数。



Java 21

字符串模板

String Templates(字符串模板) 目前仍然是 JDK 21 中的一个预览功能。

String Templates 提供了一种更简洁、更直观的方式来动态构建字符串。通过使用占位符${},我们可以将变量的值直接嵌入到字符串中,而不需要手动处理。在运行时,Java 编译器会将这些占位符替换为实际的变量值。并且,表达式支持局部变量、静态/非静态字段甚至方法、计算结果等特性。


虚拟线程正式版


Java新特性
https://lzhengjy.github.io/2024/03/10/Java新特性/
作者
Zheng
发布于
2024年3月10日
许可协议