Z
ZHANK
编程在线教程Java 教程空指针防御编程
异常处理

空指针防御编程

深入 NPE 成因、Optional 类、防御性编程技巧

空指针防御编程

NullPointerException 是 Java 最常见的异常——但也是最容易预防的。写出不会 NPE 的代码是一种思维习惯,而不是靠事后修 bug。

学完本章你将: 识别 NPE 的常见场景、用 Optional 优雅处理 null、建立防御策略。


NPE 的四种典型场景

java
// 1. 调用 null 的方法 —— 最经典
String s = null;
s.length();  // NPE

// 2. 自动拆箱 null —— 最隐蔽
Integer count = null;
int n = count;  // NPE!Integer → int 自动拆箱时调用 count.intValue()

// 3. 数组元素默认是 null
String[] names = new String[5];  // 5 个都是 null
names[0].toUpperCase();          // NPE

// 4. Map.get() 返回 null
Map<String, String> map = new HashMap<>();
String value = map.get("不存在的key"); // null
value.length(); // NPE

💡 核心原则:如果你拿不准一个值是不是 null,在用它之前先检查。


Optional —— 包装"可能存在也可能不存在"的值

Optional 是一个容器:要么装着一个值,要么空着。它强迫你思考"如果值是 null 怎么办",而不是忘记检查。

java
// ❌ 直接返回 null —— 调用者容易忘记检查
public User findUser(String id) { return null; }

// ✅ 返回 Optional —— 调用者必须处理"不存在"的情况
public Optional<User> findUser(String id) {
    User user = db.query(id);
    return Optional.ofNullable(user);
}
java
Optional<String> opt = findName(42); // 可能是空

// 有值时执行
opt.ifPresent(name -> System.out.println(name));

// 没值时给默认值
String name = opt.orElse("默认名称");
String name2 = opt.orElseGet(() -> loadFromCache()); // 延迟计算

// 没值时抛异常
String name3 = opt.orElseThrow(() ->
    new NotFoundException("用户不存在"));

// 链式处理
Optional<String> upper = opt
    .filter(s -> s.length() > 3)    // 长度不够 → 变空
    .map(String::toUpperCase);      // 有大写 → 转大写

⚠️ 不要滥用 Optional: 不要用它做字段类型或方法参数(序列化麻烦)。它最适合做方法返回值


防御策略清单

java
// ✅ 参数校验 —— 第一时间发现 null
public void process(String input) {
    Objects.requireNonNull(input, "input 不能为空");
}

// ✅ 返回空集合而不是 null
public List<String> getNames() {
    return names != null ? names : Collections.emptyList();
    // 调用者可以放心遍历,不用判空
}

// ✅ 使用注解表明 null 契约
public @Nullable User findByEmail(String email) { }  // 可能返回 null
public void update(@NotNull User user) { }            // 不接受 null

// ✅ 用 Objects.equals 替代 .equals()
Objects.equals(a, b);  // null 安全,不用写 a != null && a.equals(b)

// ✅ 级联访问用 Optional 链
String city = Optional.ofNullable(user)
    .map(User::getAddress)
    .map(Address::getCity)
    .orElse("未知");
// 清晰的 null 契约

}

// ✅ 方法链保护 String city = user.getAddress() != null ? user.getAddress().getCity() : "未知"; // 或 String city = Optional.ofNullable(user) .map(User::getAddress) .map(Address::getCity) .orElse("未知");