最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java入門(mén)異常處理最佳實(shí)踐

 更新時(shí)間:2026年03月30日 15:43:43   作者:我愛(ài)學(xué)習(xí)yyds  
本文詳細(xì)介紹了Java異常處理的核心概念、體系結(jié)構(gòu)、處理方式、執(zhí)行流程,并提供了實(shí)現(xiàn)自定義異常的方法和開(kāi)發(fā)中的最佳實(shí)踐,感興趣的朋友跟隨小編一起看看吧

在 Java 開(kāi)發(fā)的道路上,異常處理是繞不開(kāi)的核心知識(shí)點(diǎn)。無(wú)論是新手調(diào)試代碼時(shí)遇到的NullPointerException,還是開(kāi)發(fā)企業(yè)級(jí)項(xiàng)目時(shí)處理的網(wǎng)絡(luò)、文件 IO 異常,掌握規(guī)范的異常處理方式,能讓我們的代碼更健壯、更易維護(hù),還能大幅降低線上問(wèn)題的排查成本。本文將從異常的核心概念出發(fā),逐步拆解 Java 異常體系、處理方式、執(zhí)行流程,最后手把手教你實(shí)現(xiàn)自定義異常,讓你徹底吃透 Java 異常處理。

一、什么是異常?打破程序的正常流程

異常是程序運(yùn)行過(guò)程中發(fā)生的非正常行為 / 錯(cuò)誤狀態(tài),它會(huì)打斷程序的正常執(zhí)行流程,需要開(kāi)發(fā)者通過(guò)特定方式進(jìn)行處理。

在開(kāi)發(fā)中,我們無(wú)法避免各類(lèi)異常場(chǎng)景:比如除數(shù)為 0 的算術(shù)錯(cuò)誤、訪問(wèn)數(shù)組不存在的下標(biāo)、調(diào)用空對(duì)象的方法、讀取不存在的文件、網(wǎng)絡(luò)請(qǐng)求超時(shí)等。這些場(chǎng)景無(wú)法通過(guò)普通的邏輯判斷完全規(guī)避,而 Java 為每一種異常場(chǎng)景都提供了對(duì)應(yīng)的類(lèi)來(lái)描述,讓我們能精準(zhǔn)定位和處理問(wèn)題。

常見(jiàn)異常示例

/**
 * 常見(jiàn)運(yùn)行時(shí)異常演示
 */
public class CommonExceptionDemo {
    public static void main(String[] args) {
        // 1. 算術(shù)異常:ArithmeticException
        int a = 10;
        int b = 0;
        // System.out.println(a / b);
        // 2. 數(shù)組越界異常:ArrayIndexOutOfBoundsException
        String[] strArr = {"Java", "Python", "C++"};
        // System.out.println(strArr[5]);
        // 3. 空指針異常:NullPointerException
        String str = null;
        // System.out.println(str.length());
        // 4. 類(lèi)型轉(zhuǎn)換異常:ClassCastException
        Object obj = new Integer(100);
        // System.out.println((String) obj);
    }
}

取消上述代碼的注釋?zhuān)\(yùn)行后會(huì)看到控制臺(tái)拋出對(duì)應(yīng)的異常信息,包含異常類(lèi)型、異常原因和出錯(cuò)的代碼行,這也是 Java 異常給我們的核心調(diào)試線索。

二、Java 異常體系結(jié)構(gòu):理清繼承關(guān)系,不混淆概念

Java 為了對(duì)不同類(lèi)型的異常和錯(cuò)誤進(jìn)行分類(lèi)管理,設(shè)計(jì)了一套清晰的異常體系,其頂層類(lèi)是java.lang.Throwable,所有異常和錯(cuò)誤都直接或間接繼承自該類(lèi)。

核心繼承結(jié)構(gòu)

Throwable
├─ Error:JVM級(jí)別的嚴(yán)重錯(cuò)誤,無(wú)法通過(guò)代碼處理
│  ├─ StackOverflowError:棧溢出錯(cuò)誤(如遞歸無(wú)終止條件)
│  └─ OutOfMemoryError:內(nèi)存溢出錯(cuò)誤(OOM)
└─ Exception:程序級(jí)別的異常,開(kāi)發(fā)者可通過(guò)代碼處理
   ├─ 編譯時(shí)異常(受檢查異常 Checked Exception):編譯期必須處理
   │  ├─ IOException:IO操作相關(guān)異常(如文件讀取、網(wǎng)絡(luò)請(qǐng)求)
   │  ├─ SQLException:數(shù)據(jù)庫(kù)操作相關(guān)異常
   │  └─ ClassNotFoundException:類(lèi)加載失敗異常
   └─ 運(yùn)行時(shí)異常(非受檢查異常 Unchecked Exception):運(yùn)行期才會(huì)出現(xiàn),可按需處理
      ├─ NullPointerException:空指針異常
      ├─ ArrayIndexOutOfBoundsException:數(shù)組越界異常
      ├─ ArithmeticException:算術(shù)異常
      └─ ClassCastException:類(lèi)型轉(zhuǎn)換異常

