多年业务开发经验总结-Java空指针异常常见的20个场景

首页 编程分享 PHP丨JAVA丨OTHER 正文

后端程序员Aska 转载 编程分享 2025-03-01 22:04:20

简介 多年业务开发经验总结Java空指针 多年业务开发经验总结一篇关于 Java 空指针异常(NullPointerException,简称 NPE)隐藏最深的场景,这对于 Java 开发者特别有帮助!


说明

多年业务开发经验总结一篇关于 Java 空指针异常(NullPointerException,简称 NPE)隐藏最深的场景,这对于 Java 开发者特别有帮助!空指针异常通常是在不小心引用了 null 对象时发生的,在一些潜在的地方,我们可能没有及时捕捉到异常,导致它隐藏得比较深。下面我会帮你列出一些常见的但容易忽视的场景,并附上一些代码示例

1. 自动拆箱(Auto-unboxing)

当你尝试将 null 值拆箱为基本数据类型时,Java 会抛出空指针异常。

Integer number = null;
int n = number;  // 会抛出 NullPointerException

解释Integer 是一个包装类,null 是无效的,拆箱时会抛出空指针异常。

2. 方法链调用

多个方法链调用时,如果其中某个方法返回了 null,后续方法调用会抛出空指针异常。

class Person {
    private Address address;
    
    public Address getAddress() {
        return address;
    }
}

class Address {
    private String street;
    
    public String getStreet() {
        return street;
    }
}

Person person = null;
String street = person.getAddress().getStreet();  // 会抛出 NullPointerException

解释personnull,直接调用 getAddress() 会导致空指针异常。

3. 集合操作中的空指针异常

当操作集合时,某个元素为 null,对该元素调用方法时也会引发空指针异常。

List<String> list = new ArrayList<>();
list.add(null);
int length = list.get(0).length();  // 会抛出 NullPointerException

解释:当集合中包含 null 值时,直接对其调用方法会导致异常。

4. 遍历集合 出现 null

集合List支持 foreach 遍历,但是如果List变量为null,则一定会发生空指针异常 NPE。如下代码所示,当ids为空时,会发生NPE。

正确做法,在遍历List之前,一定要进行空值检查。

List<Integer> ids = extractFromRequest();
//如果ids为 null,则发生 NPE
for(Integer id: ids){
   //do something
}

5. Stream 操作中的空指针异常

在使用 Java 8 的 Stream API 时,如果流中的某个元素为 null,而后续操作对其进行方法调用时,会导致空指针异常。

List<String> list = Arrays.asList("a", "b", null, "d");
list.stream().map(String::length).collect(Collectors.toList());  // 会抛出 NullPointerException

解释map 操作中,null 元素无法调用 length 方法。

6. Null 引用作为方法参数

某些方法接收了一个 null 值参数,却没有进行 null 校验,导致后续对该参数的操作抛出空指针异常。

public void printName(String name) {
    System.out.println(name.length());  // 没有 null 检查
}

printName(null);  // 会抛出 NullPointerException

解释:在方法内直接调用 name.length(),而 namenull,导致异常。

7. 多线程环境中的共享变量

多个线程共享同一个变量,并且有可能其中某个线程将该变量设置为 null,导致其他线程出现空指针异常。

class SharedResource {
    private String message;

    public void setMessage(String message) {
        this.message = message;
    }

    public void printMessage() {
        System.out.println(message.length());  // 可能会抛出 NullPointerException
    }
}

SharedResource resource = new SharedResource();
resource.setMessage(null);
resource.printMessage();  // 会抛出 NullPointerException

解释:线程安全问题,可能导致 message 在某些情况下为 null

8. Optional 的使用不当

Optional 是 Java 8 引入的用于避免空指针异常的工具,但不正确使用时仍然会抛出空指针异常。

Optional<String> name = Optional.ofNullable(null);
int length = name.get().length();  // 会抛出 NoSuchElementException,实际上是包装了空指针异常

解释Optional.get()Optionalnull 时,会抛出异常。

9. Java Reflection API

使用反射时,如果你尝试访问一个为 null 的对象字段或方法,也会引发空指针异常。

import java.lang.reflect.Method;

class Person {
    private String name;
}

Person person = null;
Method method = Person.class.getMethod("getName");
String name = (String) method.invoke(person);  // 会抛出 NullPointerException

解释:反射操作中的对象为 null,直接调用方法会抛出空指针异常。

10. JSON 解析中的空指针异常

使用 JSON 序列化与反序列化时,如果解析出的对象为 null,而后续操作尝试访问其属性,便会引发空指针异常。

import com.fasterxml.jackson.databind.ObjectMapper;

class Person {
    private String name;
}

String json = "{}";  // JSON 字符串没有包含 name 字段
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(json, Person.class);
String name = person.name.toLowerCase();  // 会抛出 NullPointerException

解释:反序列化出来的对象属性为 null,对 null 调用方法抛出异常。

11. 打印日志使用 + 号出现 null

日志打印时,很多人习惯使用 + 加号拼接日志,这种情况可能导致 空指针异常。

