Java17新特性

Java17新特性

2021年9月14日Java 17发布。

Java 17是Java 11以来又一个LTS(长期支持)版本,Java 11 和Java 17之间发生了那些变化可以在OpenJDK官网找到JEP(Java增强建议)的完整列表。

语法新特性

  • 文本块
  • switch表达式
  • record关键字
  • sealed classes密封类
  • instanceof模式匹配
  • Helpful NullPointerExceptions
  • 日期周期格式化
  • 精简数字格式化支持
  • Stream.toList()简化

1.文本块

在Java17之前的版本里,如果我们定义一个比较长的字符串时基本都是通过如下方式定义的:

public void lowVersion() {
    String text = "{\n" +
        "  \"name\": \"wry\",\n" +
        "  \"age\": 18,\n" +
        "  \"address\": \"chongqin\"\n" +
        "}";
    System.out.println(text);
}

存在问题:

  • 双引号需要进行转义
  • 为了字符串的可读性需要通过+号连接

在Java17中通过文本块解决这个问题,三个双引号表示文本块:

private void highVersion() {
    String text = """
            {
              "name": "wry",
              "age": 18,
              "address": "chongqin"
            }
            """;
    System.out.println(text);
}

2.switch表达式

Java 17版本中switch表达式将允许switch有返回值,并且可以直接作为结果赋值给一个变量,等等一系列的变化。

在jdk1.8中的switch表达式比较单一,每个case只能有一个值,且每个case需要添加break语句,避免继续往下面的case执行。如:

 private static void lowVesion(Integer score) {
        switch (score) {
            case 1:
                System.out.println("很丑!");
                break;
            case 2:
                System.out.println("还行!");
                break;
            default:
                System.out.println("超帅!");
        }
    }

到Java17,switch表达式有了较大的改变,不仅支持多个条件用一个case,且支持返回值,不需要额外设置break。

private static void withSwitchExpression(Integer score) {
    switch (score) {
        case 1, 2 -> System.out.println("很丑!");
        case 3, 4 -> System.out.println("还行!");
        default -> System.out.println("超帅!");
    }
}

switch表达式也可以返回一个值,把switch语句当做一个有返回值的函数使用:

private static void withReturnValue(Integer score) {
    String text = switch (score) {
        case 1, 2 -> "很丑!";
        case 3, 4 -> "还行!";
        default -> "超帅!";
    };
    System.out.println(text);
}

如果你想在case里想做不止一件事,又不想返回的时候退出整个函数,比如在返回之前先进行一些计算或者打印操作,可以通过大括号来作为case块,最后的返回值使用关键字yield进行返回。

private static void oldStyleWithYield(Integer score) {
    System.out.println(switch (score) {
        case 1, 2:
        	//。。。
            yield "很丑!";
        case 3, 4:
        	//。。。
            yield "还行!";
        default:
        	//。。。
            yield "超帅!";
    });
}

3.record关键字

record用于创建不可变的数据类。在这之前如果你需要创建一个存放数据的类,通常需要先创建一个Class,然后生成构造方法、getter、setter、hashCode、equals和toString等这些方法,或者使用Lombok来简化这些操作。

比如定义一个Person类:

// 这里使用lombok减少代码
@Data
@AllArgsConstructor
public class Person {
    private String name;

    private int age;

    private String address;
}

我们来通过Person类做一些测试,比如创建两个对象,对他们进行比较,打印这些操作。

public static void testPerson() {
    Person p1 = new Person("yyy", 18, "chongqing");
    Person p2 = new Person("xxx", 28, "hangzhou");
    System.out.println(p1);
    System.out.println(p2);
    System.out.println(p1.equals(p2));
}

假设有一些场景我们只需要对Person的name和age属性进行打印,在有record之后将会变得非常容易。

public static void testPerson() {
    Person p1 = new Person("小黑说Java", 18, "北京市西城区");
    Person p2 = new Person("小白说Java", 28, "北京市东城区");
    // 使用record定义
    record PersonRecord(String name,int age){}
    
    PersonRecord p1Record = new PersonRecord(p1.getName(), p1.getAge());
    PersonRecord p2Record = new PersonRecord(p2.getName(), p2.getAge());
    System.out.println(p1Record);
    System.out.println(p2Record);
}

record也可以单独定义作为一个文件定义,但是因为Record的使用非常紧凑,所以可以直接在需要使用的地方直接定义。

package com.wry.test;

public record PersonRecord(String name, int age) {
    
}

4.密封类 sealed class

密封类可以让我们更好的控制哪些类可以对我定义的类进行扩展。

在没有sealed关键字之前,一个类要么是可以被extends的,要么是final的,只有这两种选项。

密封类可以控制有哪些类可以对超类进行继承,在Java 17之前如果我们需要控制哪些类可以继承,可以通过改变类的访问级别,比如去掉类的public,访问级别为默认。

比如我们可以定义三个类:

public abstract class Furit {
}
public class Apple extends Furit {
}
public class Pear extends Furit {
}

那么我们可以在另一个包中写如下的代码:

private static void test() {
    Apple apple = new Apple();
    Pear pear = new Pear();
    Fruit fruit = apple;
    class Avocado extends Fruit {};
}

在这个包中,既可以定义Apple,Pear,也可以将apple实例赋值给Fruit,并且可以对Fruit进行继承。

如果我们不想让Fruit在本包以外被扩展,那么我们只能修改其访问权限,去掉class的public修饰符。

这样虽然可以控制被被继承,但是也会导致Fruit fruit = apple;也编译失败。在Java 17中通过密封类可以解决这个问题。

//指定Furit只能被Apple和Pear继承
public abstract sealed class Furit permits Apple, Pear {
}

//子类需要指明它是final,non-sealed或sealed的。父类不能控制子类是否可以被继承。
public non-sealed class Apple extends Furit {
}

public final class Pear extends Furit {

}
private static void test() {
    Apple apple = new Apple();
    Pear pear = new Pear();
	// 可以将apple赋值给Fruit
    Fruit fruit = apple;
    // 只能继承Apple,不能继承Furit
    class Avocado extends Apple {};
}

5.instanceof模式匹配

通常我们使用instanceof时,一般发生在需要对一个变量的类型进行判断,如果符合指定的类型,则强制类型转换为一个新变量。

private static void oldStyle(Object o) {
    if (o instanceof Furit) {
        Furit furit = (GrapeClass) o;
        System.out.println("This furit is :" + furit.getName);
    }
}

在使用instanceof的模式匹配后,可以将类型转换和变量声明都在if中处理。同时,可以直接在if中使用这个变量

private static void oldStyle(Object o) {
    if (o instanceof Furit furit && furit.getColor()==Color.RED) {
        System.out.println("This furit is :" + furit.getName);
    }
}

因为只有当instanceof的结果为true时,才会定义变量fruit,所以这里可以使用&&,但是改为||就会编译报错。

6.Stream.toList()

如果需要将Stream转换成List,需要通过调用collect方法使用Collectors.toList(),代码非常冗长。

在Java 17中将会变得简单,可以直接调用toList()。

private static void streamToList() {
    Stream<String> stringStream = Stream.of("a", "b", "c");
    List<String> stringList =  stringStream.toList();
}

你可能感兴趣的:(java,apache,开发语言)