三大核心類(lèi)的區(qū)別

  1. Error:Java 虛擬機(jī)無(wú)法解決的嚴(yán)重問(wèn)題,屬于系統(tǒng)級(jí)錯(cuò)誤,一旦發(fā)生程序基本無(wú)法恢復(fù),只能提前預(yù)防。比如遞歸調(diào)用無(wú)終止條件會(huì)導(dǎo)致StackOverflowError,創(chuàng)建大量對(duì)象未釋放會(huì)導(dǎo)致OutOfMemoryError
  2. 編譯時(shí)異常(Checked Exception):在程序編譯階段就會(huì)被檢測(cè)到的異常,編譯器強(qiáng)制要求開(kāi)發(fā)者必須處理(捕獲或拋出),否則代碼無(wú)法通過(guò)編譯。比如讀取文件時(shí)的FileNotFoundException,必須顯式處理。
  3. 運(yùn)行時(shí)異常(Unchecked Exception):繼承自RuntimeException的異常,在程序運(yùn)行階段才會(huì)觸發(fā),編譯器不強(qiáng)制處理。這類(lèi)異常通常是由于開(kāi)發(fā)者的代碼邏輯錯(cuò)誤導(dǎo)致的,比如空指針、數(shù)組越界,建議通過(guò)優(yōu)化代碼邏輯避免,而非被動(dòng)處理。

重要區(qū)分:編譯期的語(yǔ)法錯(cuò)誤(如把System.out.println寫(xiě)成system.out.println)不屬于異常,只是代碼書(shū)寫(xiě)錯(cuò)誤,編譯器會(huì)直接提示,無(wú)法生成 class 文件;而異常是代碼編譯通過(guò)后,JVM 執(zhí)行時(shí)發(fā)生的錯(cuò)誤。

三、異常的處理方式:從防御式編程到核心關(guān)鍵字

在處理異常前,我們需要了解兩種編程思想:事前防御事后處理,Java 異常處理的核心基于后者,同時(shí)提供了 5 個(gè)核心關(guān)鍵字:throwthrows、try、catch、finally,掌握這 5 個(gè)關(guān)鍵字,就能處理絕大多數(shù)異常場(chǎng)景。

3.1 兩種防御式編程思想

LBYL:事前防御型(Look Before You Leap)

在執(zhí)行操作前,對(duì)所有可能出現(xiàn)的問(wèn)題進(jìn)行充分檢查,正常流程和錯(cuò)誤處理流程混在一起,代碼可讀性差。

/**
 * 事前防御型編程示例:用戶(hù)登陸
 */
public class LBYLDemo {
    public static void main(String[] args) {
        String username = "test";
        String password = "123";
        // 檢查用戶(hù)名是否為空
        if (username == null || username.isEmpty()) {
            System.out.println("錯(cuò)誤:用戶(hù)名為空");
            return;
        }
        // 檢查密碼是否為空
        if (password == null || password.isEmpty()) {
            System.out.println("錯(cuò)誤:密碼為空");
            return;
        }
        // 檢查用戶(hù)名密碼是否正確
        if (!"admin".equals(username) || !"admin123".equals(password)) {
            System.out.println("錯(cuò)誤:用戶(hù)名或密碼錯(cuò)誤");
            return;
        }
        System.out.println("登陸成功");
    }
}

缺陷:代碼中大量的if判斷讓核心業(yè)務(wù)邏輯被淹沒(méi),后期維護(hù)難度大。

