.

前语

随着SpringBoot 3.0 将Java 17 设置为最低版别 咱们应该明白 Java8的年代总有一天会过去。

而不再是”他发任他发,我用Java8″。

就像Java6是主流的时分,咱们以为Java8不会替代6相同,所以随着时刻的到来,Java17替代Java8也是必然的!

那么 就让咱们来回顾而且学习一下这些新特性吧!!!

Java 8要害特性回顾

Lambda表达式

Lambda表达式是Java编程言语中的一种特殊语法,用于描绘一个函数式接口。Lambda表达式由参数列表、箭头符号和一个表达式或句子块组成,其间参数列表指定了传递给Lambda表达式的参数,箭头符号”->”将参数列表和Lambda表达式的主体分开,主体能够是单个表达式或多个句子的句子块。

(parameter1, parameter2, ...) -> { expression }

或许:

(parameter1, parameter2, ...) -> statement

其间,parameter1, parameter2,…是Lambda表达式的参数列表,expression是Lambda表达式的主体,statement是Lambda表达式的主体(句子块)。

例如,下面是一个简略的Lambda表达式,它接纳两个整数作为参数,并回来它们之和:

(int x, int y) -> x + y

Lambda表达式能够在函数式接口的上下文中运用,例如:

interface MyFunctionalInterface {
    int calculate(int x, int y);
}
public class Main {
    public static void main(String[] args) {
        MyFunctionalInterface sum = (x, y) -> x + y;
        System.out.println(sum.calculate(10, 5));  // 输出 15
    }
}

在这个比如中,咱们界说了一个函数式接口MyFunctionalInterface,它有一个办法calculate,接纳两个int类型的参数并回来一个int类型的值。然后咱们运用Lambda表达式创立了一个MyFunctionalInterface类型的实例sum,它将两个参数相加。

请留意,在此示例中,咱们能够经过Lambda表达式来为MyFunctionalInterface接口的calculate办法供给完成。由于MyFunctionalInterface是一个函数式接口,因而咱们能够运用Lambda表达式来创立它的实例并完成其笼统办法。

关于匿名类创立的简化


例如,假如咱们有一个接口:

interface MyInterface {
    void doSomething();
}

在Java 8之前,咱们能够这样完成该接口:

MyInterface myInterface = new MyInterface() {
    @Override
    public void doSomething() {
        System.out.println("Doing something");
    }
};

经过运用Lambda表达式,咱们能够更简练地完成同样的功用:

MyInterface myInterface = () -> System.out.println("Doing something");

Lambda表达式的引进大大简化了代码,并使得Java中的函数式编程愈加易于运用和了解。

办法引证

Lambda 表达式还支持办法引证(Method Reference)的语法办法,其间 A::a 表明对类 A 的实例办法 a() 的引证。

办法引证能够简化 Lambda 表达式的书写,使代码愈加简练易懂。它能够用于任何可用于函数式接口的 Lambda 表达式上下文中。以下是一些运用办法引证的示例:

// 引证静态办法
Arrays.asList("apple", "banana", "orange").forEach(System.out::println);
// 引证实例办法
List<String> list = Arrays.asList("apple", "banana", "orange");
list.stream().map(String::toUpperCase).forEach(System.out::println);
// 引证结构办法
Supplier<List<String>> supplier = ArrayList::new;
List<String> list = supplier.get();

在第一个示例中,咱们运用了 System.out::println 办法引证来打印输出字符串列表中的每个元素。这相当于将以下 Lambda 表达式传递给 forEach() 办法:

s -> System.out.println(s)

在第二个示例中,咱们运用了 String::toUpperCase 办法引证来将字符串列表中的每个元素转化为大写字母。这相当于将以下 Lambda 表达式传递给 map() 办法:

s -> s.toUpperCase()

在第三个示例中,咱们运用了 ArrayList::new 结构办法引证来创立一个新的 ArrayList 实例。这相当于将以下 Lambda 表达式传递给 get() 办法:

() -> new ArrayList<String>()

留意,办法引证只是一个语法办法,本质上依然是 Lambda 表达式。因而,它们依然需求满意函数接口的界说,而且在运用时会被编译器转化为对应的函数式接口实例。

