java17新特性简单介绍
前言
本项目使用的 IDE 为 IDEA 2023.3.3(社区版)。
文本块
java17之前的写法,双引号需要转义,换行需要\n,需要加号拼接
public class TestString {
public static void main(String[] args) {
String json = "{\n" +
" \"id\": \"1\",\n" +
" \"name\": \"菜鸟教程\",\n" +
" \"url\": \"www.runoob.com\"\n" +
"}";
System.out.println(json);
}
}
java17的写法,可读性更好,一目了然。
public class TestString {
public static void main(String[] args) {
String json = """
{
"id": "1",
"name": "菜鸟教程",
"url": "www.runoob.com"
}
""";
System.out.println(json);
}
}
像传统的字符串一样,文本块是字符串的一种常量表达式。不同于传统字符串的是,在编译期,文本块要顺序通过如下三个不同的编译步骤:
- 为了降低不同平台间换行符的表达差异,编译器把文本内容里的换行符统一转换成 LF(\u000A);
- 为了能够处理Java源代码里的缩进空格,要删除所有文本内容行和结束分隔符共享的前导空格,以及所有文本内容行的尾部空格;
- 最后处理转义字符,这样开发人员编写的转义序列就不会在第一步和第二步被修改或删除。
通过反编译可以看到,文本块本质上也是语法糖。
switch表达式
java17之前的写法,必须要加break
public class TestSwitch {
public static void main(String[] args) {
test("+",5,6);
}
private static void test(String sign, int num1, int num2) {
int ret = 0;
switch (sign) {
case "+":
ret = num1 + num2;
break;
case "-":
ret = num1 - num2;
break;
case "*":
ret = num1 * num2;
break;
case "/":
ret = num1 / num2;
break;
default:
break;
}
System.out.println("ret: " + ret);
}
}
java17的写法,switch必须覆盖所有可能的情况,如果是枚举,就必须包含所有值
public class TestSwitch {
public static void main(String[] args) {
test1("*", 5, 6);
}
private static void test1(String sign, int num1, int num2) {
int ret = switch (sign) {
case "+" -> {
System.out.println("this is add");
yield num1 + num2;// 下面的是简化写法
}
case "-" -> num1 - num2;
case "*" -> num1 * num2;
case "/" -> num1 / num2;
default -> throw new IllegalStateException("Unexpected value: " + sign);
};
System.out.println("ret: " + ret);
}
}
本质上也是编译器的语法糖
record类
public class TestRecord {
public static void main(String[] args) {
Person person = new Person("lisi", "123");
System.out.println(person.username());// 不是标准的 getter
System.out.println(person);
}
public record Person(String username,String password){
}
}
会帮我们生成构造器,toString()方法,equals()方法,对象不可变,没有 setter 及标准的 getter,功能不如 lombok 强大。
密封类
在这之前一个类要么是可以被extends的,要么是final的,只有这两种选项。密封类可以控制有哪些类可以对超类进行继承。子类需要指明它是final,non-sealed或sealed的,父类不能控制子类是否可以被继承。
public class TestSealed {
public static void main(String[] args) {
new TestSealed().test();
}
public void test() {
Animal animal = new WildPig();
System.out.println(animal);
}
/**
* 动物
*/
sealed class Animal permits Dog, Cat, Pig {
}
/**
* 狗
*/
final class Dog extends Animal {
}
/**
* 猫
*/
non-sealed class Cat extends Animal {
}
/**
* 猪
*/
sealed class Pig extends Animal permits WildPig {
}
/**
* 野猪
*/
final class WildPig extends Pig {
}
}
instanceof模式匹配
java17之前必须手动进行类转换
public class TestInstanceOf {
public static void main(String[] args) {
test1("hello");
test1(123);
}
private static void test1(Object obj) {
if (obj instanceof String) {
int length = ((String) obj).length();
System.out.println(length);
}
}
}
java17可以简写
public class TestInstanceOf {
public static void main(String[] args) {
test1("hello");
}
private static void test1(Object obj) {
if (obj instanceof String str) {
System.out.println(str.length());
}
}
}
空指针异常打印增强
import lombok.Data;
public class TestNullPointerException {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.getAddress().getProvince().getCode());
}
@Data
private static class Person {
private Address address;
}
@Data
private static class Address {
private Province province;
}
@Data
private static class Province {
private String name;
private String code;
}
}
java17之前的异常信息如下,仅显示发生异常的行号,但不知道具体哪个方法调用时产生的异常,必须通过调试的方式找到。
Exception in thread "main" java.lang.NullPointerException
at com.imooc.TestNullPointerException.main(TestNullPointerException.java:9)
java17的异常信息如下,会显示出现异常的精确位置。
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.imooc.TestNullPointerException$Address.getProvince()" because the return value of "com.imooc.TestNullPointerException$Person.getAddress()" is null
at com.imooc.TestNullPointerException.main(TestNullPointerException.java:8)
Stream增强
java17之前
public class TestStream {
public static void main(String[] args) {
List<String> languages = Stream.of("java", "python").collect(Collectors.toList());
System.out.println(languages);
}
}
java17增加了一个 toList() 方法
public class TestStream {
public static void main(String[] args) {
List<String> languages = Stream.of("java", "python").toList();
System.out.println(languages);
}
}
矢量运算
private static void testFloatVector() {
float[] data = {0.1f, 0.2f};
FloatVector floatVector = FloatVector.fromArray(FloatVector.SPECIES_64, data, 0);
System.out.println(floatVector.mul(floatVector));
}
报错如下
java: package jdk.incubator.vector is not visible
(package jdk.incubator.vector is declared in module jdk.incubator.vector, which is not in the module graph)
添加编译参数 --add-modules jdk.incubator.vector
还报错
Error: Unable to initialize main class com.imooc.TestFloatVector
Caused by: java.lang.NoClassDefFoundError: jdk/incubator/vector/Vector
添加运行时VM参数 --add-modules jdk.incubator.vector
运行结果为
[0.010000001, 0.040000003]
参考
Java 17的这些新特性不看后悔
深入剖析Java新特性-02文字块:怎么编写所见即所得的字符串?
java9系列(六)HTTP/2 Client (Incubator)
Linux和Windows下JDK17下载地址
Mac下JDK17下载地址