EAFP:事后處理型(It's Easier to Ask Forgiveness than Permission)

先執(zhí)行操作,遇到問(wèn)題再捕獲處理,將正常流程和錯(cuò)誤流程分離,代碼更清晰,這也是 Java 異常處理的核心思想。

/**
 * 事后處理型編程示例:用戶(hù)登陸
 */
public class EAFPDemo {
    public static void main(String[] args) {
        String username = "test";
        String password = "123";
        try {
            // 直接執(zhí)行核心業(yè)務(wù)邏輯,不做前置檢查
            login(username, password);
            System.out.println("登陸成功");
        } catch (NullPointerException e) {
            System.out.println("錯(cuò)誤:用戶(hù)名或密碼為空");
        } catch (IllegalArgumentException e) {
            System.out.println("錯(cuò)誤:" + e.getMessage());
        }
    }
    private static void login(String username, String password) {
        if (username == null || password == null) {
            throw new NullPointerException();
        }
        if (!"admin".equals(username) || !"admin123".equals(password)) {
            throw new IllegalArgumentException("用戶(hù)名或密碼錯(cuò)誤");
        }
    }
}

優(yōu)勢(shì):核心業(yè)務(wù)邏輯login()方法簡(jiǎn)潔,錯(cuò)誤處理集中在catch塊,開(kāi)發(fā)者更關(guān)注正常流程,代碼可讀性和可維護(hù)性大幅提升。

3.2 手動(dòng)拋出異常:throw

在編寫(xiě)程序時(shí),如果檢測(cè)到非法的業(yè)務(wù)邏輯或參數(shù)錯(cuò)誤,需要主動(dòng)將錯(cuò)誤信息告知調(diào)用者,此時(shí)可以使用throw關(guān)鍵字手動(dòng)拋出一個(gè)指定的異常對(duì)象。

語(yǔ)法格式

throw new 異常類(lèi)名("異常產(chǎn)生的原因");

實(shí)戰(zhàn)示例:參數(shù)合法性校驗(yàn)

/**
 * throw 手動(dòng)拋出異常示例:獲取集合指定下標(biāo)元素
 */
import java.util.List;
public class ThrowDemo {
    public static <T> T getListElement(List<T> list, int index) {
        // 校驗(yàn)集合是否為null
        if (list == null) {
            throw new NullPointerException("傳遞的集合對(duì)象為null,無(wú)法獲取元素");
        }
        // 校驗(yàn)下標(biāo)是否合法
        if (index < 0 || index >= list.size()) {
            throw new IndexOutOfBoundsException("傳遞的下標(biāo)" + index + "越界,集合長(zhǎng)度為" + list.size());
        }
        // 校驗(yàn)通過(guò),返回元素
        return list.get(index);
    }
    public static void main(String[] args) {
        List<String> list = List.of("Java", "異常", "處理");
        // 正常獲取
        System.out.println(getListElement(list, 1));
        // 下標(biāo)越界,手動(dòng)拋出異常
        // System.out.println(getListElement(list, 5));
        // 集合為null,手動(dòng)拋出異常
        // System.out.println(getListElement(null, 0));
    }
}

throw 使用注意事項(xiàng)

  1. throw必須寫(xiě)在方法體內(nèi)部
  2. 拋出的對(duì)象必須是Exception或其子類(lèi)的實(shí)例;
  3. 拋出運(yùn)行時(shí)異常(如NullPointerException),調(diào)用者可按需處理,編譯器不強(qiáng)制;
  4. 拋出編譯時(shí)異常(如IOException),調(diào)用者必須處理(捕獲或拋出),否則代碼無(wú)法編譯;
  5. 異常一旦拋出,其后的代碼將不會(huì)執(zhí)行。

3.3 聲明異常:throws

如果當(dāng)前方法沒(méi)有能力處理拋出的異常,或者希望將異常處理的責(zé)任轉(zhuǎn)移給調(diào)用者,此時(shí)可以使用throws關(guān)鍵字在方法聲明處聲明該方法可能拋出的異常。

語(yǔ)法格式

修飾符 返回值類(lèi)型 方法名(參數(shù)列表) throws 異常類(lèi)型1, 異常類(lèi)型2... {
    // 方法體,可能拋出異常
}

實(shí)戰(zhàn)示例:文件操作聲明編譯時(shí)異常

/**
 * throws 聲明異常示例:文件讀取
 */
import java.io.File;
import java.io.FileReader;
import java.io.FileNotFoundException;
public class ThrowsDemo {
    // 聲明文件未找到異常,交給調(diào)用者處理
    public static FileReader openFile(String filePath) throws FileNotFoundException {
        File file = new File(filePath);
        // FileNotFoundException是編譯時(shí)異常,此處不處理,聲明后拋出
        return new FileReader(file);
    }
    public static void main(String[] args) {
        try {
            // 調(diào)用聲明異常的方法,必須處理異常
            FileReader fr = openFile("test.txt");
            System.out.println("文件打開(kāi)成功");
            fr.close();
        } catch (FileNotFoundException e) {
            System.out.println("異常原因:" + e.getMessage());
        }
    }
}

throws 使用注意事項(xiàng)

  1. throws必須跟在方法參數(shù)列表之后
  2. 聲明的異常必須是Exception或其子類(lèi);
  3. 方法內(nèi)部拋出多個(gè)異常時(shí),throws后用逗號(hào)分隔多個(gè)異常類(lèi)型;若異常之間有父子關(guān)系,直接聲明父類(lèi)異常即可(如FileNotFoundException繼承自IOException,可直接聲明throws IOException);
  4. 調(diào)用聲明了編譯時(shí)異常的方法,調(diào)用者必須處理(try-catch捕獲或繼續(xù)throws拋出);調(diào)用聲明了運(yùn)行時(shí)異常的方法,編譯器不強(qiáng)制處理。

3.4 捕獲并處理異常:try-catch

throws只是將異常轉(zhuǎn)移給調(diào)用者,并未真正處理異常;而try-catch是 Java 中處理異常的核心方式,能捕獲異常并對(duì)其進(jìn)行處理,讓程序在發(fā)生異常后繼續(xù)執(zhí)行。

語(yǔ)法格式

try {
    // 可能拋出異常的代碼塊(監(jiān)控區(qū))
} catch (異常類(lèi)型1 異常對(duì)象名) {
    // 處理異常類(lèi)型1的代碼(捕獲區(qū))
} catch (異常類(lèi)型2 異常對(duì)象名) {
    // 處理異常類(lèi)型2的代碼
}
// 可選:finally塊,下文單獨(dú)講解

實(shí)戰(zhàn)示例:多異常捕獲與處理

/**
 * try-catch 捕獲異常示例:多異常處理
 */
public class TryCatchDemo {
    public static void calculate(int a, int b, int[] arr) {
        try {
            System.out.println("a / b = " + (a / b));
            System.out.println("數(shù)組下標(biāo)0的元素:" + arr[0]);
        } catch (ArithmeticException e) {
            // 處理算術(shù)異常
            System.out.println("處理算術(shù)異常:" + e.getMessage());
        } catch (NullPointerException e) {
            // 處理空指針異常
            System.out.println("處理空指針異常:數(shù)組對(duì)象為null");
        } catch (ArrayIndexOutOfBoundsException e) {
            // 處理數(shù)組越界異常
            System.out.println("處理數(shù)組越界異常:" + e.getMessage());
        }
    }
    public static void main(String[] args) {
        // 測(cè)試1:除數(shù)為0
        calculate(10, 0, new int[]{1,2});
        System.out.println("===== 分割線 =====");
        // 測(cè)試2:數(shù)組為null
        calculate(10, 2, null);
        System.out.println("===== 分割線 =====");
        // 測(cè)試3:數(shù)組越界(空數(shù)組)
        calculate(10, 2, new int[]{});
        // 異常處理后,后續(xù)代碼正常執(zhí)行
        System.out.println("程序執(zhí)行完成");
    }
}

運(yùn)行結(jié)果

處理算術(shù)異常:/ by zero
===== 分割線 =====
處理空指針異常:數(shù)組對(duì)象為null
===== 分割線 =====
處理數(shù)組越界異常:Index 0 out of bounds for length 0
程序執(zhí)行完成

可以看到,即使發(fā)生了異常,經(jīng)過(guò)try-catch處理后,程序的后續(xù)代碼依然能正常執(zhí)行,這也是異常處理的核心目的。

try-catch 使用關(guān)鍵注意事項(xiàng)

  1. try塊中拋出異常的位置后續(xù)代碼不會(huì)執(zhí)行;
  2. 異常捕獲遵循類(lèi)型匹配原則:只有catch的異常類(lèi)型與try中拋出的異常類(lèi)型一致,或?yàn)槠?strong>父類(lèi),才能捕獲到異常;
  3. 處理多個(gè)不同類(lèi)型的異常時(shí),需注意子類(lèi)異常在前,父類(lèi)異常在后,否則會(huì)出現(xiàn)語(yǔ)法錯(cuò)誤(父類(lèi)異常會(huì)捕獲所有子類(lèi)異常,后續(xù)的子類(lèi)異常catch塊永遠(yuǎn)無(wú)法執(zhí)行);
  4. 若多個(gè)異常的處理邏輯完全相同,可使用 ** 豎線 |** 合并捕獲,簡(jiǎn)化代碼:
    catch (ArithmeticException | NullPointerException | ArrayIndexOutOfBoundsException e) {
        System.out.println("處理異常:" + e.getMessage());
    }
    
  5. 可以使用Exception捕獲所有異常(因?yàn)?code>Exception是所有程序級(jí)異常的父類(lèi)),但不推薦:會(huì)掩蓋具體的異常類(lèi)型,不利于問(wèn)題排查,僅適用于通用的異常兜底處理。