加深练习

  1. 对数组进行排序
// 运用Lambda表达式对整数数组进行排序
int[] numbers = {5, 2, 8, 1, 9};
Arrays.sort(numbers, (a, b) -> a - b);
System.out.println(Arrays.toString(numbers));
  1. 遍历集兼并打印元素
// 运用Lambda表达式遍历字符串列表并打印每个元素
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Dave");
names.forEach(name -> System.out.println(name));
  1. 在调集中查找满意条件的元素
// 运用Lambda表达式在字符串列表中查找长度大于等于5的第一个元素
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
String foundName = names.stream()
                        .filter(name -> name.length() >= 5)
                        .findFirst()
                        .orElse(null);
System.out.println(foundName);
  1. 将调集中的元素映射成新的调集
// 运用Lambda表达式将字符串列表中的每个元素转化为大写字母,并收集到一个新的列表中
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
List<String> upperCaseNames = names.stream()
                                    .map(name -> name.toUpperCase())
                                    .collect(Collectors.toList());
System.out.println(upperCaseNames);

Optional类

创立办法

of()、empty() 和 ofNullable() 都是用于创立 Optional 目标的办法。

  1. of() 办法:该办法承受一个非空的值,并回来一个包含该值的 Optional 目标。假如传入的值为 null,则会抛出 NullPointerException 反常。示例如下:
String value = "hello";
Optional<String> optional = Optional.of(value); // 创立包含 "hello"Optional 目标
  1. empty() 办法:该办法回来一个空的 Optional 目标,即该目标中没有任何值。能够直接运用该办法创立一个空的 Optional 目标,示例如下:
Optional<String> optional = Optional.empty(); // 创立一个空的 Optional 目标
  1. ofNullable() 办法:该办法承受一个能够为 null 的值,并回来一个包含该值的 Optional 目标。假如传入的值为 null,则回来一个空的 Optional 目标。示例如下:
String value = null;
Optional<String> optional = Optional.ofNullable(value); // 创立一个空的 Optional 目标

需求留意的是,在运用 get() 办法获取 Optional 目标中的值时,假如目标为空,那么会抛出 NoSuchElementException 反常。因而主张在运用 get() 办法之前先调用 isPresent() 办法判别目标是否存在。而假如只是想要取得默许值,能够运用 orElse() 或许 orElseGet() 办法来防止这种反常。

实践运用

Optional 类是 Java 8 中新增的一个类,它能够用于表明一个值或许存在,也或许不存在的状况。在运用Optional 类时,咱们能够经过 isPresent() 办法先判别值是否存在,假如存在则能够经过 get() 办法获取该值,不然能够履行一些默许操作或许抛出反常。

Optional<String> optional = Optional.ofNullable("hello");
if (optional.isPresent()) {
    String value = optional.get();
    System.out.println(value);
} else {
    System.out.println("value is null");
}

在这个示例中,咱们运用 ofNullable() 办法创立了一个 Optional 目标,并给它赋予了一个非空的值 “hello”。然后运用 isPresent() 办法检查值是否存在,假如存在,则运用 get() 办法获取该值并输出,不然输出 “value is null”。

需求留意的是,当 Optional 目标中的值为 null 时,调用 get() 办法将会抛出 NoSuchElementException 反常。因而,咱们能够经过 orElse() 或许 orElseGet() 办法来指定一个默许值,在值为 null 的状况下回来该默许值,示例如下:

Optional<String> optional = Optional.ofNullable(null);
String value = optional.orElse("default");
System.out.println(value);

在这个示例中,咱们创立了一个 Optional 目标并给它赋予了一个 null 值。然后经过 orElse() 办法指定了一个默许值 “default”,在值为 null 的状况下回来该默许值并输出。

运用场景

Optional 类的首要运用场景是解决空指针反常(NullPointerException)问题。在 Java 开发中,咱们经常会遇到需求判别一个值是否为 null 的状况,假如不加以处理,这样的代码很简略出现空指针反常,导致程序崩溃或许运转犯错。

经过运用 Optional 类,咱们能够明晰地表明某个值或许存在,也或许不存在的状况,然后防止了空指针反常的发生,并让代码愈加强健和可读性更高。

