如何在 Java 中检查 Character 对象是否为 null

JavaBeginner
立即练习

介绍

在本次实验中,我们将探讨在使用 Java 的 Character 包装类时如何处理潜在的 null 值。与基本数据类型 char 不同,Character 对象可以为 null,若不处理这种情况,可能会导致 NullPointerException 错误。我们将学习如何验证 Character 对象是否为 null,将空值检查与其他字符属性检查相结合,并利用 Optional 类进行更安全的空值处理。

通过实际示例,你将获得编写健壮 Java 代码的实践经验,这些代码能够有效管理空的 Character 对象,避免常见的运行时错误,提高应用程序的可靠性。

验证 Character 包装类是否为 null

在这一步中,我们将探讨在使用 Java 的 Character 包装类时如何处理潜在的 null 值。基本数据类型 char 不能为 null,但 Character 包装类作为一个对象,是可以为 null 的。处理 null 值对于防止 NullPointerException 错误至关重要,这种错误在 Java 中很常见,可能会导致你的程序崩溃。

我们将从创建一个简单的 Java 程序开始,该程序演示了当你尝试对一个为 null 的 Character 对象调用方法时,NullPointerException 是如何发生的。

  1. 如果 HelloJava.java 文件尚未在 WebIDE 编辑器中打开,请打开它。

  2. 将文件的全部内容替换为以下代码:

    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = null;
    
            // 如果 myChar 为 null,这一行将导致 NullPointerException
            // System.out.println("Is myChar a letter? " + Character.isLetter(myChar));
        }
    }
    

    在这段代码中:

    • 我们声明了一个名为 myChar 的 Character 变量,并将其显式地设置为 null。
    • 注释掉的代码行 System.out.println("Is myChar a letter? " + Character.isLetter(myChar)); 试图使用一个 null 参数调用静态方法 Character.isLetter()。虽然 Character.isLetter() 是一个静态方法,但向其传递一个 null 的 Character 对象仍然会导致 NullPointerException,因为该方法在内部会尝试将 Character 对象拆箱为其基本数据类型 char 值,而对于 null 来说这是不可能的。
  3. 保存文件(Ctrl + S 或 Cmd + S)。

  4. 现在,让我们来编译这个程序。打开 WebIDE 底部的终端并运行:

    javac HelloJava.java
    

    如果编译成功,你应该看不到任何输出。

  5. 现在,让我们尝试运行这个程序。在终端中运行:

    java HelloJava
    

    由于会导致错误的那一行代码被注释掉了,程序将运行且不会有任何输出或错误。这表明仅仅将一个 Character 声明为 null 并不会立即引发问题;问题出在你试图对其执行操作时。

在下一步中,我们将取消注释有问题的那一行代码,并观察 NullPointerException。

结合 null 检查和字母检查

在上一步中,我们了解了在处理 Character 对象时 NullPointerException 是如何发生的。现在,让我们取消注释导致错误的那一行代码,看看异常是如何出现的。然后,我们将学习一种常见的方法,通过将空值检查与字母检查相结合来避免这种情况。

  1. 在 WebIDE 编辑器中打开 HelloJava.java 文件。

  2. 取消调用 Character.isLetter() 那一行代码的注释。现在你的代码应该如下所示:

    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = null;
    
            // 如果 myChar 为 null,这一行将导致 NullPointerException
            System.out.println("Is myChar a letter? " + Character.isLetter(myChar));
        }
    }
    
  3. 保存文件(Ctrl + S 或 Cmd + S)。

  4. 在终端中编译修改后的程序:

    javac HelloJava.java
    

    同样,如果编译成功,你应该看不到任何输出。

  5. 现在,运行程序:

    java HelloJava
    

    你应该会看到类似以下的输出,表明出现了 NullPointerException:

    Exception in thread "main" java.lang.NullPointerException
        at java.base/java.lang.Character.isLetter(Character.java:xxxx)
        at HelloJava.main(HelloJava.java:x)
    

    这个错误的出现是因为 myChar 为 null,而 Character.isLetter() 方法无法对 null 对象进行操作。

  6. 为了避免这个 NullPointerException,我们可以在调用 Character.isLetter() 之前添加一个检查,看看 myChar 是否为 null。我们可以使用 if 语句来实现这一点。修改你的 HelloJava.java 文件,加入这个检查:

    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = null;
    
            if (myChar != null && Character.isLetter(myChar)) {
                System.out.println("myChar is a letter.");
            } else {
                System.out.println("myChar is not a letter or is null.");
            }
        }
    }
    

    在这个更新后的代码中:

    • 我们使用了一个 if 语句,其中两个条件通过 &&(逻辑与运算符)组合在一起。
    • 第一个条件 myChar != null 检查 myChar 是否 不为 null。
    • 第二个条件 Character.isLetter(myChar) 检查 myChar 是否为字母。
    • && 运算符是“短路”的。这意味着如果第一个条件(myChar != null)为假,第二个条件(Character.isLetter(myChar))不会被计算。这就避免了 NullPointerException,因为我们只有在 myChar 不为 null 时才会尝试调用 Character.isLetter()。
  7. 保存文件。

  8. 再次编译程序:

    javac HelloJava.java
    
  9. 运行程序:

    java HelloJava
    

    这次,程序应该会无错误运行,并输出:

    myChar is not a letter or is null.
    

    这是因为 myChar 为 null,所以 if 语句中的第一个条件(myChar != null)为假,else 块被执行。