3.5 必執(zhí)行的代碼塊:finally

在程序開(kāi)發(fā)中,有些代碼無(wú)論是否發(fā)生異常,都必須執(zhí)行,比如打開(kāi)的文件流、數(shù)據(jù)庫(kù)連接、網(wǎng)絡(luò)連接等資源的釋放,否則會(huì)造成資源泄漏。finally塊就是為了解決這個(gè)問(wèn)題,它配合try-catch使用,里面的代碼永遠(yuǎn)會(huì)被執(zhí)行。

語(yǔ)法格式

try {
    // 可能拋出異常的代碼
} catch (異常類(lèi)型 e) {
    // 處理異常的代碼
} finally {
    // 無(wú)論是否發(fā)生異常,都會(huì)執(zhí)行的代碼(資源釋放為主)
}

核心場(chǎng)景:資源釋放(全新示例)

/**
 * finally 塊示例:資源釋放(Scanner)
 */
import java.util.Scanner;
import java.util.InputMismatchException;
public class FinallyDemo {
    public static int getIntInput() {
        Scanner sc = new Scanner(System.in);
        try {
            System.out.print("請(qǐng)輸入一個(gè)整數(shù):");
            // 嘗試獲取整數(shù)輸入
            int num = sc.nextInt();
            return num;
        } catch (InputMismatchException e) {
            System.out.println("輸入類(lèi)型錯(cuò)誤,不是整數(shù)");
            return -1;
        } finally {
            // 無(wú)論是否輸入正確,都關(guān)閉Scanner,釋放資源
            System.out.println("執(zhí)行finally塊:關(guān)閉Scanner資源");
            sc.close();
        }
    }
    public static void main(String[] args) {
        int num = getIntInput();
        System.out.println("獲取到的數(shù)字:" + num);
    }
}