别的,Optional 类还有以下一些运用场景:

  1. 作为办法的回来值类型,表明该办法或许回来一个非空的值,也或许回来一个空值。
  2. 用作办法参数的类型,表明该参数能够承受一个非空的值,也能够承受一个空值。
  3. 用于防止分支句子中的重复代码。

例如,假定咱们要获取一个用户的姓名和年纪,而且在获取进程中需求判别两个字段是否为空。在没有运用 Optional 类的状况下,或许会写出以下代码:

public void processUser(User user) {
    if (user == null) {
        throw new IllegalArgumentException("user must not be null");
    }
    String name = user.getName();
    if (name == null || name.isEmpty()) {
        throw new IllegalArgumentException("user name must not be null or empty");
    }
    Integer age = user.getAge();
    if (age == null || age < 0) {
        throw new IllegalArgumentException("user age must not be null or negative");
    }
    // do something with the user information
}

假如运用 Optional 类来处理,能够将上述代码简化为如下办法:

public void processUser(User user) {
    Optional.ofNullable(user).orElseThrow(() -> new IllegalArgumentException("user must not be null"));
    String name = Optional.ofNullable(user.getName()).orElseThrow(() -> new IllegalArgumentException("user name must not be null or empty"));
    Integer age = Optional.ofNullable(user.getAge()).filter(a -> a >= 0).orElseThrow(() -> new IllegalArgumentException("user age must not be null or negative"));
    // do something with the user information
}

经过运用 Optional 类,咱们能够将对用户目标和它的属性是否为空的判别和默许操作简化为一行代码,而且可读性更高,减少了犯错的概率。

But!!!


在某些状况下,手动对字段进行校验或许愈加简略和可读性更高。例如,在代码中只涉及到一个或许两个字段的状况下,运用 Optional 类或许会显得过于繁琐。

但是,在处理杂乱的目标或许多个字段的状况下,运用 Optional 类能够使代码愈加简练、可读性更高,而且防止了很多重复的判别代码。此外,运用 Optional 类还有利于编写愈加强健的代码,防止了空指针反常等常见问题。

因而,运用 Optional 类是否更好,需求依据具体的状况来决定。在实践开发中,主张结合具体场景进行挑选,以达到最佳的代码效果。

Java 9新特性

模块化开发

即 Java Platform Module System (JPMS),它答应将一个运用程序或库拆分为多个独立的模块,然后更好地办理代码依靠和杂乱度。

在模块化开发中,每个模块都有自己的称号、版别以及依靠联系,这些信息都被记载在模块描绘文件(module-info.java)中。经过运用模块描绘文件,在编译时就能够检查模块之间的依靠联系,并保证一切需求的依靠项都已经加载。

在界说模块时,能够指定哪些包和类是公共的,哪些是私有的。这样能够使得模块之间的接口愈加明晰明晰,减少了对外暴露的接口数量,进步了代码的可保护性。

此外,Java 9 还引进了 jlink 东西,能够将运用程序打包成一个独立的履行文件,其间包含了运用程序所需的一切依靠项和运转时环境,然后简化了运用程序的布置和分发进程。

整体来说,Java 9 中的模块化体系是一种新的代码安排办法,它能够协助咱们更好地办理代码依靠和杂乱度,进步了代码的可保护性和可重用性,一同也使得代码愈加明晰明晰。

怎么运用

首先,咱们需求在模块的根目录下创立一个 module-info.java 文件,并界说该模块的称号和依靠联系。例如,咱们创立了一个名为模块 mymodule 的模块,它依靠于 Java 的标准库模块 java.base

module mymodule {
    requires java.base;
}

接着,咱们能够在该模块中界说一些公共的类和接口,以及供给给其他模块运用的服务。例如,咱们在 mymodule 模块中界说了一个名为 MyService 的接口和一个完成该接口的类 MyServiceImpl

package com.example.mymodule;
public interface MyService {
    void sayHello();
}
package com.example.mymodule;
public class MyServiceImpl implements MyService {
    public void sayHello() {
        System.out.println("Hello from MyService!");
    }
}

最终,咱们能够在另一个模块中运用 mymodule 模块供给的服务。例如,咱们在一个名为 app 的模块中运用 MyService 接口来输出一条消息:

