首页 > 编程语言 >Java 17 新增的语法特性

Java 17 新增的语法特性

时间:2024-06-13 18:10:48浏览次数:37  
标签:JEP Java 17 语法 sealed class out

Java 17 新增的语法特性

目录

说明:本文代码详见《面向实践的Java程序设计教程》教材的代码仓库

JEP与Project Amber

在Java语言的发展过程中,如果需要添加新的语法特性,可以先由发起者以JEP(Java Enhancement Proposal,Java增强提案)文档的形式提出,然后通过初步评估、创建JEP、社区反馈、JEP评审、批准或拒绝、预览等一系列步骤,才可能正式发布。
Open JDK推出的Project Amber项目是用来探索和孵化已经被JEP接受的较小的Java语言特性。本文中所介绍的Java新特性,通常会在后面标上JEP,这是用来说明该语法特性是属于哪个JEP。在搜索引擎中输入JEP编号,即可找到对应的JEP文档。

引言

Java 17于2021年9月14日发布,新增了多个语法特性与类。其中,最值得关注的特性包括:Record、Text Blocks、Pattern Matching for instanceof、Sealed Classes等。这些特性有的是在Java 17发布,有的则是在Java 17之前Java 11之后发布。本文将详细介绍这些新特性,并提供相应的示例代码。

1. Records [JEP 395]

Java 16发布的Record 类型是一种新的数据类型,用于表示不可变的数据,它允许我们创建一个不可变的类型。Record类型只包含数据字段访问器方法,不包含可变状态或自定义方法。
通过record关键字,可以轻松地创建不可变的数据传输对象(DTO)。DTO的英文全称是Data Transfer Object,它是一种用于数据传输的简单POJO(Plain Old Java Object)。Record类型适用于存储纯粹的值类型数据,如坐标点和只读的日志记录。当你需要一个类型仅用来存储数据的时候,就使用Record类型。

例子:现在需要定义一个表示点的record类型Point,该类型包含两个字段x和y,以及相应的访问器方法x()y()。可以使用如下代码:

public class PointRecordMain {
    public static void main(String[] args) {
        // 定义了record类型,也可以单独定义到一个文件中
        record Point(int x, int y) {}; 
  
        // record类型的使用
        Point origin = new Point(0, 0);
        Point dest = new Point(0, 0);
        System.out.println(origin); // 调用origin自动生成的toString()方法
        System.out.println(origin.x()); // 获得x的属性值
        System.out.println(origin.y());
        System.out.println(origin.equals(dest)); // 比较所有属性的值,返回true
        System.out.println(origin == dest); // 返回false,因为比较的是引用地址
    }
}

程序输出:

Point[x=0, y=0]
0
0
true
false

代码分析:

Point虽然没有定义任何的构造方法、getter方法、equals方法等,但是编译器会自动生成这些方法。
toString()方法:返回包含所有属性的字符串表示形式。
x()、y()访问器方法:回对应属性的值。
equals方法:会比较所有属性的值是否相等,类似于使用Objects.equals()方法。这使得record类型实例调用equals方法时,会比较所有属性的值,而不是引用地址。
实际上,使用record关键字定义的类型直接继承自java.lang.Record类,如果在一个文件中有多个record类型,将会生成对应的多个.class文件。可以使用javap 对应的字节码文件.class来查看验证。

record类型可以定义在一个单独的文件中,也可以如例程所示在需要使用的方法中直接定义。record类型还支持定义多个构造方法,以及默认构造方法,详见本书所附代码仓库中的PointRecordMain.java

参考资料:

JDK有用的新特性-Java Record

2. switch表达式与yield[JEP 361]

在Java 17中,引入了switch表达式(Switch Expressions)、yield关键字与->符号。
可以在标签case(label case)中使用yield关键字返回一个值。
示例代码如下所示:

public class SwitchMain {
    public static void main(String[] args) {
        String day = "Monday";

        String r = switch (day) { // switch表达式
        case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> {
            System.out.println("进入分支1");
            yield "工作日"; // 相当于return "工作日"
        }
        case "Saturday", "Sunday" -> "休息日"; // 无需break与yield,直接返回
        default -> throw new IllegalArgumentException("输入错误");
        };

        System.out.println(day + "是" + r);
    }
}

程序输出:

进入分支1
Monday是工作日

在这段代码中,switch表达式返回了一个字符串,并将其赋给变量r。在case分支中,使用yield关键字来返回一个值。这样,switch表达式就可以返回一个值,并且无需使用break来进行中断。如果->后没有大括号,则表示返回一个表达式。代码case "Saturday", "Sunday" -> "休息日";中虽然没有使用yield关键字,但是它返回了字符串"休息日"。

注意:switch块中的->右侧可以是代码块、表达式或者是手动抛出的异常。