測(cè)試結(jié)果 1:輸入正確整數(shù)

請(qǐng)輸入一個(gè)整數(shù):100
執(zhí)行finally塊:關(guān)閉Scanner資源
獲取到的數(shù)字:100

測(cè)試結(jié)果 2:輸入非整數(shù)

請(qǐng)輸入一個(gè)整數(shù):abc
輸入類(lèi)型錯(cuò)誤,不是整數(shù)
執(zhí)行finally塊:關(guān)閉Scanner資源
獲取到的數(shù)字:-1

可以看到,無(wú)論try塊中是否發(fā)生異常,finally塊的代碼都會(huì)執(zhí)行,完美解決了資源釋放的問(wèn)題。

finally 的特殊注意事項(xiàng)

  1. finally塊的執(zhí)行時(shí)機(jī):在方法返回之前(即使trycatch中有return語(yǔ)句,也會(huì)先執(zhí)行finally塊,再執(zhí)行return);
  2. finally塊中也有return語(yǔ)句,會(huì)覆蓋trycatch中的return結(jié)果,強(qiáng)烈不建議finally中寫(xiě)return(編譯器會(huì)給出警告);
  3. finally塊唯一不執(zhí)行的情況:程序執(zhí)行到try/catch塊時(shí),調(diào)用了System.exit(0)(強(qiáng)制終止 JVM),此時(shí) JVM 直接退出,所有代碼都不再執(zhí)行。

四、異常的處理流程:跟著調(diào)用棧走,理清執(zhí)行順序

要徹底理解異常處理,必須理清異常的傳播和處理流程,而核心就是方法調(diào)用棧:Java 中方法之間的調(diào)用關(guān)系會(huì)被 JVM 存儲(chǔ)在虛擬機(jī)棧中,當(dāng)發(fā)生異常時(shí),異常會(huì)沿著調(diào)用棧從下往上傳播,直到被捕獲處理,若最終無(wú)人處理,則由 JVM 接管,程序異常終止。

異常處理完整執(zhí)行流程

  1. 程序先執(zhí)行try塊中的代碼;
  2. try塊中未發(fā)生異常,跳過(guò)catch塊,直接執(zhí)行finally塊,再執(zhí)行try-catch-finally后的代碼;
  3. try塊中發(fā)生異常,立即終止try塊后續(xù)代碼,匹配catch塊的異常類(lèi)型:
    • 找到匹配的異常類(lèi)型:執(zhí)行對(duì)應(yīng)catch塊的處理代碼,再執(zhí)行finally塊,最后執(zhí)行后續(xù)代碼;
    • 未找到匹配的異常類(lèi)型:先執(zhí)行finally塊,再將異常向上傳播給上層調(diào)用者;
  4. 上層調(diào)用者重復(fù)步驟 3,若所有調(diào)用者都未處理異常,最終傳遞到main方法;
  5. main方法也未處理異常,異常會(huì)被JVM 接管,JVM 會(huì)打印異常信息(類(lèi)型、原因、調(diào)用棧),并強(qiáng)制終止程序,main方法后續(xù)代碼不再執(zhí)行。

實(shí)戰(zhàn)示例:異常的向上傳播

/**
 * 異常處理流程示例:異常向上傳播
 */
public class ExceptionFlowDemo {
    // 方法3:拋出數(shù)組越界異常
    public static void method3() {
        int[] arr = {1,2,3};
        System.out.println(arr[10]); // 拋出異常
    }
    // 方法2:調(diào)用method3,未處理異常
    public static void method2() {
        method3();
    }
    // 方法1:調(diào)用method2,捕獲并處理異常
    public static void method1() {
        try {
            method2();
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("method1捕獲到異常:" + e.getMessage());
        }
    }
    public static void main(String[] args) {
        method1();
        // 異常被處理,后續(xù)代碼正常執(zhí)行
        System.out.println("main方法后續(xù)代碼執(zhí)行");
    }
}