package com.example.app;
import com.example.mymodule.MyService;
import java.util.ServiceLoader;
public class MyApp {
    public static void main(String[] args) {
        ServiceLoader<MyService> loader = ServiceLoader.load(MyService.class);
        for (MyService service : loader) {
            service.sayHello();
        }
    }
}

在这个示例中,咱们运用 ServiceLoader 类来加载 mymodule 模块中的 MyService 接口完成类,并输出一条消息。

需求留意的是,为了能够编译和运转这个示例,咱们还需求运用 java 指令的 -p 参数来指定模块途径,以及指定运用程序的主模块。例如,在指令行上履行以下指令:

javac -d out --module-source-path src -m mymodule
javac -d out --module-source-path src -m app
java -p out -m app/com.example.app.MyApp

这样就能够成功地编译和运转该示例了。

四种模块化开发

Java 9 中供给了四种模块类型,别离是:

java.base:这是一切Java模块的基础,包含了Java运转时体系中的基本类和资源文件。

java.se:Java标准版模块,它包含了大多数的Java SE API。

java.xml.bind:Java XML绑定模块,包含了Java体系结构界说言语(XML)和Java目标之间的转化东西。

jdk.management:JDK办理模块,供给了JMX(Java办理扩展)API的完成,用于办理Java运用程序的运转时状况。

除此之外,Java 9 还供给了其他许多模块,例如 java.sql, java.logging 等。每个模块都有自己的姓名、版别号和依靠联系,这使得Java运用程序的开发、布置和保护愈加牢靠和可控。

效果与效果

模块化开发能够供给以下效果和效果:

  1. 更好的可保护性:经过将代码安排成逻辑上彼此独立、责任明晰的模块,使得代码结构愈加明晰,易于了解和保护。
  2. 更好的可重用性:模块能够被其他运用程序或库重复运用,然后防止了重复编写相同功用的代码。
  3. 更好的可扩展性:新增一个功用时,只需求添加对应的模块,而不需求修正运用程序的其余部分。这极大地减少了代码变更对整个运用程序的影响。
  4. 更好的安全性:Java 9 引进了模块体系,能够界说模块之间的依靠联系和拜访权限,然后有效地操控运用程序的拜访权限,进步安全性。
  5. 更好的功用:由于模块之间明晰了依靠联系,JVM 能够进行更准确的优化,进步运用程序的功用。
  6. 更好的易用性:模块体系能够主动办理依靠联系,供给了一种更简略的办法来处理类途径问题,使得运用程序的布置愈加简略。

总归,模块化开发是一种愈加规范、可控的办法来安排代码,它能够进步代码的可保护性、可重用性、可扩展性和安全性,一同也能够进步运用程序的功用和易用性。

JShell交互式编程

Java 9 中引进了 JShell,它是一个交互式的 Java 编程东西。运用 JShell,能够在不用创立类或办法的状况下,直接输入和履行 Java 代码,一同还供给了主动补全、过错提示等功用,使得编写和测验 Java 代码愈加简略便利。

JShell 能够经过指令行或许 IDE 插件的办法进行运用。以下是一些常用的 JShell 指令

/help:显现一切可用的 JShell 指令。

/vars:显现当前一切已界说的变量。

/methods:显现当前一切已界说的办法。

/imports:显现当前一切已导入的包和类。

/edit:修正指定行号的代码。

/save:将当前 JShell 会话保存到文件中。

/reset:重置 JShell,并铲除一切已界说的变量和办法。

以下是一个简略的示例:

以下是JShell 的运用场景

  1. 快速原型开发:运用 JShell 能够快速地测验各种 Java 代码片段,然后更快地原型开发和调试。
  2. 学习和教育:JShell 是一个交互式的环境,能够协助初学者更好地了解和学习 Java 编程言语。
  3. 调试和测验:运用 JShell 能够便利地进行代码片段的调试和测验,然后更快地定位和解决代码问题。
  4. API 探索:开发人员能够运用 JShell 来探索各种 Java API 和库的运用办法。
  5. 快速验证主意:JShell 能够协助开发人员快速验证其主意,而且能够在很多重复的代码之间节省时刻。

