null是JVM规定的特殊字面量,表示引用未关联堆中对象;它非对象、非未定义,仅适用于引用类型,赋值给基本类型非法,未初始化局部变量编译报错,成员变量默认为null。

Java 中对象为 null,表示该引用变量当前不指向任何实际对象,它指向的是“虚无”——内存中一个明确的空地址(通常为 0),不是对象,也不是未初始化的随机值。
为什么 null 不是对象,也不是“没定义”?
Java 的引用变量本质是存放对象内存地址的变量。null 是 JVM 规定的一个特殊字面量,专门用来表示“这个引用此刻没有关联任何堆内存中的对象”。它和 undefined(JavaScript)或未声明变量完全不同:
-
null只能赋给引用类型(String、List、自定义类等),不能赋给基本类型(int、boolean等) - 声明但未初始化的局部引用变量(如
String s;)在使用前必须显式赋值,否则编译报错:variable s might not have been initialized - 而类字段(成员变量)即使不显式赋值,也会被 JVM 自动初始化为
null(引用类型默认值)
NullPointerException 怎么来的?哪些操作会触发?
当代码试图对一个值为 null 的引用执行“需要真实对象支撑”的操作时,JVM 就抛出 NullPointerException。常见场景包括:
- 调用实例方法:
s.length()(若s == null) - 访问实例字段:
user.name(若user == null) - 调用
toString()、hashCode()、equals()等继承自Object的方法 - 数组访问:
arr[0](若arr == null) - 同步块:
synchronized(obj) { ... }(若obj == null)
注意:静态方法调用(String.valueOf())、instanceof 判断(obj instanceof String)、以及 Java 14+ 的 switch 表达式对 null 的处理,不会直接抛 NPE。
立即学习“Java免费学习笔记(深入)”;
怎么安全地判断和处理 null?
别靠“感觉”,要用明确、可读、符合上下文语义的方式:
- 优先用
Objects.isNull()或Objects.nonNull()(JDK 7+),语义清晰且避免手误写成= - 集合/字符串操作尽量用工具类:
StringUtils.isEmpty(s)、CollectionUtils.isEmpty(list)(Apache Commons),它们内部已处理null安全 - 构造函数/方法入参校验推荐用
Objects.requireNonNull(param, "param must not be null"),提前失败比隐藏 bug 更可控 - Java 8+ 可考虑
Optional包装可能为空的返回值(如Optional),但不要滥用在字段或参数上findUser(id)
示例:
if (Objects.nonNull(user) && "admin".equals(user.getRole())) {
grantAdminAccess();
}
容易被忽略的细节:自动拆箱和泛型擦除会让 null 更危险
这两个机制会让 null 在看似安全的地方突然爆发:
- 自动拆箱:把
Integer i = null;传给需要int的方法(如Math.max(i, 0)),会在运行时触发 NPE,因为 JVM 尝试调用i.intValue() - 泛型擦除:方法签名
在运行时无法知道T getFirst(List list) T是什么类型,如果返回null,调用方强转(如(String) getFirst(list))不会报错,但后续使用时才暴露问题 - 流操作:
list.stream().map(String::toUpperCase).collect(...)中,若list里有null元素,String::toUpperCase会立刻炸
真正难缠的 null,往往藏在层层封装之后,等你调用一个普通方法时才浮现——所以防御性编程不是过度检查,而是清楚每个接口契约是否允许 null。