正确做法:使用{}占位符方式,打印变量。 日志框架会进行判空,当变量出现 Null 时,不会出现空指针。

很多人在 review 代码时,不重视日志代码,容易忽略日志代码中的问题。曾经有个同事搞出的线上问题就是因为日志打印出现了 NPE。

一定要敬畏每一行代码,包括日志代码。

12. String 操作中的空指针

如果 String 对象为 null,而我们调用其方法(例如 length()charAt() 等),会导致空指针异常。

String str = null;
int len = str.length();  // 会抛出 NullPointerException

String str2 = null;
System.out.println(str2.toString());  // 会抛出 NullPointerException

解释strnull,直接调用 length() 方法会抛出异常。

解释:当对象为 null 时,调用其 toString() 方法就会触发异常。

13. 使用 new 运算符与 null

有时候我们可能误以为通过 new 运算符实例化一个对象就一定有效,但如果对象已经是 null,某些操作会导致异常。

String[] array = new String[5];
array[0] = null;
System.out.println(array[0].length());  // 会抛出 NullPointerException

解释:虽然数组被创建,但元素是 null,对 null 元素调用方法会引发异常。

14. Lambda 表达式中的空指针

在 Java 8 的 Lambda 表达式中,若方法引用或函数式接口参数为 null,调用时可能会导致空指针异常。

List<String> list = Arrays.asList("a", "b", null);
list.forEach(s -> System.out.println(s.length()));  // 会抛出 NullPointerException

解释forEach 方法尝试调用 null 元素的 length() 方法时会出错。

15. 容器(如 Map、Set)中的空指针异常

在使用容器类(如 MapSet)时,如果尝试访问不存在的元素或将 null 放入容器,可能会导致空指针异常,尤其是在后续操作时。

Map<String, String> map = new HashMap<>();
map.put("key", null);
String value = map.get("key").toLowerCase();  // 会抛出 NullPointerException

解释:虽然 key 存在,但 null 值不能调用方法,导致空指针异常。

16. 集合和流中的空指针异常

Java 8 的 Stream API 操作中,元素本身为 null 时,会抛出空指针异常。

List<String> list = Arrays.asList("one", "two", null);
list.stream().filter(s -> s.length() > 3).collect(Collectors.toList());  // 会抛出 NullPointerException

解释:过滤操作中,null 值不能调用 length() 方法。

17. 内部类(或匿名类)引用外部类字段为 null

当外部类的字段是 null,而内部类或匿名类试图访问该字段时,也会导致空指针异常。

class Outer {
    private String name;

    public void createInner() {
        Inner inner = new Inner();
        inner.printName();  // 会抛出 NullPointerException
    }

    class Inner {
        public void printName() {
            System.out.println(name.length());  // 外部类的字段是 null
        }
    }
}

Outer outer = new Outer();
outer.createInner();  // 会抛出 NullPointerException

解释:外部类的字段 namenull,内部类访问时会出错。

18. 带有 null 参数的递归方法

递归方法中,如果传递的参数为 null,且方法没有对其进行有效的 null 检查,则可能抛出空指针异常。

public void printLength(String str) {
    if (str == null) return;
    System.out.println(str.length());
    printLength(str.substring(1));  // 如果 str 是 null,会抛出异常
}

printLength(null);  // 会抛出 NullPointerException

解释:方法没有判断 str 是否为 null,会引发异常。

19. 数据库操作中的空指针异常

在与数据库交互时,如果从数据库返回的结果集为 null,且代码没有进行检查,直接访问字段或属性时会抛出空指针异常。

ResultSet rs = getResultSetFromDatabase();
String name = rs.getString("name").toLowerCase();  // 如果 rs.getString 返回 null,则会抛出 NullPointerException

解释:数据库返回的 namenull,调用 toLowerCase() 时会抛出空指针异常。

20. 不当的 equals 方法实现

自定义类的 equals 方法中,如果未正确处理 null 值,可能会导致空指针异常。

class Person {
    private String name;

    public boolean equals(Person other) {
        return this.name.equals(other.name);  // 没有处理其他为 null 的情况
    }
}

Person p1 = new Person();
Person p2 = null;
p1.equals(p2);  // 会抛出 NullPointerException

解释othernull,调用 this.name.equals(other.name) 会导致空指针异常。

结尾建议:

这些场景展示了空指针异常在不同情况下的潜在发生点。你可以通过以下方式避免这些问题:

  • 及时进行 null 检查。
  • 使用 Optional 类型代替直接使用 null
  • 使用 Objects.requireNonNull 来确保传入的参数不为 null
  • 在合适的地方使用断言、try-catch 块等来捕获异常并做处理
  • 另外,可以强调如何通过单元测试或集成测试来提前发现这类问题

转载链接:https://juejin.cn/post/7475929962172694538


Tags:


本篇评论 —— 揽流光,涤眉霜,清露烈酒一口话苍茫。


    声明:参照站内规则,不文明言论将会删除,谢谢合作。


      最新评论




ABOUT ME

Blogger:袅袅牧童 | Arkin

Ido:PHP攻城狮

WeChat:nnmutong

Email:nnmutong@icloud.com

标签云