总归,JShell 能够协助开发人员更高效地编写和测验 Java 代码,然后加速开发速度,进步开发功率。

private()办法

在办法声明前加上 private 要害字来创立私有办法。这意味着只有在同一类中的其他办法才能够拜访该办法,而无法从另一个类中进行拜访。

下面是一个示例:

public class MyClass {
    public void publicMethod() {
        // 能够调用 privateMethod
        privateMethod();
    }
    private void privateMethod() {
        // 只能被 MyClass 中的其他办法调用
    }
}

在上面的代码中,privateMethod 办法只能被 MyClass 类中的其他办法调用,而不能从其他类中直接拜访它。

期望这能够协助你了解怎么在Java 9中创立私有办法。

工厂办法

调集类新增了一些工厂办法来创立不行变的调集目标。这些办法回来的调集目标是只读的,而且具有更好的功用和更小的内存占用。

这些工厂办法在实践业务处理中具有以下几个效果:

  1. 简化代码:运用调集工厂办法能够让代码愈加简练明晰。经过这些办法创立调集实例不需求手动遍历和添加元素,而是直接将元素以参数办法传递给办法,然后使代码愈加简练。
  2. 安全性:由于调集工厂办法回来的调集实例是不行变的,因而它们比手动创立的调集实例愈加安全。这些调集实例不能被修正或被其他线程篡改,因而能够保证数据的一致性。
  3. 功用:在创立小型调集时,运用调集工厂办法一般比手动创立调集实例更快。这是由于这些办法运用了内部优化技能,能够更快地创立出调集实例。
  4. 可读性:运用调集工厂办法能够使代码愈加可读性强,当阅览代码时,咱们很简略看出这儿创立了一个不行变的调集实例,而不需求剖析杂乱的代码来了解调集怎么初始化。

总归,Java 9 中的调集工厂办法能够协助咱们愈加轻松、简练地创立调集实例,进步了代码的可读性和安全性,而且在处理小型调集时具有更好的功用。

// 创立一个不行变的 List 实例,其间包含 "apple", "banana", "orange" 三个元素
List<String> fruits = List.of("apple", "banana", "orange");
// 创立一个不行变的 Set 实例,其间包含 "red", "green", "blue" 三个元素
Set<String> colors = Set.of("red", "green", "blue");
// 创立一个不行变的 Map 实例,其间包含两个键值对 ("key1", "value1"), ("key2", "value2")
Map<String, String> map = Map.of("key1", "value1", "key2", "value2");

改善的Stream() API

Java 9 在 Stream API 中引进了一些改善,包含:

ofNullable() 办法:这个办法答应咱们创立一个或许为空的 Stream 目标,防止运用 null 值时出现空指针反常。

takeWhile() 和 dropWhile() 办法:这两个办法别离回来契合和不契合指定条件的元素,遇到第一个不契合条件的元素后就中止履行流操作。

iterate() 办法的重载版别:新增了一个能够设置流大小上限的 iterate() 办法,防止了无限制生成流的问题。

ofNullable() 和 iterate() 办法的优化:Java 9 对这两个办法进行了优化,进步了它们的功用。

toArray() 办法的重载版别:toArray() 办法现在能够承受一个 IntFunction 参数,用于创立指定类型和长度的新数组。

Optional 类的 stream() 办法:Optional 类现在也支持经过 stream() 办法创立一个 Stream 目标,这样能够便利地将 Optional 目标转化为 Stream 目标进行操作。

这些改善使得 Stream API 愈加易于运用和灵敏,一同进步了功用和功率。

Java 10新特性

var局部变量类型揣度

Java 10 引进了局部变量类型揣度功用,也称为 “var” 类型。这项新功用答应开发人员在不显式指定变量类型的状况下声明局部变量,而是运用要害字 “var” 来替代。

例如,运用 var 界说一个字符串变量:

var message = "Hello, World!";

编译器会主动揣度出变量类型为 String,并生成相应的字节码。能够经过运用 getClass() 办法来验证变量类型:

System.out.println(message.getClass()); // 输出:class java.lang.String