3. 用于instanceof的模式匹配[JEP 394]

在Java 16中,就可以使用模式匹配(Pattern Matching)和instanceof关键字来处理类型检查和匹配,可以无需进行显式的类型转换。如下代码所示:

record Person(String name, int age) {
};

public class PatternMatchMain {
    public static void main(String[] args) {
        Object obj = new Person("李耀", 18);

        if (obj instanceof Person p) { // 如为true,则将obj转化为Person类型然后赋给模式变量p
            // 无需强制类型转换,直接使用转换后的p
            System.out.println(p.age());
        } else if (obj instanceof String s) {
            // 如果是字符串则调用其toLowerCase()
            System.out.println(s.toLowerCase());
        }

        // instanceof语句后拼接其他条件
        if (obj instanceof Person p && p.age() > 16) {
            System.out.println("age = " + p.age() + " > " + 16);
        }
    }
}

程序输出:

18
age = 18 >16

代码obj instanceof Person p中的Person p就是类型模式(type pattern),p是模式变量(pattern variable)。如果obj instanceof Person返回true,那么obj就会被转化为Person类型并赋值给变量p。

注意:模式变量只是一个特殊的局部变量,有其作用域,不能在作用域外使用。哪些算是作用域外?无需逐步分析,直接让Java编译器帮我们判定即可。

4. 文本块[JEP 378]