在访问对象的方法或属性之前检查 null,这种方法是 Java 中避免 NullPointerException 的基本技巧。

使用 Optional 实现空值安全

在上一步中,我们学习了如何通过在使用对象之前显式检查对象是否为 null 来防止 NullPointerException。虽然这种方法很有效,但有时会导致代码中充斥着大量的空值检查。Java 8 引入了 Optional 类,以一种更具函数式和表现力的方式来处理可能为 null 的值。

Optional 是一个容器对象,它可能包含也可能不包含非空值。如果存在值,isPresent() 方法返回 true,get() 方法返回该值。如果不存在值,则该对象被视为空,isPresent() 方法返回 false。对空的 Optional 对象调用 get() 方法会抛出 NoSuchElementException。

让我们重构我们的示例,使用 Optional<Character> 来处理 Character 可能为 null 的情况。

  1. 在 WebIDE 编辑器中打开 HelloJava.java 文件。

  2. 将文件的全部内容替换为以下代码:

    import java.util.Optional;
    
    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = null; // 仍然可能为 null
    
            // 从可能为 null 的 Character 创建一个 Optional
            Optional<Character> optionalChar = Optional.ofNullable(myChar);
    
            // 使用 Optional 方法检查和处理值
            if (optionalChar.isPresent() && Character.isLetter(optionalChar.get())) {
                 System.out.println("myChar is a letter.");
            } else {
                 System.out.println("myChar is not a letter or is null.");
            }
    
            // 另一种使用 Optional 函数式方法的方式(更高级)
            // optionalChar.filter(Character::isLetter)
            //             .ifPresentOrElse(
            //                 c -> System.out.println("myChar is a letter (using Optional methods)."),
            //                 () -> System.out.println("myChar is not a letter or is null (using Optional methods).")
            //             );
        }
    }
    

    在这段代码中:

    • 我们导入了 Optional 类。
    • 我们仍然将 myChar 声明为可能为 null。
    • Optional<Character> optionalChar = Optional.ofNullable(myChar); 创建了一个 Optional 对象。Optional.ofNullable() 用于处理值可能为 null 的情况。如果 myChar 为 null,optionalChar 将是一个空的 Optional。如果 myChar 有值,optionalChar 将包含该值。
    • 然后,我们使用 optionalChar.isPresent() 检查 Optional 是否包含值,然后再使用 optionalChar.get() 获取该值并将其传递给 Character.isLetter()。这与我们之前的空值检查类似,但使用了 Optional API。
    • 注释掉的部分展示了一种更高级的使用 Optional 的方式,使用了 filter 和 ifPresentOrElse 等函数式方法,在某些场景下可以使代码更简洁。在这个入门实验中,我们不会关注这种高级用法,但了解一下是有好处的。
  3. 保存文件。

  4. 编译程序:

    javac HelloJava.java
    
  5. 运行程序:

    java HelloJava
    

    输出应该与上一步相同:

    myChar is not a letter or is null.
    

    这证实了使用 Optional.ofNullable() 和 isPresent() 可以正确处理空值情况。

现在,让我们将 myChar 改为一个非空字符,看看程序的行为。

  1. 修改 HelloJava.java 文件,将 myChar 设置为一个字符,例如 'A':

    import java.util.Optional;
    
    public class HelloJava {
        public static void main(String[] args) {
            Character myChar = 'A'; // 现在 myChar 有值了
    
            // 从可能为 null 的 Character 创建一个 Optional
            Optional<Character> optionalChar = Optional.ofNullable(myChar);
    
            // 使用 Optional 方法检查和处理值
            if (optionalChar.isPresent() && Character.isLetter(optionalChar.get())) {
                 System.out.println("myChar is a letter.");
            } else {
                 System.out.println("myChar is not a letter or is null.");
            }
        }
    }
    
  2. 保存文件。

  3. 编译程序:

    javac HelloJava.java
    
  4. 运行程序:

    java HelloJava
    

    这次,输出应该是:

    myChar is a letter.
    

    这表明当 myChar 有值时,optionalChar.isPresent() 返回 true,并且 Character.isLetter() 检查能正确执行。

使用 Optional 可以使你的代码更易读,并明确指出值可能缺失的情况,从而降低意外出现 NullPointerException 的可能性。

总结

在这个实验中,我们学习了在 Java 里使用 Character 包装类时如何处理可能出现的 null 值。我们首先展示了在 null 的 Character 对象上调用方法(即使是像 Character.isLetter() 这样的静态方法)时,NullPointerException 是如何产生的。这凸显了在对 Character 对象执行操作之前显式检查 null 的重要性,以防止程序崩溃。