留意,var 只能用于局部变量的界说,不能用于实例变量、静态变量、办法参数或回来值等。一同,还需求留意防止过度运用 var,坚持代码的可读性和明晰性。

总归,局部变量类型揣度使得 Java 代码愈加简练、易读和安全,能够进步开发功率和减少犯错概率。

Java 11新特性

用于lambda的形参局部变量

在 Java 11 中,能够在 lambda 表达式的形参列表中运用 var 要害字来声明局部变量,然后能够在 lambda 表达式中运用这些变量。举个比如,假定有一个 List 类型的调集,想要过滤掉长度大于等于 5 的字符串并输出剩余的字符串,能够运用以下代码:

List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
list.stream()
    .filter(s -> {
        var length = s.length();
        return length < 5;
    })
    .forEach(System.out::println);

在这个比如中,运用 var 要害字声明晰一个名为 length 的局部变量,并将其赋值为字符串 s 的长度。然后经过 filter() 办法过滤出长度小于 5 的字符串,并经过 forEach() 办法输出剩余的字符串。

需求留意的是,var 要害字只能用于局部变量的声明,不能用于成员变量、办法参数和回来值类型的声明。此外,在运用 var 声明局部变量时,有必要进行初始化,由于编译器需求揣度出变量的类型。假如不进行初始化,则会导致编译过错。

针对String类的办法增强

Java 11 对 String 类进行了一些增强,包含但不限于:

isBlank() 办法:用于检查一个字符串是否为空或仅由空格字符组成,回来值为 boolean 类型。

lines() 办法:用于将一个字符串按行分割成多个子串并回来一个 Stream 目标。

repeat(int count) 办法:用于重复一个字符串 count 次并回来新的字符串。

strip() 和 stripLeading()、stripTrailing() 办法:用于去除字符串两端的空格(包含 Unicode 中的空格字符),别离回来去除左边空格、右侧空格和两边空格后的新字符串。

这些增强使得 String 类具有更好的易用性和灵敏性,能够更便利地处理字符串相关的业务逻辑。

全新的HttpClient API

以下是一个运用 Java 11 HttpClient API 发送 GET 恳求并解析呼应的示例代码:

public class HttpClientDemo {
    public static void main(String[] args) throws Exception {
        // 创立 HttpClient 目标
        HttpClient client = HttpClient.newHttpClient();
        // 创立恳求目标
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://www.baidu.com/"))
                .build();
        // 发送恳求并获取呼应
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        // 输出呼应成果
        System.out.println(response.body());
    }
}

在这段代码中,咱们首先创立了一个 HttpClient 目标,然后运用它来发送一个 GET 恳求,恳求的地址为 www.baidu.com/。咱们运用 HttpRequest 类来构建恳求目标,并调用 HttpClient 的 send() 办法来发送恳求并获取呼应。最终,咱们从呼应中获取状况码和呼应体,并将它们输出到操控台上。

值得留意的是,这儿咱们运用了 HttpResponse.BodyHandlers.ofString() 来指定呼应体的处理办法为字符串类型。还有其他一些处理办法可供挑选,例如将呼应体处理为字节数组、文件、流等。别的,在实践开发中,咱们还能够对恳求进行更多的配置,例如设置恳求头、恳求体、署理等。

Java12-16新特性

Switch表达式

Java 12 中引进了新的 switch 表达式语法,能够愈加简练和灵敏地编写 switch 句子。Java 12 到 Java 16 版别都对 switch 表达式做出了一些增强和改善。

在运用 switch 表达式时,能够省掉掉惯例的 break 和 case 要害字,而且能够将多个 case 分支兼并成一个,如下所示:

int num = 2;
String result = switch (num) {
    case 1 -> "One";
    case 2, 3 -> "Two or Three";
    case 4 -> "Four";
    default -> "None";
};
System.out.println(result);

这段代码中,咱们运用新的 switch 表达式语法对变量 num 进行判别,并依据不同的状况回来不同的字符串成果。能够看到,咱们运用箭头符号 -> 来衔接 case 分支和相应的回来值,一同能够将多个 case 分支兼并成一个,并运用逗号来分隔多个条件。最终,将表达式的成果赋值给变量 result。

