NPE 兩大典型成因
- 引用空對象:調用
null.method() 或訪問 null.field
- 拆箱空值:將
Integer 等包裝類 null 賦給基本類型 int
分層防禦策略
1. 外部依賴返回值 —— 嚴格驗證
對第三方 API、數據庫查詢、工作流引擎等不可控來源:
// 集合類
if (CollectionUtils.isNotEmpty(list)) { ... }
if (MapUtils.isNotEmpty(map)) { ... }
// 字符串
if (StringUtils.isNotBlank(str)) { ... } // 同時檢查 null、""、空白字符
// Optional 模式 (Java 8+)
Optional.ofNullable(externalService.getData())
.ifPresent(data -> process(data));
2. 內部創建對象 —— 契約式設計
- 構造器/工廠方法中對必填參數做
Objects.requireNonNull()
- 集合字段初始化為空集合而非
null:private List<Item> items = new ArrayList<>();
- 對外暴露不可變視圖:
return Collections.unmodifiableList(items);
3. Web 表單綁定對象 —— 框架層面處理
Spring MVC 等框架將空字符串綁定為 null 而非 ""。建議:
- DTO 字段用
@NotBlank、@NotNull 校驗註解
- 全局異常處理器統一返回 400 錯誤,避免 NPE 冒泡到 500
4. 工具類優於原生 API
| 操作 | 原生風險 | 推薦替代 |
| 字符串分割 | "".split(",") 返回 [""] | StringUtils.split(str, ",") 返回 [] |
| 集合判空 | list.size() > 0 拋 NPE | CollectionUtils.isNotEmpty(list) |
| 對象比較 | a.equals(b) 拋 NPE | Objects.equals(a, b) |
關於性能的迷思
「過多判空影響性能」是微優化誤區。一次方法調用納秒級開銷,遠小於 NPE 導致的業務中斷、日志分析、應急修復成本。**可讀性與穩定性優先**,熱點路徑經 JIT 優化後幾乎零開銷。
現代 Java 最佳實踐
// 1. 參數校驗
public void save(UserDTO dto) {
Objects.requireNonNull(dto, "用戶數據不能為空");
String name = dto.getName();
if (StringUtils.isBlank(name)) throw new IllegalArgumentException("姓名必填");
...
}
// 2. Optional 鏈式處理
public String getCityName(Long userId) {
return userRepo.findById(userId)
.map(User::getAddress)
.map(Address::getCity)
.orElse("未知城市");
}
// 3. Record 不可變數據載體 (Java 14+)
public record OrderDTO(@NotNull Long id, @NotBlank String item) {}
建立「不信任任何外部輸入、內部契約自證明、善用標準庫工具」的編碼習慣,可從根源上消除 90% 以上的 NPE。
- 上一篇文章: MySQL 遠程連接配置全攻略:權限授予與防火牆開放
- 下一篇文章: Java 遞歸遍歷目錄樹:從基礎實現到生產級優化
Copyright ©
程式碼之家 All Rights Reserved