首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 每日一題:枚舉 switch 空指針陷阱與編譯期檢查機制深度解析

     編輯:Java 綜合教學

題目代碼

package Dec2016;

public class Ques1206 {
    public static void main(String[] args) {
        doSex(null);
    }
    
    public static void doSex(Sex sex) {
        switch (sex) {
            case Boy:
                System.out.println("男的");
                break;
            case Girl:
                System.out.println("女的");
                break;
            default:
                System.out.println("出錯");
                break;
        }
    }
}

enum Sex {
    Boy("男"), Girl("女");
    private String sex;
    Sex(String sex) { this.sex = sex; }
}

核心問題:能否正常運行輸出結果?

答案:運行時拋出 NullPointerException,無法正常輸出。

詳細執行流程分析

  1. main 呼叫 doSex(null),參數 sex 為 null
  2. 進入 switch (sex) 表達式求值
  3. JLS 14.11 規定:switch 表達式為引用類型時,若值為 null → 拋出 NullPointerException
  4. 異常在 switch 行拋出,不進入任何 case/default 分支
  5. 程式異常終止,控制台輸出堆疊追蹤

編譯期 vs 運行期行為對比

場景編譯期運行期
switch 表達式為 null✅ 編譯通過❌ 拋出 NPE
case 常量非枚舉成員❌ 編譯錯誤-
缺少 default 分支✅ 編譯通過 (枚舉 switch 不強制)匹配不到 → 無動作
枚舉新增成員未更新 switch✅ 編譯通過⚠️ 走 default 或無匹配

修正方案

方案 1:參數非空檢查 (推薦)

public static void doSex(Sex sex) {
    if (sex == null) {
        System.out.println("出錯:性別不能為空");
        return;
    }
    switch (sex) {
        case Boy: System.out.println("男的"); break;
        case Girl: System.out.println("女的"); break;
        default: System.out.println("出錯:未知性別"); // 防禦性編程
    }
}

方案 2:使用 Objects.requireNonNull

public static void doSex(Sex sex) {
    Objects.requireNonNull(sex, "性別不能為空");
    // switch 邏輯...
}

方案 3:Java 12+ Switch 表達式 (隱式非空檢查)

// Java 12+ switch 表達式仍會對 null 拋出 NPE,但語法更簡潔
public static String doSex(Sex sex) {
    return switch (sex) {
        case Boy -> "男的";
        case Girl -> "女的";
        default -> throw new IllegalArgumentException("未知性別: " + sex);
    };
}

枚舉 switch 最佳實踐清單

  1. 入參非空斷言:公開 API 必須在 switch 前檢查 null
  2. 保留 default 分支:防禦性處理未來新增枚舉值
  3. 考慮密封類:Java 17+ sealed interface + permits 編譯期窮盡性檢查
  4. 避免業務邏輯直接依賴枚舉順序:使用 ordinal() 易破壞

延伸知識:編譯器生成的 switch 枚舉映射

// 反編譯後類似結構
int index = sex == null ? -1 : sex.ordinal(); // null 檢查在這裡
switch (index) {
    case 0: // Boy
    case 1: // Girl
    default:
}

可見 null 檢查發生在 ordinal() 呼叫前,直接導致 NPE。

相關面試追問

  1. if (sex == Sex.Boy) 配合 null 如何表現? → 安全,null == Enum 為 false
  2. 枚舉實現介面時 switch 行為有變化? → 無變化,仍針對枚舉常量
  3. EnumSet / EnumMap 如何處理 null? → EnumMap key 為 null 拋 NPE,EnumSet 不允許 null 元素
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved