P01 · 空指针 NullPointerException:几乎每个 Java 程序员都踩过的坑
场景还原:你写了一个"按 id 查用户"的方法,数据库里刚好没有这条记录,方法返回了 null。调用方拿到这个 null 想都没想就 .name 取名字——“啪”,程序当场崩了,终端上留下一行刺眼的红字:NullPointerException。
这大概是 Java 世界里出现频率最高、也最让人又爱又恨的异常:它好懂,但架不住天天见。更气人的是,代码量大以后,你根本不知道是哪个"没判空"埋的雷。
一、问题现象
运行下面的代码,程序直接崩溃:
Exception in thread "main" java.lang.NullPointerException: Cannot read field "name" because "<local1>" is null
at NpeDemo.main(NpeDemo.java:19)

二、出错代码
public class NpeDemo {
static class User {
String name;
Address addr;
}
static class Address {
String city;
}
// 模拟"查不到用户"——数据库没这条记录/缓存为空时经常返回 null
static User queryUser(int id) {
return null;
}
public static void main(String[] args) {
User u = queryUser(1);
// 最常见的翻车点:拿到对象后没判 null 直接往下用
System.out.println("用户名: " + u.name); // ← 这里炸了
}
}
三、为什么会这样
一句话:null 上什么都没有,你却非要问它要东西。
u 是 null 时,u.name 等价于"对一个不存在的对象取属性",Java 只能抛 NullPointerException 告诉你"我做不到"。
注意 JDK 14 之后报错信息变得更贴心了,会直接告诉你是哪个引用是 null(because "<local1>" is null),定位比以前容易多了。
常见的"null 从哪来":
- 数据库/接口查无记录,返回了 null;
- Map.get(key) 键不存在,返回 null;
- 链式调用的中间某一步返回了 null,比如 user.getAddress().getCity(),getAddress() 返回 null 就崩;
- 第三方库/老代码的返回值没写"可能为 null"的注释。
四、怎么解决
第一层:源头堵——不确定有没有值的方法,要么返回 Optional,要么明确告诉调用方"可能为 null":
// 返回 Optional,调用方被迫考虑"没有值"的情况
static Optional<User> queryUser(int id) {
// 查不到就返回 Optional.empty()
return Optional.empty();
}
public static void main(String[] args) {
User u = queryUser(1).orElse(null);
if (u == null) { // 判空后再用
System.out.println("用户不存在,走兜底逻辑");
return;
}
System.out.println("用户名: " + u.name);
}
第二层:用的时候判——凡是"别人给你的对象",用之前先想一句"它可能是 null 吗":
String city = (u != null && u.addr != null) ? u.addr.city : "未知";
第三层:能自动挡就自动挡——Spring 等框架里可以加 @NotNull / @Nullable 注解,编译期插件能帮你提前发现一半问题。
五、防坑总结
- 方法返回值可能是 null 时,要么文档注明,要么返回 Optional;
- 调用外部方法/接口返回的对象前,先判空;
- 链式调用(a.getB().getC())每层都可能空,拆开判或封装安全取值方法;
- 集合用 getOrDefault、Map 查询用 containsKey 先判断;
- 一个方法里 NPE 修完,顺手看看同类写法还有没有(复制粘贴是 NPE 的温床)。
下一篇:P02 · 序列化失败 NotSerializableException






