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"