Java中JDK8-17新特性下

BingBing-8888 / 2024-10-23 / 原文

JDK8-17新特性(第二部分)


目录
  • JDK8-17新特性(第二部分)
    • switch表达式增强
    • 文本块标准化
    • 模式匹配
    • 局部变量类型判断

switch表达式增强

在Java中,switch语句的增强主要体现在Java 12引入的预览特性(并在Java 14中成为正式特性)中,即所谓的“Switch Expressions”(switch表达式)。这个新特性使得switch语句变得更加灵活和强大,特别是在处理返回值和代码可读性方面。

传统Switch语句

在传统的Java中,switch语句主要用于根据一个表达式的值选择执行多个代码块之一。但是,它有几个限制:

  1. 它不能直接返回一个值(除非在每个case分支中使用return语句)。
  2. 它必须被完整地包含在一个代码块中,这可能导致“fall through”问题(即一个case执行后不会自动停止,而是继续执行下一个case,除非显式地使用了break语句)。

Switch Expressions

Switch Expressions解决了上述问题,并提供了一种更简洁、更灵活的方式来处理基于条件分支的返回值。其主要特点包括:

  1. 返回值:Switch Expressions可以直接返回一个值,这使得它们可以在表达式中使用,而不仅仅是作为语句。
  2. 箭头(->)语法:每个case分支现在使用箭头(->)来分隔条件和结果,这使得代码更加简洁和易于阅读。
  3. 默认分支(default):与传统switch语句一样,Switch Expressions也支持default分支来处理所有未明确匹配的情况。
  4. 覆盖所有情况的编译时检查:如果Switch Expressions的枚举类型或字符串字面量没有为所有可能的值提供case分支,并且没有default分支,则编译器将报错。这有助于避免运行时错误。

示例

假设我们有一个枚举类型Day,并希望根据枚举值返回对应的字符串描述:

enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

public class SwitchExample {
    public static String dayToString(Day day) {
        return switch (day) {
            case MONDAY, FRIDAY, SATURDAY, SUNDAY -> "Weekend";
            case TUESDAY, WEDNESDAY, THURSDAY -> "Weekday";
            default -> throw new IllegalStateException("Unexpected value: " + day);
        };
    }

    public static void main(String[] args) {
        System.out.println(dayToString(Day.MONDAY)); // 输出: Weekend
        System.out.println(dayToString(Day.WEDNESDAY)); // 输出: Weekday
    }
}

在这个例子中,我们使用了Switch Expressions来根据Day枚举的值返回不同的字符串。每个case分支都使用箭头(->)来分隔条件和结果。此外,我们还添加了一个default分支来处理所有未明确匹配的情况,并抛出一个异常。

总的来说,Switch Expressions是Java中switch语句的一个重要增强,它提供了更简洁、更灵活的方式来处理基于条件分支的返回值。

文本块标准化

17 引入了几项新特性,其中一项较为显著的是对文本块(Text Blocks)的进一步改进和标准化。文本块特性最初在Java 13中作为预览功能引入,并在Java 15中继续作为预览特性,最终在Java 17中成为正式特性。这一特性的引入,主要是为了简化编写多行字符串和格式化字符串的工作。

文本块简介
文本块允许你以更自然的方式编写字符串字面量,特别是对于那些需要跨越多行、包含特殊字符或需要特定格式(如HTML、SQL查询或JSON数据)的字符串。它们通过三重双引号(""")来界定,并自动管理行尾和缩进。

基本用法
在Java 17中,你可以这样使用文本块:

String html = """
    <html>
        <body>
            <h1>Hello, World!</h1>
        </body>
    </html>
""";

这里,字符串直接按原样保留了格式和换行,而不需要使用\n来手动插入换行符,也不需要担心内部的引号需要转义。

特性与优势

自动格式化:文本块会自动处理字符串中的换行和缩进,使得代码更加清晰易读。

减少转义字符:在文本块中,大多数情况下不需要使用转义字符,如"来表示双引号。

保留格式:特别适合用于编写SQL查询、HTML、JSON等格式化的文本内容。

控制缩进:末尾的空格会被忽略,可以通过最后一个"""之前的空格或制表符来控制输出字符串的前导空白。

注意事项

  • 文本块中,如果需要在字符串中表示三个连续的双引号,可以使用三个双引号加一个空格(""" 」)或者在三个双引号后直接跟随换行。
  • 虽然文本块在Java 17中成为正式特性,但在团队协作或维护旧项目时,考虑兼容性问题,确认项目环境和团队是否已准备好采用这一新特性。

模式匹配

如果您使用的是Java,那么您之前很有可能会看到其模式匹配。 String#matches(String)方法在内部使用Pattern类型,该类型包含更复杂的功能:

通过编译正则表达式来创建Pattern 。 该模式与任何输入字符串匹配,并且可以选择查找捕获组,这些捕获组隔离了字符串数据的某些部分。

该API的用法如下:

 Pattern pattern = Pattern.compile( "([\\^\\S]+) is powerful" );  Matcher matcher = pattern.matcher( "Java is powerful" );  System.out.println(matcher.find()); // true  System.out.println(matcher.group()); // Java is powerful  System.out.println(matcher.group( 1 )); // Java 

find()方法查找模式的下一次出现,该模式与本示例中的整个输入字符串匹配。 group()方法返回整个捕获组,即与整个模式匹配,或者在使用索引限定时返回单个捕获组。 捕获组索引从1开始,而不是从0

还有一个matches()方法,其工作方式略有不同:

 Pattern pattern = Pattern.compile( "([\\^\\S]+) is powerful" );  Matcher matcher = pattern.matcher( "Our Java is powerful" );  System.out.println(matcher.matches()); // false  System.out.println(matcher.find()); // true 

matches()尝试从头到尾将整个输入字符串与模式匹配,而find()仅尝试在输入字符串中的某个位置查找模式。

另外,提醒一下:请仅将快捷方式String#matches(String)Pattern#matches(String, CharSequence)用于不重复重复的单个匹配调用。 模式编译起来很繁琐,我们应该利用模式类型的不变性,并将其重用于多个匹配项。

局部变量类型判断

局部变量类型判断目的

通过减少与编写 Java 代码相关的形式来提高开发人员的体验,同时维持 Java 对静态类型安全的承诺,允许开发人员忽略局部变量类型的经常不必要的声明。 此功能将允许声明,例如:

            var list = new ArrayList<String>();  // 推断类型为: ArrayList<String>
            var stream = list.stream();          // 推断类型为: Stream<String>

局部变量类型推断的概述:

对于带有初始化器的局部变量声明、增强 For 循环索引和传统 For 循环中声明的索引变量,允许用保留类型名 var 代替清单类型

标识符 var 不是关键字,而是保留类型名称。 这意味着使用 var 用作变量、方法或包名称的代码不会受到影响; 使用 var 作为类或接口名称的代码则会受到影响(但这些名称在实践中很少见,因为它们违反了通常的命名约定)。

不允许缺少初始化器、声明多个变量、具有额外数组维括号或引用正在初始化的变量的局部变量声明形式。 未经初始化就拒绝局部变量会缩小特性的范围,避免“ 远距离操作 ”推断错误,并且在典型的程序中只排除一小部分局部变量。

“ var ”语法在有参数的lambda表达式使用:

Consumer<String> consumer = (var t) -> System.out.println(t.toLowerCase()+"-world");
consumer ==> $Lambda$15/0x00000008000b1c40@6093dd95
|  已创建 变量 consumer : Consumer<String>
 
consumer.accept("Hello");
hello-world