null 的安全操作 vs 危险操作
1. 完全安全的操作(不会NPE)
| 操作类型 | 示例代码 | 说明 |
|---|---|---|
| 单纯赋值 | ListNode a = null; | 直接赋null值完全合法 |
| 引用传递 | headB = headB.next; | 即使headB.next为null也安全 |
| 判等比较 | if(headB == null) | 判断是否为null不会引发NPE |
| 方法内参数传递 | someMethod(headB.next); | 仅传递引用,不访问成员 |
2. 会引发NPE的危险操作
| 操作类型 | 示例代码 | 触发条件 |
|---|---|---|
| 访问成员变量 | int val = headB.val; | headB为null时 |
| 调用方法 | headB.toString(); | headB为null时 |
| 链式属性访问 | int val = headB.next.val; | headB或headB.next为null |
| 数组访问 | int[] arr = null; arr[0] = 1; | arr为null时 |
关键区分原则
-
仅操作引用本身(安全):
-
赋值、传参、比较等操作只涉及引用变量本身,不访问对象内部
-
示例:
ListNode a = null; // 安全 ListNode b = a; // 安全(b也变为null) if(a == null) {...} // 安全
-
-
访问引用指向的对象(危险):
-
任何试图通过引用访问对象成员(字段/方法)的操作
-
示例:
a.val; // 危险!若a为null则NPE a.next.val; // 双重危险!若a或a.next为null则NPE
-
链表操作中的典型场景
✅ 安全操作(无需判空)
// 移动指针(即使.next返回null)
while(current != null) {current = current.next; // 安全!
}
❌ 危险操作(必须判空)
// 访问节点值
if(current.next.val > 0) { // 需要先检查current.next!// ...
}// 正确写法
if(current.next != null && current.next.val > 0) {// ...
}
特殊案例解析
案例1:方法调用中的null传递
void process(ListNode node) {System.out.println(node); // 打印null是安全的System.out.println(node.val); // NPE!
}process(headB.next); // 仅传递引用,不立即报错
案例2:三目运算符的短路特性
// 安全写法(等效于if-else) ListNode next = (current != null) ? current.next : null;
记忆技巧
-
"点号"警示原则:
-
看到
.(如obj.xxx)就要警惕可能NPE -
除非
.前面是class/static成员(如Math.PI)
-
-
操作类型速查表:
操作符/语法 是否可能NPE 示例 =❌ a = b==/!=❌ if(a == null)instanceof❌ if(a instanceof X).(成员访问)✅ a.val[](数组访问)✅ arr[0]
终极总结
-
安全区:所有不涉及访问对象内部的操作(赋值、比较、传参)
-
危险区:任何试图通过引用访问对象数据的操作(字段、方法、数组)
-
链表编程口诀:
"移动指针不需慌,访问数据要验null"
