Java 8 Optional类使用教程:告别空指针异常的终极指南

在java中,经常会出现的bug是空指针异常。这通常程序员没有正确地进行空值检查,或者在程序的某个地方出现了预期之外的情况。
以下是程序中经常发生的空指针问题:

  1. 未初始化的对象引用:如果一个对象没有被初始化,即没有使用new关键字为其分配内存空间,而直接对其进行方法调用或属性访问,就会抛出空指针异常。例如,声明了一个字符串String str;而没有进行初始化,直接调用str.length()会引发空指针异常。
  2. 返回值为null的方法:当一个方法返回null,而调用者没有对返回值进行判断,直接对其进行方法调用或属性访问,也会抛出空指针异常。例如,如果getName()方法返回null,而后续代码中直接调用name.length(),则会导致空指针异常。
  3. 数据库中的null值:如果从数据库查询的结果中含有null值,而在代码中没有进行适当的处理,直接使用这些可能为null的值进行操作,也可能引发空指针异常。这种情况下,可以在SQL查询语句中使用ifnull(列名,‘’)来处理null值,或者在获取数据后进行判空处理。
  4. 数组未初始化:声明了一个数组但没有对其进行初始化,如int[] arr;,然后直接访问其长度arr.length,这同样会导致空指针异常。
  5. 接口类型的对象未初始化:如果声明了一个接口类型的对象但没有用具体的类进行初始化,如List lt;,这样的对象是null,任何对其的方法调用都会引发空指针异常。正确的做法是使用具体的实现类进行初始化,如List lt = new ArrayList();

Optional类介绍

Optional类的出现主要是为了解决在Java编程中普遍存在的空指针异常问题。在Java 8之前,空指针异常通常是由于在访问对象的方法或属性时,没有进行适当的null检查而导致的。Optional类迫使开发者明确地处理可能为空的情况,从而避免潜在的空指针异常风险。此外,Optional类也体现了Java在函数式编程方面的一步进化,它鼓励开发者编写更加清晰和安全的代码。通过使用Optional,可以更好地指导开发者如何处理可能为空的值,尤其是在编写公共API或处理集合和数组时可能出现的null值情况。

创建对象

  • Optional.empty():创建一个空的Optional对象。
  • Optional.of(value):创建一个包含非空值的Optional对象。
  • Optional.ofNullable(value):创建一个Optional对象,允许值为null。
    一般使用:
public static void main(String[] args) {
        User user = null;
        System.out.println(Optional.ofNullable(user).orElse(new User().setId("2")));
    }

直接使用of方法会报npe,从jdk源码中我们得知:ofNullable只是多了null的判断,根据是否为空,返回前两者:

 public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

  
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

判断Optional是否有值

  • isPresent():如果Optional对象包含值,则返回true;否则返回false。
  • ifPresent(Consumer action):如果Optional对象有值,则执行给定的动作。
    存在则输出:
 public static void main(String[] args) {
        // 创建一个包含值的Optional对象
        Optional<String> optionalValue = Optional.of("Hello, World!");

        // 使用ifPresent方法打印值(如果存在)
        optionalValue.ifPresent(value -> System.out.println("Value is: " + value));
    }

以下不会执行,因为值为空:

 public static void main(String[] args) {
        User user = null;
        Optional<User> optionalValue = Optional.ofNullable(user);
        optionalValue.ifPresent(value -> System.out.println("Value is: " + value.getId()));
    }

获取Optional的值

  • get():如果Optional对象有值,则返回该值;否则抛出NoSuchElementException异常。
    以下将抛出NoSuchElementException:
public static void main(String[] args) {
        User user = null;
        User optionalValue = Optional.ofNullable(user).get();
        System.out.println(optionalValue);
    }
  • orElse(T other):如果有值则返回该值,否则返回指定的other对象。
  • orElseGet(Supplier other):与orElse类似,但other参数是一个Supplier接口的实现,用于在需要时提供一个默认值。
 public static void main(String[] args) {
        User user = null;
        User optionalValue = Optional.ofNullable(user).orElseGet(()->new User().setId("1"));
        System.out.println(optionalValue);
    }

转换

  • map(Function mapper):对Optional中的值进行映射转换。如果Optional对象有值,则对其执行调用mapper函数得到返回值。如果返回值不为null,则创建包含mapper函数返回值的Optional作为map方法返回值,否则返回空Optional。
    以下代码将返回指定的ID值:
public static void main(String[] args) {
        User user = new User().setId("1");
        String id = Optional.ofNullable(user).map(User::getId).get();
        System.out.println(id);
    }
  • flatMap(Function> mapper):与map方法类似,但返回值是Optional类型。

peek

用于在不改变Optional对象值的情况下,对其中的值执行某种操作。这个方法接收一个Consumer接口的实现作为参数,当Optional对象包含值时,会对该值执行Consumer的accept方法。

peek方法主要用于在处理Optional对象的值之前或之后执行一些额外的操作,例如日志记录、性能监控等。它不会改变Optional对象的值,也不会返回一个新的Optional对象,而是直接返回原始的Optional对象本身。

public static void main(String[] args) {
        // 创建一个包含值的Optional对象
        Optional<String> optionalValue = Optional.of("Hello, World!");

        // 使用peek方法打印值(如果存在)
        Optional<String> peekResult = optionalValue.peek(value -> System.out.println("Value: " + value));
}

优点

  1. 减少空指针异常:通过明确地表明一个变量可能为空,Optional有助于避免在运行时发生空指针异常。
  2. 提高代码可读性:使用Optional可以使代码的意图更加清晰,因为它强制开发者显式地处理可能的null值。
  3. 简化数据校验:Optional提供了一系列的方法,如isPresent、orElse和map,这些方法可以帮助开发者更简洁地进行数据校验和处理。
  4. 函数式编程支持:Optional支持函数式编程范式,允许链式调用和延迟执行,这有助于编写更简洁的代码。

缺点

  1. 性能开销:虽然Optional提供了许多有用的方法,但它也引入了一些性能开销,因为它是一个包装类。
  2. 可能导致滥用:Optional有时可能被滥用,导致代码变得不必要地复杂。例如,在某些情况下,简单的null检查可能比使用Optional更直接有效。

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