自从2018年开始,Java采用了每六个月发布一次新版本的策略。这样的策略使得Java保持了新鲜感以及强劲的生命力,在这篇文章中,我将会为大家带来6个实用的Java新特性。

1. Optional类

NullPointerException是Java所有异常中最经典的一种。大家肯定对它不陌生,它经常出乎意料地出现,让人头疼不已。好在Java 8为我们引入了Optional类,Java 10完善了这一机制。

从本质上讲,Optional类允许你包装一个变量,然后使用包装器的方法来更简洁地处理Null的问题。

下面我们看一个对照的例子:

例1.1 未使用Optional类的空指针

public class MyClass {
    public static void main(String args[]) {
        InnerClass innerClass = null;
        // 这里对引用为null的变量进行了方法访问,产生一个NullPointerException
        System.out.println("innerClass = " + innerClass.getName());
    }
}

class InnerClass {
    String name = "";

    public String getName() {
        return this.name;
    }
}

复制代码

现在我们如何使用Optional来避免上面情况的出现呢?

Optional类有一个isPresent()方法,你可以拿它做一个if-check。但是我们由更简短的方法——ifPresent(),只有在值存在的情况下才会继续运行代码。

例1.2 使用Optional类的空指针

public class MyClass {
    public static void main(String args[]) {
        InnerClass innerClass = null;
        Optional fooWrapper = Optional.ofNullable(innerClass);
        fooWrapper.ifPresent(x -> System.out.println("innerClass = " + x.getName()));
    }
}

class InnerClass {
    String name = "";

    public InnerClass(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}
复制代码

当然,当你使用Optional类的时候,你还可以使用orElse()方法来调用一个默认值,或者考虑orElseGet()来提供一个函数引用。

2. Record类

DTO(Data Transfer Object)我们都很熟悉,通常用来对数据库、文件系统等存储的数据进行存储和传输。Java 14中引入了Record关键字,Java15在此基础上进行了改进。

例3.1展示了引入Record类之前的怎么定义一个DTO。

例3.1 一个简单的DTO

public class MyClass {
    public static void main(String args[]) {
        Student student = new Student("Jay", 18);
        // . . .
    }
}
class Student {
    String name;
    Integer age;
    public Student(String name, Integer age){
        this.name = name;
        this.age = age;
    }
    public String getName(){
        return this.name;
    }
    public Integer getAge(){
        return this.age;
    }
}
复制代码

我们可以看到上面的例子里,充斥着啰嗦的样板代码,我们现在可以通过Record关键字来达到简化DTO的目的。

例3.2 使用Record关键字

public class MyClass {
    public static void main(String args[]) {
      Student student = new Student("Jay", 18);

      // . . .
    }
}

public record Student(String name, Integer age) {}
复制代码

Record类型还定义了equals()、hashCode()和toString()的默认实现,同时也允许开发者重写这些实现。我们也可以提供一个自定义的构造函数。

另外,请注意Record类不能被子类化。

4. 新的字符串方法

在Java 10和Java 12中,增加了几个有用的新的String方法。除了字符串操作方法外,还引入了两种新的方法来简化文本文件访问。

4.1 Java 10中的新字符串方法

  • isBlank(): 如果字符串是空的,或者字符串只包含空格(包括Tab),则返回true。
  • lines(): 将一个字符串分割成一个字符串流,每个字符串包含一个行。每一行是由/r或/n或/r/n分隔开。

例4.1.1 lines()

import java.io.IOException;
import java.util.stream.Stream;
public class MyClass {
    public static void main(String args[]) throws IOException{
      String str = "test \ntest2 \n\rtest3 \r";
      Stream lines = str.lines();
      lines.forEach(System.out::println);
    }
}

/*
outputs:
test
test2
test3
*/
复制代码
  • strip(), stripLeading(), stripTrailing(): 分别从开头和结尾、仅开头和仅结尾删除空白。
  • repeat(int times): 返回一个字符串,该字符串取自原始字符串并重复指定的次数。
  • readString(): 从文件路径直接读取字符串。

例4.1.2 readString()

Path path = Path.of("test.txt"); 
String text = Files.readString(path);
复制代码
  • writeString(Path path): 将字符串直接写到指定路径的文件中。

4.2 Java 12中新字符串方法

  • indent(int level): 为每行字符串的开头增加或删除空格以调整字符串行的缩进。
  • transform(Function f): 将给定的lambda表达式应用于字符串。

5. Switch表达式

Java 12引入了新的switch表达式,允许在语句中内联使用switch。换句话说,switch表达式会返回一个值。

Java 13更进一步,引入了yield关键字。使用了yield关键字可以直接跳出当前switch块,返回一个值。

Java 14将新的switch表达式语法作为一个完整的函数。

我们看下新老Switch表达式之间的异同。

例5.1 古老的Java Switch

public class Test {
    public static void main(String args[]) {
        int size = 3;
        String message = "";

        switch (size) {
            case 1:
                message = "one";
                break;
            case 3:
                message = "three";
                break;
            default:
                message = "unknown";
                break;
        }

        System.out.println("The number is " + message);
    }

}
复制代码

在现在看来,这段代码相当啰嗦,看看我们是怎么简化它的。

例5.2 新的Java Switch

public class Test {
        public static void main(String args[]) {
            int size = 3;
            var message = switch (status) {
                case 1 -> "one";
                case 2 -> "two";
                case 3 -> "three";
                default -> {
                    System.out.println("closed");
                    yield "unknown";
                }
            };
            System.out.println("The number is " + message);
        }

}
复制代码

6. Text block

Java 13通过引入文本块解决了长期以来在Java中处理复杂文本字符串的烦恼。Java 14完善了这种支持。

像JSON、XML和SQL这样的东西会让你因为多层嵌套的转义发疯。在Java中,将HTML、XML、SQL或JSON的片段嵌入到一个字符串字面中,通常需要通过转义和连接进行大量的编辑,然后包含该片段的代码才能被编译。该片段通常难以阅读,维护起来也很费劲。

我们看下下面这个例子,其中新的文本块语法被用来创建一个JSON片段。

例6.1 使用文本块的JSON

class TextBlock { 
  public static void main(String args[]) {
    String json = """
      {
        "name" : "Jay",
        "age" : "18"
      }
    """;

    System.out.println(json);
  }
}
复制代码

在上面的例子中,没有看到一个转义字符,请注意三倍双引号的语法。

7. Sealed class —— 封闭类

Java 15引入了封闭类的概念。简而言之,新的sealed关键字允许你定义哪些类可以子类化一个接口。

封闭类的意义在于通过封闭类可以把可扩展性的限制在可以预测和控制的范围内,这为接口设计和实现提供了新的可能性;通过和型式测试的结合,许可类可以穷举,这使得我们可以更好地控制代码的安全性和健壮性。

例7.1 封闭类

public abstract sealed class Pet
    permits Cat, Dog, Bird {...}
复制代码

在上面这个例子,我们使用sealed关键字来指定哪些类被允许扩展Pet类。

原文链接:https://juejin.cn/post/7051348082826412039