1. 引言
写 Java 代码时,我们经常要判断:某个方法收到 null 参数时,到底该抛 IllegalArgumentException
还是 NullPointerException
?这个问题看似小,但在实际开发中高频出现,处理不好容易踩坑。
本文就来深入聊聊这两种选择背后的逻辑,帮你建立清晰的判断标准。我们不搞教条主义,而是结合 Javadoc、JDK 源码习惯和实际调试体验,给出更接地气的建议。
2. 选择 IllegalArgumentException
先看支持抛 IllegalArgumentException
的理由。
下面是一个典型示例:当传入参数为 null 时,手动抛出 IllegalArgumentException
:
public void processSomethingNotNull(Object myParameter) {
if (myParameter == null) {
throw new IllegalArgumentException("Parameter 'myParameter' cannot be null");
}
// 正常处理逻辑
}
2.1. 符合 Javadoc 定义
查一下 IllegalArgumentException
的官方文档,它适用于“向方法传递了非法或不合适的参数值”。
✅ 如果你的方法明确要求参数不能为 null,那 null 就是“非法值”,抛 IllegalArgumentException
完全合理。
2.2. 更符合开发者调试直觉
想象你在看线上错误日志。如果看到 NullPointerException
,第一反应通常是:哪里空指针解引用了? 你会顺着堆栈往下找 obj.toString()
、obj.getXXX()
这类调用。
但如果你看到的是 IllegalArgumentException
,你会立刻意识到:是调用方传参出问题了。这时候你会直接看调用栈最外层是谁传了 null,定位更快。
✅ 换句话说,IllegalArgumentException
更贴近“参数校验失败”的语义,调试时能更快定位到错误源头。
2.3. 其他支持理由
- ✅ 一致性更好:如果你的方法还会校验其他非法值(比如长度为 0 的字符串、负数等),统一用
IllegalArgumentException
更一致。 - ⚠️ 有人认为“只有 JDK 才能抛
NullPointerException
”,这其实是个误区。Javadoc 并没有这种限制,后面我们也会看到 JDK 自己就在用。
3. 选择 NullPointerException
再来看支持 NullPointerException
的观点。
示例代码如下:
public void processSomethingElseNotNull(Object myParameter) {
if (myParameter == null) {
throw new NullPointerException("Parameter 'myParameter' cannot be null");
}
// 正常处理逻辑
}
3.1. 也符合 Javadoc 定义
根据 NullPointerException
的文档:
Thrown when an application attempts to use
null
in a case where an object is required.
翻译一下:当程序在需要对象的地方使用了 null,就应该抛这个异常。
✅ 方法参数声明的是引用类型(如 Object myParameter
),意味着“这里需要一个对象”,传 null 就违反了这个契约 —— 所以抛 NullPointerException
也没毛病。
3.2. 与 JDK API 保持一致
这是最关键的一点。
JDK 自己就这么干。来看几个例子:
- ✅
Objects.requireNonNull(T obj)
:传 null 直接抛NullPointerException
- ✅
ArrayList.addAll(int index, Collection c)
:index
越界 →IndexOutOfBoundsException
c
为 null →NullPointerException
注意:这里没有用 IllegalArgumentException
,而是用了更具体的 NullPointerException
。说明 JDK 认为 null 是一种特定的非法状态,值得单独用一个异常类型表示。
✅ 再看 Guava、Spring 等主流框架,也都倾向于在参数校验时抛 NullPointerException
,尤其是配合 Objects.requireNonNull()
时。
4. 结论与建议
这个问题没有绝对正确答案,但可以总结出以下几点实用建议:
✅ 推荐做法
场景 | 建议 |
---|---|
手动校验参数 null | **优先抛 NullPointerException **,与 JDK 保持一致 |
使用 Objects.requireNonNull() |
它本身抛 NPE ,别包装成 IAE |
其他非法参数(如空字符串、非法范围) | 用 IllegalArgumentException |
团队已有规范 | 务必保持项目内统一,别混用 |
✅ 最佳实践示例
public void serviceMethod(String name, Integer id) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
if (id == null) {
throw new NullPointerException("id cannot be null");
}
if (name.isEmpty()) {
throw new IllegalArgumentException("name cannot be empty");
}
if (id < 0) {
throw new IllegalArgumentException("id must be >= 0");
}
// 正常业务逻辑
}
或者更简洁地:
public void serviceMethod(String name, Integer id) {
Objects.requireNonNull(name, "name cannot be null");
Objects.requireNonNull(id, "id cannot be null");
// 其他校验...
}
✅ 调试友好提示
无论选哪个异常,一定要在异常信息里带上参数名,比如:
❌ "Parameter cannot be null"
✅ "Parameter 'name' cannot be null"
这样出问题时一眼就知道是哪个参数错了,省去查堆栈的时间。
示例代码已托管至 GitHub:https://github.com/baeldung/core-java-modules/tree/master/core-java-exceptions-3