運(yùn)行結(jié)果

method1捕獲到異常:Index 10 out of bounds for length 3
main方法后續(xù)代碼執(zhí)行

若刪除method1中的try-catch,異常會(huì)傳播到main方法,若main方法也不處理,JVM 會(huì)接管,程序終止,main方法后續(xù)代碼不再執(zhí)行。

五、自定義異常:貼合業(yè)務(wù)場(chǎng)景,讓異常更有意義

Java 內(nèi)置了豐富的異常類(lèi),但這些異常類(lèi)都是通用的,無(wú)法精準(zhǔn)描述實(shí)際開(kāi)發(fā)中的業(yè)務(wù)異常,比如用戶(hù)登陸時(shí)的 “用戶(hù)名不存在”、“密碼錯(cuò)誤”,訂單操作時(shí)的 “訂單不存在”、“庫(kù)存不足” 等。此時(shí)我們需要自定義異常類(lèi),貼合業(yè)務(wù)場(chǎng)景,讓異常信息更精準(zhǔn),便于問(wèn)題排查和業(yè)務(wù)處理。

5.1 自定義異常的實(shí)現(xiàn)步驟

Java 中自定義異常的核心是繼承,遵循以下兩步即可:

  1. 自定義異常類(lèi),繼承自Exception(編譯時(shí)異常,受檢查)或RuntimeException(運(yùn)行時(shí)異常,非受檢查);
  2. 實(shí)現(xiàn)帶 String 類(lèi)型參數(shù)的構(gòu)造方法,將異常原因通過(guò)super()傳遞給父類(lèi)構(gòu)造方法(便于通過(guò)getMessage()獲取異常原因)。

5.2 實(shí)戰(zhàn)示例:用戶(hù)登陸業(yè)務(wù)的自定義異常

我們針對(duì)用戶(hù)登陸場(chǎng)景,自定義兩個(gè)業(yè)務(wù)異常:UserNameNotExistException(用戶(hù)名不存在)、PasswordErrorException(密碼錯(cuò)誤),并在業(yè)務(wù)代碼中拋出和處理。

步驟 1:實(shí)現(xiàn)自定義異常類(lèi)

/**
 * 自定義異常:用戶(hù)名不存在(繼承Exception,編譯時(shí)異常)
 */
public class UserNameNotExistException extends Exception {
    // 構(gòu)造方法,傳遞異常原因
    public UserNameNotExistException(String message) {
        super(message);
    }
}
/**
 * 自定義異常:密碼錯(cuò)誤(繼承Exception,編譯時(shí)異常)
 */
public class PasswordErrorException extends Exception {
    public PasswordErrorException(String message) {
        super(message);
    }
}

可選優(yōu)化:若希望自定義異常為運(yùn)行時(shí)異常,只需將父類(lèi)改為RuntimeException,編譯器不強(qiáng)制處理。

步驟 2:在業(yè)務(wù)代碼中拋出自定義異常

/**
 * 用戶(hù)登陸業(yè)務(wù)類(lèi)
 */
public class UserLoginService {
    // 模擬數(shù)據(jù)庫(kù)中的用戶(hù)信息
    private static final String DB_USERNAME = "admin";
    private static final String DB_PASSWORD = "admin123456";
    /**
     * 登陸方法,拋出自定義業(yè)務(wù)異常
     * @param username 用戶(hù)名
     * @param password 密碼
     * @throws UserNameNotExistException 用戶(hù)名不存在
     * @throws PasswordErrorException    密碼錯(cuò)誤
     */
    public void login(String username, String password) throws UserNameNotExistException, PasswordErrorException {
        // 校驗(yàn)用戶(hù)名
        if (!DB_USERNAME.equals(username)) {
            throw new UserNameNotExistException("用戶(hù)名[" + username + "]不存在");
        }
        // 校驗(yàn)密碼
        if (!DB_PASSWORD.equals(password)) {
            throw new PasswordErrorException("密碼錯(cuò)誤,請(qǐng)重新輸入");
        }
    }
}

步驟 3:調(diào)用業(yè)務(wù)方法,處理自定義異常

/**
 * 測(cè)試自定義異常:用戶(hù)登陸
 */
public class CustomExceptionTest {
    public static void main(String[] args) {
        UserLoginService loginService = new UserLoginService();
        // 測(cè)試1:用戶(hù)名不存在
        String username = "test";
        String password = "admin123456";
        try {
            loginService.login(username, password);
            System.out.println("登陸成功!");
        } catch (UserNameNotExistException e) {
            System.out.println("登陸失敗:" + e.getMessage());
            // 可做后續(xù)處理,如跳轉(zhuǎn)到注冊(cè)頁(yè)面
        } catch (PasswordErrorException e) {
            System.out.println("登陸失?。? + e.getMessage());
            // 可做后續(xù)處理,如提示密碼找回
        }
        // 測(cè)試2:密碼錯(cuò)誤
        System.out.println("===== 分割線 =====");
        username = "admin";
        password = "123";
        try {
            loginService.login(username, password);
            System.out.println("登陸成功!");
        } catch (UserNameNotExistException | PasswordErrorException e) {
            System.out.println("登陸失?。? + e.getMessage());
        }
    }
}

運(yùn)行結(jié)果

登陸失敗:用戶(hù)名[test]不存在
===== 分割線 =====
登陸失?。好艽a錯(cuò)誤,請(qǐng)重新輸入

可以看到,自定義異常能精準(zhǔn)描述業(yè)務(wù)中的錯(cuò)誤場(chǎng)景,讓異常處理更貼合實(shí)際業(yè)務(wù),同時(shí)異常信息更直觀,便于開(kāi)發(fā)和運(yùn)維人員排查問(wèn)題。

5.3 自定義異常的選型建議

  • 若希望編譯器強(qiáng)制處理該異常(如核心業(yè)務(wù)異常,必須顯式處理),讓自定義異常繼承Exception(編譯時(shí)異常);
  • 若該異常可通過(guò)代碼邏輯避免,或希望簡(jiǎn)化代碼(不強(qiáng)制處理),讓自定義異常繼承RuntimeException(運(yùn)行時(shí)異常);
  • 自定義異常的命名要見(jiàn)名知意,通常以Exception結(jié)尾,如OrderNotExistException、StockNotEnoughException。

六、異常處理的最佳實(shí)踐

掌握了異常的基礎(chǔ)知識(shí)點(diǎn)后,更重要的是在實(shí)際開(kāi)發(fā)中遵循最佳實(shí)踐,讓異常處理更規(guī)范、更高效:

  1. 避免捕獲所有異常:不要直接捕獲Exception,會(huì)掩蓋具體的異常類(lèi)型,不利于問(wèn)題排查,應(yīng)捕獲具體的異常類(lèi)型;
  2. 不要忽略異常:不要在catch塊中只寫(xiě)e.printStackTrace(),甚至空的catch塊,應(yīng)根據(jù)業(yè)務(wù)場(chǎng)景做具體處理(如記錄日志、提示用戶(hù)、重試操作);
  3. 及時(shí)釋放資源:打開(kāi)的 IO 流、數(shù)據(jù)庫(kù)連接、網(wǎng)絡(luò)連接等資源,必須在finally塊中釋放,或使用 Java7 的try-with-resources自動(dòng)釋放;
  4. 異常信息要精準(zhǔn):拋出異常時(shí),填寫(xiě)清晰的異常原因(如throw new NullPointerException("用戶(hù)信息對(duì)象為null,無(wú)法獲取用戶(hù)ID")),便于排查問(wèn)題;
  5. 子類(lèi)方法拋出異常范圍不超過(guò)父類(lèi):繼承父類(lèi)并重寫(xiě)方法時(shí),子類(lèi)方法拋出的異常類(lèi)型不能是父類(lèi)方法異常的父類(lèi),也不能拋出更多的受檢查異常;
  6. 合理選擇自定義異常的父類(lèi):核心業(yè)務(wù)異常建議繼承Exception,強(qiáng)制調(diào)用者處理;非核心異常建議繼承RuntimeException,簡(jiǎn)化代碼;
  7. 使用日志框架記錄異常:實(shí)際開(kāi)發(fā)中,不要使用e.printStackTrace(),應(yīng)使用 SLF4J/Logback 等日志框架記錄異常(可記錄異常級(jí)別、調(diào)用棧、業(yè)務(wù)上下文),便于線上問(wèn)題排查。

七、總結(jié)

Java 異常處理是保證程序健壯性的核心,其核心是事后處理的編程思想,通過(guò)throw、throws、trycatchfinally五個(gè)關(guān)鍵字實(shí)現(xiàn)異常的拋出、聲明、捕獲和處理。

本文從異常的概念出發(fā),理清了Throwable、Error、Exception的繼承關(guān)系,區(qū)分了編譯時(shí)異常和運(yùn)行時(shí)異常;然后詳細(xì)講解了異常處理的核心方式和執(zhí)行流程;最后通過(guò)實(shí)戰(zhàn)實(shí)現(xiàn)了貼合業(yè)務(wù)的自定義異常,并給出了開(kāi)發(fā)中的最佳實(shí)踐。

掌握異常處理的關(guān)鍵,不僅是記住語(yǔ)法和規(guī)則,更重要的是結(jié)合業(yè)務(wù)場(chǎng)景選擇合適的處理方式:讓程序在發(fā)生異常時(shí),既能精準(zhǔn)定位問(wèn)題,又能優(yōu)雅地處理異常,保證程序的正常運(yùn)行。希望本文能讓你對(duì) Java 異常處理有更全面、更深入的理解,在實(shí)際開(kāi)發(fā)中玩轉(zhuǎn)異常體系!

到此這篇關(guān)于Java入門(mén)異常處理最佳實(shí)踐的文章就介紹到這了,更多相關(guān)java異常處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring中@Scheduled功能的使用方法詳解

    Spring中@Scheduled功能的使用方法詳解

    @Scheduled 由Spring定義,用于將方法設(shè)置為調(diào)度任務(wù),下面這篇文章主要給大家介紹了關(guān)于Spring中@Scheduled功能的使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • myeclipse8.5優(yōu)化技巧詳解

    myeclipse8.5優(yōu)化技巧詳解

    本文詳細(xì)介紹了myeclipse8.5優(yōu)化技巧,在這里和大家分享一下,希望對(duì)大家有用。
    2017-09-09
  • Java多線程鎖機(jī)制相關(guān)原理實(shí)例解析

    Java多線程鎖機(jī)制相關(guān)原理實(shí)例解析

    這篇文章主要介紹了Java多線程鎖機(jī)制相關(guān)原理實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • SpringBoot3解決跨域請(qǐng)求的方案小結(jié)

    SpringBoot3解決跨域請(qǐng)求的方案小結(jié)

    解決跨域請(qǐng)求,主要有JSONP,iframe,window.name,CORS等方式,其中CORS方式是最常用的跨域?qū)崿F(xiàn)方式,而且是對(duì)各種請(qǐng)求方法、各種數(shù)據(jù)請(qǐng)求類(lèi)型都是完美支持的,本文介紹了SpringBoot3解決跨域請(qǐng)求的方案小結(jié),需要的朋友可以參考下
    2024-07-07
  • Docker?快速部署Springboot項(xiàng)目超詳細(xì)最新版