在Java 17中的文本块(Text Blocks)是一种新的字符串字面量,它允许我们在编写多行字符串时更加方便和易于阅读。文本块使用三个反引号(```)来包裹字符串,并且可以在字符串中包含换行符、制表符和其他特殊字符。如下代码所示:

public class TextBlockMain {
    public static void main(String[] args) {
        String textBlock = """
                这是一个"多行字符串"
                可以包含制表符\t我前面是制表符
                还可以包含换行符\n我前面是换行符
                  我前面是2个空格。
                接下来是一个Person对象的json字符串
                {
                  "name": "John Doe",
                  "age": 30,
                }
                                """;
        System.out.println(textBlock);
    }
}

程序输出:

这是一个"多行字符串"
可以包含制表符    我前面是制表符
还可以包含换行符
我前面是换行符
  我前面是2个空格。
接下来是一个Person对象的json字符串
{
  "name": "John Doe",
  "age": 30,
}

文本块常用于编写多行注释和文档字符串,还可用来编写SQL查询语句、JSON字符串等。
在实际编写文本块的时候,可以先在一个文本编辑软件中编写好文本块,然后复制到Java代码中。

更多文本块的用法可参考文章:

【升级到Java 17】文本块

5. 密封类[JEP 409]

在Java 17中,密封类(sealed class)允许我们限制一个类的继承范围或一个接口的实现范围,确保它只能被指定的子类继承或指定的类实现
密封类或密封接口可以有一个或多个允许的子类,这些子类必须是事先指定的,并且必须在同一个模块中声明。比如,某些类你只想让本模块中的其他类继承,而不想让其他模块中的类继承,就可以使用密封类来限制继承范围。

直接继承或实现一个sealed的类型的类只能声明为final, sealed或non-sealed,参考代码如下:

// 只允许Bird, Aircraft, UFO实现Flyable接口
sealed interface Flyable permits Bird, Aircraft, UFO {
}

/*直接继承或实现一个sealed的类型的类只能声明为final, sealed或non-sealed*/

// 1. 声明为final。其不能有子类。
final class Bird implements Flyable{
}

// 2. 声明为non-sealed。未封闭,其可以有子类
non-sealed class Aircraft implements Flyable{
}

class Helicopter extends Aircraft{
}

// 3. 声明为sealed。封闭,代表其只能拥有permits声明的子类。
sealed class UFO implements Flyable permits CircleUFO, RectUFO{
}

final class CircleUFO extends UFO{
}

final class RectUFO extends UFO{
}

在上述代码中,Flyable接口被声明为sealed,这意味着它只能被Bird, Aircraft, UFO三个类实现。Bird类被声明为final,这意味着它不能有子类。Aircraft类被声明为non-sealed,这意味着它允许有子类。UFO类被声明为sealed,这意味着它只能被CircleUFO, RectUFO两个类实现。

Java 17的其他主要改进

Java 17还有一些其他值得注意的改进,见下文:

稳定版的ZGC垃圾回收器

ZGC垃圾回收器(Z Garbage Collector)是JVM的一种低延迟垃圾回收器,在Java 11作为实验性特性引入。在Java 17中,ZGC成为稳定版,这意味着它将包含在JDK中,并且可以用于生产环境。ZGC作为JVM可以达到亚毫秒级停顿,不过需要手动开启。

主流Java开发框架支持

Spring Framework 6Spring Boot 3框架需要基于Java 17。

更清晰的NullPointerException异常信息提示

Java 17中对于NullPointerException异常信息提示进行了改进,以前的提示只能定位到行,Java 17中则能定位到具体某个变量。
以如下代码进行说明:

public class NullPointerExceptionMessageDemo {
    public static void main(String[] args) {
        String str = null;
        System.out.println(str.length());
    }
}

运行后,会得到如下异常信息提示:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null
可以看到,异常信息提示中的because "str" is null已经明确指出str是null。

Stream接口新增toList()方法

在Java 17中Stream接口正式增加了toList()方法,可以方便地将Stream转换为List,而无需使用Collectors.toList()方法。

如下代码所示:

import java.util.List;
// import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamToListDemo {
    public static void main(String[] args) {
        Stream<Integer> scoreStream = Stream.of(98, 97, 90);
        List<Integer> scoreList = scoreStream.toList();
        // Java 11中需要使用Collectors收集器
        // List<Integer> list = scoreStream.collect(Collectors.toList());
        
        for (Integer e : scoreList) {
            System.out.print(e+" ");
        }
    }
}

标签:JEP,Java,17,语法,sealed,class,out
From: https://www.cnblogs.com/zhrb/p/18246475

相关文章

  • Java 21新增的语法特性
    Java21新增的语法特性目录Java21新增的语法特性引言record模式[JEP440]用于switch的模式匹配[JEP441]序列集合[JEP431]虚拟线程[JEP444]Java21的其他改进说明1:本文大量参考了JEP文档与Oracle官方文档,部分文字直接引自这两个文档并进行了适当的修改。说明2:本文代码详......
  • Spring Junit 测试报错 java.lang.IllegalStateException
    写测试代码的时候出现了java.lang.IllegalStateException:CouldnotloadTestContextBootstrapper[null].Specify@BootstrapWith's'value'attributeormakethedefaultbootstrapperclassavailable.代码如下:packagecom.example.service;importcom.example.c......
  • Java优雅统计耗时【工具类】
    任务耗时如何优雅的打印,看完本文你就明白了!~importcn.hutool.core.date.StopWatch;importcn.hutool.core.lang.Console;/***优雅打印出任务耗时*/publicclassMain{publicstaticvoidmain(String[]args)throwsException{StopWatchstopWat......
  • JAVA面向对象练习题2
    题目要求:        定义一个Student实体类,成员变量:name、age。静态成员变量:在线人数。在测试类中:创建集合,存储学生对象,每创建一个学生对象,在线人数+1,删除一个学生对象,在线人数-1定义方法完成:请给集合中存储3个学生对象,并遍历集合,并输出在线人数。定义方法完成:请判断......
  • 2024.5.17
    8-9【Python0010】正整数的因子展开式分数10全屏浏览作者 doublebest单位 石家庄铁道大学【题目描述】编写程序,输出一个给定正整数x(x>1)的质因子展开式。【输入格式】请在一行中输入整数x的值。【输出格式】对每一组输入的x,按以下格式输出x的质因子......
  • SVRF Statement Syntax Conventions (SVRF 语法约束)
    SVRFStatementSyntaxConventionsSVRF语法语句约束ParameterOrder参数顺序Casesensitivity大小写敏感区分Literalkeywordsversusvariableparameters原文关键字和变量的特征Whitespaceconsiderations空白区域考虑(不太明白这个是是什么意思)Reserved......
  • SpringBoot问卷管理系统-计算机毕业设计源码71781
    摘 要随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,问卷调查当然也不例外。问卷管理系统是以实际运用为开发背景,运用软件工程原理和开发方法,采用Java技术构建的一个管理系统。整个开发过程首先对软件系统进行......
  • SpringBoot问卷管理系统-计算机毕业设计源码71781
    摘 要随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,问卷调查当然也不例外。问卷管理系统是以实际运用为开发背景,运用软件工程原理和开发方法,采用Java技术构建的一个管理系统。整个开发过程首先对软件系统进行......
  • 一起来学javascript-axios
       <!--//AJAX的封装插件——Axios。  //什么是Axios  //Axios是一个基于Promise的HTTP库,可以用于浏览器和Node.js,支持VanillaJS、Angular、React、Vue等框架。  //简单的理解就是对Ajax的封装,且具有易用、简洁、高效等特点。  ......
  • 使用 JavaScript 中的 DeviceOrientationEvent
    在前端开发中,DeviceOrientationEvent是一个非常有用的API,它允许我们访问设备的物理方向信息,如设备的倾斜和旋转。这个API可以在移动设备上获取设备的方向,可以用来创建各种有趣和交互性强的应用程序,比如游戏、增强现实体验等。本文将介绍如何使用DeviceOrientationEventAP......