除此之外,Java 13、Java 14 和 Java 15 都为 switch 表达式做出了一些增强,例如能够添加 yield 句子、运用 lambda 表达式等。需求留意的是,在运用 switch 表达式时,要保证一切的分支都有回来值,而且回来值类型有必要一致。

文本块

Java 12 到 16 之间添加了文本块(Text Blocks)这一新特性,用于更便利地编写多行字符串。

在运用文本块时,能够运用三个双引号 “”” 来表明一个多行的字符串。例如:

String html = """
              <html>
                  <body>
                      <p>Hello, world!</p>
                  </body>
              </html>
              """;

运用文本块时,不需求添加额定的换行符或转义字符,由于一切的空白符(包含换行符)都会保留在生成的字符串中。这使得多行字符串的编写变得愈加直观和易读。

此外,还能够运用嵌入式表达式(Embedded Expressions)来将变量或表达式插入到文本块中。嵌入式表达式由一个美元符号和花括号组成,如下所示:

String name = "John";
int age = 30;
String message = """
                 Name: ${name}
                 Age: ${age}
                 """;

在这个比如中,name 和 age 变量的值将会被嵌入到 message 字符串中。

这些文本块的新特性使得 Java 中多行字符串的创立和处理愈加简略和高雅。

轻松搞懂java8-17新特性

新的instanceof句子

Java 14 引进了一种新的 instanceof 语法,称为“形式匹配 instanceof”,它能够使代码愈加简练、易读和类型安全。

在传统的 instanceof 中,咱们需求将目标强制转化为特定的类型,而且运用该类型来履行操作。示例如下:

if (obj instanceof String) {
    String str = (String) obj;
    System.out.println(str.toUpperCase());
}

在 Java 14 中,能够运用“形式变量”(Pattern Variable)来完成此操作,语法为 instanceof 后跟一个类型和一个变量名,如下所示:

if (obj instanceof String str) {
    System.out.println(str.toUpperCase());
}

在这个比如中,假如 obj 是 String 类型的实例,就会将其转化为 str 变量,然后运用它履行操作。经过这种办法,能够防止运用不必要的类型转化,愈加直观地表达意图,并添加类型安全性。

此外,在 Java 16 中,对形式匹配 instanceof 进行了一些改善,使其愈加灵敏和易用。例如,在 switch 表达式中也能够运用形式匹配 instanceof,如下所示:

Object obj = "hello";
String result = switch(obj) {
    case String str && str.length() > 5 -> "long string";
    case String str -> "short string";
    case Integer i -> "integer";
    default -> "unknown";
};
System.out.println(result); // 输出:short string

在这个比如中,咱们运用形式变量 str 和 && 运算符来对字符串进行特定的匹配。这使得 switch 表达式愈加灵敏和强壮,能够处理更杂乱的数据类型和条件。

空指针反常改善

在 Java 14 中,引进了对空指针反常的改善,称为“可空性注解”(Nullability Annotations)。该特性答应开发人员在代码中运用注解来指示哪些变量、参数或回来值能够为空,哪些不能够为空。这有助于编译器更好地检测潜在的空指针反常,并在编译时供给更好的过错信息。

具体地说,Java 14 引进了两个新的可空性注解:@Nullable 和 @NotNull。@Nullable 注解表明被注解的元素能够为 null,而 @NotNull 则表明被注解的元素不该该为 null。

例如,假定咱们要界说一个办法来接纳一个字符串参数,而且期望保证该参数不为 null。咱们能够在办法签名上运用 @NotNull 注解,如下所示:

public void processMessage(@NotNull String message) {
    // ...
}

在这个比如中,假如调用者测验将 null 值传递给该办法,编译器将会提出正告。

除了 @Nullable 和 @NotNull 注解之外,还有一些其他的注解能够用来指定可空性,例如 @NonNullApi 和 @NonNullFields。这些注解能够用于整个包或类,以指示它们的成员或字段是否应该默许为非空或可空。

这些可空性注解能够使代码愈加明晰、易读和类型安全,特别是在处理大型代码库或与其他团队成员协作时。但是,需求留意的是,这些注解并不会主动防止空指针反常,开发人员依然需求慎重地编写代码以防止这种状况的发生。

记载类型

