Jdk8之后各版本常用新特性

JDK10 var 关键字

REP-286 增强 Java 语言,将类型推断扩展到带有初始值设定项的局部变量声明。我们寻求通过减少与编写 Java 代码相关的仪式改善开发人员的体验,同时通过允许开发人员省略通常不必要的局部变量类型的清单声明来保持 Java 对静态类型安全的承诺​查询详情

局部变量推断类型,作用在局部代码块中,通过编译器的类型推断来避免频繁的定义一些类型,其目标是作用在局部变量、lambda表达式、for循环中声明降低 Java 代码编写和阅的繁琐程度(不用左右复制变量的类型信息真的很爽),但是var关键字使用不当也会降低其代码的可读性,所以还是谨慎使用

1
2
3
4
5
6
7
8
9
10
@Test
@SneakyThrows
public void testVar() {
// 立即声明且初始话的时候才能使用var变量
var users = new ArrayList<>();
assert users instanceof List;
users.add("zs");
users.add("ls");
users.forEach(System.out::println);
}

switch

switch-case语法一直都有,但是过去的switch不能判断基本数据类型、枚举、null,分支之后也不支持代码块,但是从JDK12开始对其进行了大的增强,一直延续到现在还没有停止,本文使用环境为JDK17,所以还有19上新增加的case类型判断没有测试

  • 基本数据类型的支持和yield关键字的使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Test
    public void testSwitch() {
    var number = 2;
    var result = switch (number) {
    case 0:
    yield "zero";
    case 1:
    yield "one";
    case 2:
    yield "two";
    case 3:
    yield "three";
    default:
    yield "未知";
    };
    log.info("The English representation of {} is {}", number, result);
    }
  • 枚举的支持与case多值

    1
    2
    3
    4
    // 枚举
    enum DAYS {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    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
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    @Test
    public void testSwitch2() {
    var day = DAYS.SUNDAY;
    var type = switch (day) {
    case MONDAY:
    yield "Weekday";
    case TUESDAY:
    yield "Weekday";
    case WEDNESDAY:
    yield "Weekday";
    case THURSDAY:
    yield "Weekday";
    case FRIDAY:
    yield "Weekday";
    case SATURDAY:
    yield "Weekend";
    case SUNDAY:
    yield "Weekend";
    default:
    yield "Unknown";
    };
    log.info("{} is {}", day, type);
    }

    // new
    @Test
    public void testSwitch3() {
    var day = DAYS.MONDAY;
    var type = switch (day) {
    // case 多值
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY:
    yield "Weekday";
    case SATURDAY, SUNDAY:
    yield "Weekend";
    default:
    yield "Unknown";
    };
    log.info("{} is {}", day, type);
    }
  • lambda表达式(代码块)支持

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Test
    public void testSwitch4() {
    var day = DAYS.MONDAY;
    var type = switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
    log.info("Weekday!!!");
    yield "Weekday";
    }
    case SATURDAY, SUNDAY -> {
    log.info("Weekend!!!");
    yield "Weekend";
    }
    default -> {
    log.info("Unknown!!!");
    yield "Unknown";
    }
    };
    log.info("{} is {}", day, type);
    }

JDK15 text block

JEP 378 将文本块添加到 Java 语言中。文本块是多行字符串文字,它不需要大多数转义序列,以可预测的方式自动格式化字符串,并让开发人员在需要时控制格式。

1
2
3
4
5
6
7
8
9
10
@Test
public void testTextBlock() {
var html = "<html>\n" +
" <body>\n" +
" <p>%s</p>\n" +
" </body>\n" +
"</html>\n";
log.info("{}", html);
log.info("{}", html.formatted("hello world!"));
}

JDK16 instanceof

JEP 394通过instanceof 运算符的模式匹配增强Java 编程语言。模式匹配允许更简洁、更安全地表达程序中的通用逻辑,即从对象中条件提取组件。

经常会遇到我得到了一个Object对象,但是我知道实际上它是一个String对象,这个时候如果我想使用该对象的方法那我就必须使用强制转换,但是强制转换之前我一般都会进行一次instanceof检查它的正确类型避免强制类型转换报错,就像下边这样,这样看上去就像是告诉编译器两次它是String类型,看起来就很愚蠢