    Docker?快速部署Springboot項(xiàng)目超詳細(xì)最新版

    這篇文章主要介紹了Docker?快速部署Springboot項(xiàng)目超詳細(xì)最新版的相關(guān)資料,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Java實(shí)現(xiàn)批量下載選中文件功能

    Java實(shí)現(xiàn)批量下載選中文件功能

    這篇文章主要介紹了Java實(shí)現(xiàn)批量下載選中文件功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-11-11
  • Java實(shí)現(xiàn)動(dòng)態(tài)數(shù)字時(shí)鐘

    Java實(shí)現(xiàn)動(dòng)態(tài)數(shù)字時(shí)鐘

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)動(dòng)態(tài)數(shù)字時(shí)鐘,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • Java事務(wù)失效八大場(chǎng)景詳細(xì)解析

    Java事務(wù)失效八大場(chǎng)景詳細(xì)解析

    在Java開(kāi)發(fā)中Spring框架提供了強(qiáng)大的事務(wù)管理功能,然而在實(shí)際開(kāi)發(fā)過(guò)程中,由于對(duì)框架特性的誤解或不當(dāng)使用,可能導(dǎo)致事務(wù)失效問(wèn)題,這篇文章主要介紹了Java事務(wù)失效八大場(chǎng)景的相關(guān)資料,需要的朋友可以參考下
    2025-10-10
  • 在SpringBoot里自定義Spring MVC配置的三種方法

    在SpringBoot里自定義Spring MVC配置的三種方法

    SpringBoot幫我們自動(dòng)配好了 MVC 的核心組件,但實(shí)際開(kāi)發(fā)中,光用默認(rèn)配置肯定不夠今天就教你 3 種自定義 Spring MVC 配置的方法,從簡(jiǎn)單到復(fù)雜,新手也能一步步跟著做,需要的朋友可以參考下
    2026-03-03
  • 詳解SpringBoot+Ehcache使用示例

    詳解SpringBoot+Ehcache使用示例

    本文介紹了SpringBoot中配置Ehcache、自定義get/set方式,并實(shí)際使用緩存的過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-09-09

最新評(píng)論

洞口县| 乐陵市| 都兰县| 临邑县| 宁德市| 古丈县| 辽阳县| 柳州市| 城步| 安岳县| 赫章县| 汤阴县| 泾川县| 邻水| 肇东市| 遂平县| 玉溪市| 措勤县| 武鸣县| 香港| 古浪县| 高安市| 天柱县| 上虞市| 阳城县| 万全县| 额济纳旗| 丽水市| 阳信县| 抚松县| 马公市| 洛南县| 蒲城县| 礼泉县| 佳木斯市| 固阳县| 久治县| 宜良县| 玉山县| 徐州市| 遂宁市|