Java 12-16 的记载类型是一项新特性,它为Java引进了一种便利的办法来声明不行变的数据类。以下是该特性的一些关键:

  1. 记载类型运用要害字“record”进行声明,能够看作是一种轻量级的类界说办法。
  2. 记载类型的属功用够经过结构函数或许主动生成的getter办法进行拜访,这些办法会主动依据属性称号生成。
  3. 记载类型中的属性默许是final和private的,而且不能被修正,因而记载类型是不行变的。
  4. 记载类型还支持equals()、hashCode()和toString()等标准办法的主动生成,这样能够更便利地进行目标比较和打印。
  5. 记载类型能够承继其他类或接口,而且也能够完成接口,然后具备更多的灵敏性。
  6. 记载类型供给了一个便利的办法来快速创立目标,能够直接运用表达式进行初始化。
  7. 记载类型还支持局部声明,能够将其嵌套在其他类或办法内部,然后愈加灵敏地运用该特性。

总归,记载类型是一项十分有用的新特性,它能够使Java代码愈加简练、易于了解和保护。

运用

咱们能够创立一个简略的Person类,用于存储人员信息:

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

这儿运用了要害字record声明晰一个名为Person的记载类型,并界说了两个属性name和age。由于运用了要害字record,所以这个类是不行变的,也就是说,这两个属性不能在其他当地修正。

这个记载类型会主动生成以下内容:

结构函数,用于初始化目标。

主动完成了equals()、hashCode()和toString()办法,便利进行目标比较和打印。

主动完成了getter办法,用于获取属性值。

咱们能够创立一个Person目标并输出其信息:

Person person = new Person("Tom", 25);
System.out.println(person.name());
System.out.println(person.age());
System.out.println(person);

将会输出以下成果:

Tom
25
Person[name=Tom, age=25]

能够看到,记载类型的确很简练便利,能够让代码愈加易于了解和保护。

java 17新特性

密封类型

Java 17 供给了一项新特性——密封类型。这是一种限制类承继的机制,它答应程序员界说一个密封类,该类只能被一组已知的子类承继。这个特功用够让程序员愈加严格地操控类型承继,然后使代码愈加安全、强健和易于保护。

以下是对Java 17密封类型的一些关键:

  1. 密封类运用要害字sealed进行声明,能够看作是一种特殊的笼统类界说办法。
  2. 密封类能够显式声明答应承继的子类,这些子类需求运用要害字permits进行声明,例如: sealed class Shape permits Circle, Rectangle, Triangle {}
  3. 密封类的直接子类有必要是预界说的子类或许在密封类中明晰声明的子类,不然编译器将报错。
  4. 密封类的结构函数默许是私有的,而且不能被外部调用,因而密封类是不行实例化的。
  5. 密封类能够界说笼统办法,也能够重写父类的非笼统办法。
  6. 密封类能够与switch表达式一同运用,以完成愈加明晰和安全的代码逻辑。

总归,密封类型是一项十分有用的新特性,它能够让程序员在类型承继方面拥有更多的操控权,并使代码愈加安稳和易于保护。

下面是一个示例,演示了怎么运用Java 17中的密封类型:

sealed interface Shape permits Circle, Rectangle, Triangle {
    // Shape接口,声明晰答应的子类为Circle、Rectangle和Triangle
    double area();
}
final class Circle implements Shape {
    // Circle完成了Shape接口
    private final double radius;
    public Circle(double radius) {
        this.radius = radius;
    }
    public double area() {
        return Math.PI * radius * radius;  // 计算圆的面积
    }
}
final class Rectangle implements Shape {
    // Rectangle完成了Shape接口
    private final double width;
    private final double height;
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    public double area() {
        return width * height;  // 计算矩形的面积
    }
}
final class Triangle implements Shape {
    // Triangle完成了Shape接口
    private final double base;
    private final double height;
    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }
    public double area() {
        return 0.5 * base * height;  // 计算三角形的面积
    }
}

能够看到,这个示例运用了密封类型来限制Shape接口的可承继子类,并别离界说了三个子类Circle、Rectangle和Triangle,它们都完成了Shape接口,并供给了特定的area()办法用于计算各自的面积。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。