1
2
3
if (obj instanceof String) {
String s = (String) obj; // grr...
}

还好在JDK16引入了全新的instanceof写法,帮我们解决了这个愚蠢的问题

1
2
3
4
5
6
7
8
@Test
@SneakyThrows
public void testInstanceOf() {
Object names = List.of("zs");
if (names instanceof List list) {
list.forEach(System.out::println);
}
}

JDK16 recode 关键字

JEP 395使用recode增强 Java 编程语言,记录是充当不可变数据的透明载体的类。记录可以被认为是名义元组。
人们一直认为Java是一门比较繁杂的语言,有时候仅仅是想创建一个需要返回给前端的VO都要写很长的代码,在Lombok出现之后极大了减少了必须由我们所写的重复的代码。在recode出现之后这类情况大有改善。我们以一个具有横纵坐标的Ponit为例看下recode究竟帮我们减轻了多少负担

  • before

    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
    26
    class Point {
    private final int x;
    private final int y;

    Point(int x, int y) {
    this.x = x;
    this.y = y;
    }

    int x() { return x; }
    int y() { return y; }

    public boolean equals(Object o) {
    if (!(o instanceof Point)) return false;
    Point other = (Point) o;
    return other.x == x && other.y == y;
    }

    public int hashCode() {
    return Objects.hash(x, y);
    }

    public String toString() {
    return String.format("Point[x=%d, y=%d]", x, y);
    }
    }
  • now

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    record Point(int x, int y) { }

    // 查看编译后的文件可以发现其实编译器帮我自动生成了规范的构造方法,如下
    // 因为它代表是一个数据载体,所以它没有set方法
    public record Point(int x, int y) {
    public Point(int x, int y) {
    this.x = x;
    this.y = y;
    }

    public int x() {
    return this.x;
    }

    public int y() {
    return this.y;
    }
    }

    当然我们如果想验证他的一些字段也可以通过一个“紧凑的规范构造函数”,用于验证其隐式形式参数

    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
    26
    public record Point(int x, int y) {
    public Point {
    if (x < 0 || y < 0) {
    throw new IllegalArgumentException("参数 'x' 'y' 不能小于0");
    }
    }
    }
    // 编译之后得到
    public record Point(int x, int y) {
    public Point(int x, int y) {
    if (x >= 0 && y >= 0) {
    this.x = x;
    this.y = y;
    } else {
    throw new IllegalArgumentException("参数 'x' 'y' 不能小于0");
    }
    }

    public int x() {
    return this.x;
    }

    public int y() {
    return this.y;
    }
    }

    同样,我们也能重写它的get方法在获取的时候验证其参数是否合法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public record Point(int x, int y) {
    public Point {
    if (x < 0 || y < 0) {
    throw new IllegalArgumentException("参数 'x' 'y' 不能小于0");
    }
    }

    public int x() {
    return Math.max(x, 0);
    }

    public int y() {
    return Math.max(y, 0);
    }
    }

JDK15密封类

在Java 15之前,Java认为”代码重用”始终是一个终极目标,所以,一个类和接口都可以被任意的类实现或继承。但是,在很多场景中,这样做是容易造成错误的,而且也不符合物理世界的真实规律。而密封类(Sealed Classes)是一种限制继承的新机制,通过使用关键字sealed修饰类,可以将一个类限制为只能被特定的一些类继承。

1
2
3
4
5
6
7
8
9
10
11
// 定义了一个密闭类People,它规定只能被Student和Teacher两个类继承
public sealed class People permits Student, Teacher {
}

final class Student extends People {

}

final class Teacher extends People {

}
1
2
3
4
5
6
7
8
9
10
11
12
// 定义了一个密闭接口Service,它规定只能被StudentService和TeacherService两个类实现
public sealed interface Service permits StudentService,TeacherService {
}


final class StudentService implements Service {

}

final class TeacherService implements Service {

}

Jdk8之后各版本常用新特性
https://vegetablest.github.io/2022/08/11/jdk8之后各版本常用新特性/
作者
af su
发布于
2022年8月11日
许可协议