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

全面解析Java中常見Exception異常的錯誤排查與代碼修正

 更新時間:2026年03月10日 09:16:25   作者:百錦再@新空間創(chuàng)想科技  
這篇文章主要為大家詳細(xì)介紹了Java開發(fā)中最常見的異常類型及其處理方法,包括運行時異常(如NullPointerException,ArrayIndexOutOfBoundsException)和受檢異常,下面小編就和大家詳細(xì)介紹一下吧

前言

適用對象

本課程適合已經(jīng)掌握J(rèn)ava基礎(chǔ)語法,初步了解異常處理概念,但希望系統(tǒng)掌握常見異常排查與修復(fù)能力的開發(fā)者。無論你是剛?cè)胄械男氯?,還是有一定經(jīng)驗的開發(fā)者,這門課程都將幫助你建立系統(tǒng)的異常排查思維,提升代碼質(zhì)量。

學(xué)習(xí)目標(biāo)

通過兩個課時的系統(tǒng)學(xué)習(xí),你將能夠:

  • 識別 Java中10+種最常見的異常及其產(chǎn)生場景
  • 分析 異常堆棧信息,快速定位問題根源
  • 掌握 針對不同異常的系統(tǒng)性排查方法
  • 運用 最佳實踐修復(fù)代碼,預(yù)防同類問題再次發(fā)生
  • 建立 異常處理的正確思維模式

課程安排

  • 第一課時(約60分鐘):異常基礎(chǔ)概念 + 運行時異常深度剖析(NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException、ArithmeticException)
  • 第二課時(約60分鐘):受檢異常深度剖析 + 復(fù)雜異常排查 + 綜合實戰(zhàn)演練

教學(xué)方式

每個異常都遵循“現(xiàn)象描述 → 出現(xiàn)場景 → 堆棧分析 → 排查方法 → 代碼修正 → 預(yù)防措施”的六步教學(xué)法,確保理論與實踐緊密結(jié)合。

第一部分:Java異常體系回顧(約10分鐘)

1.1 異常是什么

在深入具體異常之前,我們先理解異常的本質(zhì)。異常(Exception) 是程序運行過程中出現(xiàn)的打斷正常執(zhí)行流程的事件。它本質(zhì)上是一個對象,封裝了錯誤類型、錯誤描述、方法調(diào)用堆棧以及可能的底層原因。

1.2 Java異常體系結(jié)構(gòu)

        java.lang.Object
               |
       java.lang.Throwable
               |
      ---------------------
      |                   |
  java.lang.Error     java.lang.Exception
                          |
              -------------------------
              |                       |
   RuntimeException              其他 Exception
   (運行時異常)                  (受檢異常)

  • Error:JVM級別的嚴(yán)重錯誤,如OutOfMemoryError、StackOverflowError,程序通常無法處理。
  • Exception:程序可處理的異常,分為兩類:
    • 受檢異常(Checked Exception):編譯時必須處理(捕獲或聲明拋出),如IOException、SQLException
    • 運行時異常(RuntimeException):編譯時不強(qiáng)制處理,通常由程序邏輯錯誤導(dǎo)致,如NullPointerException、ArrayIndexOutOfBoundsException

1.3 異常信息解讀

一個典型的異常堆棧包含以下要素:

Exception java.lang.IllegalArgumentException: item quantity must be a number
        at io.jzheaux.pluralsight.DeliController.orderSandwich (DeliController.java:45)
        // …
Caused by java.lang.NumberFormatException: For input string: " 3"
        at NumberFormatException.forInputString (NumberFormatException.java:67)
        at Integer.parseInt (Integer.java:647)
        ...
  • 異常類型IllegalArgumentException
  • 異常消息item quantity must be a number
  • 堆棧軌跡:從main開始到異常發(fā)生處的調(diào)用鏈
  • Caused by:底層根本原因,通常是排查的關(guān)鍵入口

排查技巧:遇到復(fù)雜異常時,不要只看第一行,要順著堆棧往下找,尤其是“Caused by”部分,那里往往藏著真正的原因。

第二課時(上):運行時異常深度剖析(約30分鐘)

運行時異常(RuntimeException)是Java程序中最常見的異常類型,它們通常由代碼邏輯錯誤引起。下面我們將逐個剖析最常見的運行時異常。

2.1 NullPointerException(空指針異常)

現(xiàn)象描述

當(dāng)應(yīng)用程序試圖在需要對象的地方使用null引用時,拋出此異常。這是Java中最著名的異常,占據(jù)了異??倲?shù)的很大比例。

出現(xiàn)場景

場景一:直接調(diào)用null對象的方法或?qū)傩?/strong>

String text = null;
int length = text.length(); // 拋出NullPointerException

場景二:自動拆箱時包裝類型為null

Boolean willVote = null;
if (willVote) { // 自動拆箱時拋出NullPointerException
    System.out.println("可以投票");
}

場景三:方法參數(shù)或返回值未做空檢查

void parseDocument(Document doc) {
    doc.getElements(); // 如果傳入的doc為null,拋出異常
}

String lookupElement(Document doc) {
    Element element = doc.findElement("span");
    return element.getValue(); // 如果element為null,拋出異常
}

場景四:數(shù)組元素未初始化

Person[] people = new Person[5];
people[0].getName(); // 數(shù)組元素默認(rèn)為null,拋出異常

堆棧分析示例

Exception in thread "main" java.lang.NullPointerException
    at com.example.UserService.getUserAge(UserService.java:25)
    at com.example.UserController.main(UserController.java:12)

從堆??梢钥闯?,UserService.java的第25行調(diào)用了某個null對象的方法。

排查方法流程圖

代碼修正與預(yù)防

修正方案一:參數(shù)校驗

// 錯誤代碼
void parseDocument(Document doc) {
    doc.getElements();
}

// 修正代碼
void parseDocument(@NonNull Document doc) {
    if (doc == null) {
        throw new IllegalArgumentException("doc cannot be null");
    }
    doc.getElements();
}

修正方案二:使用守衛(wèi)語句

// 錯誤代碼
String lookupElement(Document doc) {
    Element element = doc.findElement("span");
    return element.getValue();
}

// 修正代碼
@Nullable String lookupElement(Document doc) {
    Element element = doc.findElement("span");
    if (element == null) {
        return null; // 或者返回默認(rèn)值
    }
    return element.getValue();
}

修正方案三:使用Optional(Java 8+)

public Optional<String> lookupElement(Document doc) {
    return Optional.ofNullable(doc.findElement("span"))
                  .map(Element::getValue);
}

修正方案四:數(shù)組元素初始化

Person[] people = new Person[5];
for (int i = 0; i < people.length; i++) {
    people[i] = new Person(); // 確保每個元素都被初始化
}

預(yù)防措施

  • 明確空值來源:是無效值(上游問題)還是有效值(可接受null)
  • 使用@NonNull和@Nullable注解,讓IDE幫助檢查
  • 遵循"盡早失敗"原則:在方法入口處就進(jìn)行參數(shù)校驗
  • 謹(jǐn)慎處理返回值:明確方法是否可能返回null,并在文檔中說明

2.2 ArrayIndexOutOfBoundsException(數(shù)組下標(biāo)越界異常)

現(xiàn)象描述

當(dāng)試圖使用非法索引訪問數(shù)組元素時拋出,非法索引包括負(fù)數(shù)、0到數(shù)組長度減1范圍外的值。

出現(xiàn)場景

場景一:索引超出數(shù)組長度

int[] numbers = {1, 2, 3};
int value = numbers[3]; // 索引3超出范圍(有效索引0-2)

場景二:循環(huán)條件錯誤

int[] scores = {85, 90, 78, 92};
for (int i = 0; i <= scores.length; i++) { // 應(yīng)該是 i < scores.length
    System.out.println(scores[i]); // 最后一次循環(huán)i=4,越界
}

場景三:索引為負(fù)數(shù)

int[] data = new int[10];
int index = -1;
data[index] = 100; // 負(fù)索引越界

堆棧分析示例

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
    at com.example.ArrayDemo.processArray(ArrayDemo.java:15)
    at com.example.ArrayDemo.main(ArrayDemo.java:8)

異常信息直接告訴我們:試圖訪問索引5,但數(shù)組長度只有5(有效索引0-4)。

排查方法

代碼修正與預(yù)防

修正方案一:修正循環(huán)邊界

// 錯誤代碼
for (int i = 0; i <= scores.length; i++) {
    System.out.println(scores[i]);
}

// 修正代碼
for (int i = 0; i < scores.length; i++) {
    System.out.println(scores[i]);
}

// 更好的方式:使用增強(qiáng)for循環(huán)
for (int score : scores) {
    System.out.println(score);
}

修正方案二:訪問前檢查索引

public int getElement(int[] array, int index) {
    if (array == null) {
        throw new IllegalArgumentException("array cannot be null");
    }
    if (index < 0 || index >= array.length) {
        throw new IndexOutOfBoundsException(
            "Index " + index + " out of bounds for length " + array.length);
    }
    return array[index];
}

預(yù)防措施

  • 優(yōu)先使用增強(qiáng)for循環(huán)處理數(shù)組遍歷
  • 使用Arrays工具類的方法進(jìn)行數(shù)組操作
  • 動態(tài)計算索引時,添加邊界檢查
  • **考慮使用ArrayList**等集合類,它們提供了更安全的get()方法(也會拋出越界異常,但信息更明確)

2.3 ClassCastException(類型轉(zhuǎn)換異常)

現(xiàn)象描述

當(dāng)試圖將一個對象強(qiáng)制轉(zhuǎn)換為它不是實例的子類時拋出。這是使用繼承和多態(tài)時的常見問題。

出現(xiàn)場景

場景一:將父類對象強(qiáng)制轉(zhuǎn)換為子類類型

Object obj = new Object();
Integer num = (Integer) obj; // Object不能轉(zhuǎn)換為Integer

場景二:集合中元素類型不一致

List list = new ArrayList();
list.add("Hello");
list.add(123); // 混合類型

String first = (String) list.get(0); // 正常
String second = (String) list.get(1); // 拋出ClassCastException,123不能轉(zhuǎn)String

場景三:不正確的向下轉(zhuǎn)型

Animal animal = new Dog();
Cat cat = (Cat) animal; // Dog不能轉(zhuǎn)換為Cat

堆棧分析示例

Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
    at com.example.GenericDemo.processList(GenericDemo.java:22)
    at com.example.GenericDemo.main(GenericDemo.java:15)

異常信息明確告訴我們:試圖將Integer轉(zhuǎn)換為String,類型不兼容。

排查方法

代碼修正與預(yù)防

修正方案一:使用泛型

// 錯誤代碼
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);

// 修正代碼:使用泛型
List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0); // 無需強(qiáng)制轉(zhuǎn)換

修正方案二:使用instanceof檢查

Object obj = getSomeObject();
if (obj instanceof String) {
    String str = (String) obj; // 安全轉(zhuǎn)換
    // 處理字符串
} else if (obj instanceof Integer) {
    Integer num = (Integer) obj; // 安全轉(zhuǎn)換
    // 處理整數(shù)
}

修正方案三:Java 16+的Pattern Matching for instanceof

Object obj = getSomeObject();
if (obj instanceof String str) {
    // 這里可以直接使用str變量
    System.out.println(str.length());
} else if (obj instanceof Integer num) {
    System.out.println(num + 10);
} else {
    // 處理其他情況
}

預(yù)防措施

  • 始終使用泛型確保集合類型安全
  • 在向下轉(zhuǎn)型前使用instanceof檢查
  • 遵循里氏替換原則,避免不必要的向下轉(zhuǎn)型
  • 考慮使用多態(tài),而不是頻繁的類型轉(zhuǎn)換

2.4 ArithmeticException(算術(shù)異常)

現(xiàn)象描述

當(dāng)發(fā)生異常的算術(shù)條件時拋出,最常見的是整數(shù)除零。

出現(xiàn)場景

場景一:整數(shù)除零

int result = 10 / 0; // 拋出ArithmeticException

場景二:取模運算除零

int remainder = 10 % 0; // 拋出ArithmeticException

注意:浮點數(shù)除零不會拋出異常,會返回Infinity或NaN

double result = 10.0 / 0.0; // 返回 Infinity,不會拋出異常

堆棧分析示例

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at com.example.Calculator.divide(Calculator.java:10)
    at com.example.Calculator.main(Calculator.java:5)

異常信息直接告訴我們問題:除零。

排查方法

代碼修正與預(yù)防

修正方案一:檢查除數(shù)

public int divide(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("除數(shù)不能為0");
    }
    return a / b;
}

修正方案二:使用Optional處理可能為0的情況

public Optional<Integer> safeDivide(int a, int b) {
    if (b == 0) {
        return Optional.empty();
    }
    return Optional.of(a / b);
}

修正方案三:使用浮點數(shù)運算(如果業(yè)務(wù)允許)

double result = 10.0 / 0.0; // 返回 Infinity,不會拋出異常
if (Double.isInfinite(result)) {
    // 處理無窮大情況
}

預(yù)防措施

  • 在進(jìn)行除法或取模前,始終檢查除數(shù)是否為0
  • **考慮使用BigDecimal**進(jìn)行精確計算,它提供了更好的異常處理
  • 從用戶輸入獲取除數(shù)時,必須進(jìn)行驗證

第二課時(中):運行時異常(續(xù))與常見受檢異常(約20分鐘)

2.5 NumberFormatException(數(shù)字格式異常)

現(xiàn)象描述

當(dāng)嘗試將字符串轉(zhuǎn)換為數(shù)字類型,但字符串格式不合法時拋出。

出現(xiàn)場景

場景一:字符串包含非數(shù)字字符

int num = Integer.parseInt("123abc"); // 拋出NumberFormatException

場景二:字符串包含空格或特殊符號

int num = Integer.parseInt(" 123 "); // 拋出NumberFormatException,空格未處理

場景三:數(shù)字超出類型范圍

int num = Integer.parseInt("2147483648"); // 超出int最大值,拋出異常

場景四:空字符串或null

int num = Integer.parseInt(""); // 拋出NumberFormatException
Integer.parseInt(null); // 拋出NullPointerException,注意這里是NPE

代碼修正與預(yù)防

修正方案一:數(shù)據(jù)清洗

// 錯誤代碼
String input = " 123 ";
int value = Integer.parseInt(input); // 拋出異常

// 修正代碼
String input = " 123 ";
input = input.trim(); // 去除前后空格
if (!input.isEmpty()) {
    try {
        int value = Integer.parseInt(input);
    } catch (NumberFormatException e) {
        // 處理異常
    }
}

修正方案二:使用正則表達(dá)式預(yù)驗證

public int parsePostalCode(String input) {
    // 預(yù)驗證:必須是5位數(shù)字
    if (input == null || !input.matches("\\d{5}")) {
        throw new IllegalArgumentException("郵政編碼必須是5位數(shù)字");
    }
    return Integer.parseInt(input); // 此時已保證安全
}

修正方案三:使用Apache Commons Lang的NumberUtils

import org.apache.commons.lang3.math.NumberUtils;

String input = "123";
int value = NumberUtils.toInt(input, 0); // 失敗時返回默認(rèn)值0,不拋出異常

修正方案四:Java 8+的Optional + 異常處理

public Optional<Integer> tryParseInt(String input) {
    try {
        return Optional.of(Integer.parseInt(input.trim()));
    } catch (NumberFormatException e) {
        return Optional.empty();
    }
}

預(yù)防措施

  • 始終對輸入進(jìn)行清洗(trim、去除非數(shù)字字符)
  • 解析前驗證格式,特別是來自外部系統(tǒng)的數(shù)據(jù)
  • 考慮使用專門的驗證框架如Hibernate Validator
  • 使用try-catch包圍解析代碼,優(yōu)雅處理異常

2.6 IllegalArgumentException(非法參數(shù)異常)

現(xiàn)象描述

當(dāng)向方法傳遞了不合法或不適當(dāng)?shù)膮?shù)時拋出。這通常表示調(diào)用者的責(zé)任。

出現(xiàn)場景

場景一:參數(shù)值超出允許范圍

public void setAge(int age) {
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("年齡必須在0-150之間");
    }
    this.age = age;
}

場景二:參數(shù)格式錯誤

public void setEmail(String email) {
    if (email == null || !email.contains("@")) {
        throw new IllegalArgumentException("郵箱格式不正確");
    }
    this.email = email;
}

場景三:參數(shù)為null但方法不允許

public void processData(@NonNull Data data) {
    if (data == null) {
        throw new IllegalArgumentException("data cannot be null");
    }
    // 處理數(shù)據(jù)
}

排查方法

  • 查看異常消息,通常會說明參數(shù)需要滿足什么條件
  • 檢查調(diào)用代碼,確認(rèn)傳入的參數(shù)值
  • 驗證參數(shù)來源,判斷是輸入錯誤還是上游數(shù)據(jù)問題

代碼修正

// 在方法開頭進(jìn)行參數(shù)校驗
public void registerUser(String username, String email, int age) {
    // 參數(shù)校驗集中處理
    if (username == null || username.trim().isEmpty()) {
        throw new IllegalArgumentException("用戶名不能為空");
    }
    if (email == null || !email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) {
        throw new IllegalArgumentException("郵箱格式不正確");
    }
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("年齡無效");
    }
    
    // 業(yè)務(wù)邏輯
}

2.7 IllegalStateException(非法狀態(tài)異常)

現(xiàn)象描述

當(dāng)方法在非法或不適當(dāng)?shù)臅r間被調(diào)用時拋出。通常表示被調(diào)用者的狀態(tài)不適合執(zhí)行請求的操作。

出現(xiàn)場景

場景一:對象未正確初始化

public class ConnectionPool {
    private boolean initialized = false;
    
    public void connect() {
        if (!initialized) {
            throw new IllegalStateException("連接池未初始化");
        }
        // 建立連接
    }
}

場景二:迭代器越界

List<String> list = Arrays.asList("A", "B");
Iterator<String> it = list.iterator();
it.next(); // A
it.next(); // B
it.next(); // 拋出NoSuchElementException,但有時會被包裝為IllegalStateException

排查方法

  • 閱讀異常消息,了解當(dāng)前對象應(yīng)該處于什么狀態(tài)
  • 檢查對象初始化或配置代碼,確保在調(diào)用前已正確設(shè)置
  • 檢查操作順序,確認(rèn)是否按正確步驟調(diào)用

代碼修正

public class FileProcessor {
    private boolean opened = false;
    
    public void open() {
        // 打開文件
        opened = true;
    }
    
    public void readData() {
        if (!opened) {
            throw new IllegalStateException("必須先調(diào)用open()方法打開文件");
        }
        // 讀取數(shù)據(jù)
    }
}

2.8 IOException(輸入輸出異常)

現(xiàn)象描述

當(dāng)輸入輸出操作失敗或中斷時拋出。這是最典型的受檢異常,處理文件、網(wǎng)絡(luò)、流操作時經(jīng)常遇到。

出現(xiàn)場景

場景一:文件不存在(FileNotFoundException)

FileReader fr = new FileReader("nonexistent.txt"); // 拋出FileNotFoundException

場景二:讀取流時連接斷開

InputStream in = socket.getInputStream();
int data = in.read(); // 如果連接已關(guān)閉,可能拋出IOException

場景三:寫入磁盤空間不足

FileOutputStream fos = new FileOutputStream("largefile.bin");
byte[] data = new byte[1024];
fos.write(data); // 如果磁盤空間不足,拋出IOException

堆棧分析示例

java.io.FileNotFoundException: nonexistent.txt (系統(tǒng)找不到指定的文件)
    at java.base/java.io.FileInputStream.open0(Native Method)
    at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
    at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
    at com.example.FileReaderDemo.main(FileReaderDemo.java:8)

排查方法

代碼修正與預(yù)防

修正方案一:使用try-with-resources確保資源關(guān)閉

// 錯誤代碼:可能忘記關(guān)閉資源
public String readFile(String path) throws IOException {
    FileReader fr = new FileReader(path);
    BufferedReader br = new BufferedReader(fr);
    return br.readLine();
    // 沒有關(guān)閉資源,可能造成內(nèi)存泄漏
}

// 修正代碼:使用try-with-resources
public String readFile(String path) throws IOException {
    try (FileReader fr = new FileReader(path);
         BufferedReader br = new BufferedReader(fr)) {
        return br.readLine();
    } // 自動關(guān)閉
}

修正方案二:檢查文件存在性

public void processFile(String path) {
    File file = new File(path);
    if (!file.exists()) {
        System.err.println("文件不存在: " + path);
        return; // 或者拋出更友好的異常
    }
    
    try (BufferedReader br = new BufferedReader(new FileReader(file))) {
        // 處理文件
    } catch (IOException e) {
        System.err.println("讀取文件時發(fā)生錯誤: " + e.getMessage());
        e.printStackTrace();
    }
}

修正方案三:多層異常處理

public void copyFile(String src, String dest) {
    try (FileInputStream in = new FileInputStream(src);
         FileOutputStream out = new FileOutputStream(dest)) {
        byte[] buffer = new byte[1024];
        int length;
        while ((length = in.read(buffer)) > 0) {
            out.write(buffer, 0, length);
        }
    } catch (FileNotFoundException e) {
        System.err.println("源文件不存在或目標(biāo)目錄無法寫入: " + e.getMessage());
    } catch (IOException e) {
        System.err.println("復(fù)制過程中發(fā)生IO錯誤: " + e.getMessage());
    }
}

預(yù)防措施

  • 始終使用try-with-resources或確保finally中關(guān)閉資源
  • 操作前檢查文件和目錄狀態(tài)
  • 為IO操作提供有意義的錯誤消息
  • 考慮重試機(jī)制,特別是網(wǎng)絡(luò)相關(guān)的IO操作

2.9 ClassNotFoundException(類未找到異常)

現(xiàn)象描述

當(dāng)應(yīng)用程序試圖通過字符串名加載類,但在類路徑中找不到該類的定義時拋出。

出現(xiàn)場景

場景一:Class.forName()加載類

Class.forName("com.mysql.jdbc.Driver"); // 如果驅(qū)動jar不在類路徑中,拋出異常

場景二:類加載器加載類

ClassLoader.getSystemClassLoader().loadClass("com.example.MissingClass");

場景三:使用反射創(chuàng)建實例

Object obj = Class.forName("com.example.DynamicClass").newInstance();

堆棧分析示例

java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
    at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:476)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:398)

排查方法

代碼修正與預(yù)防

修正方案一:確保JAR包在類路徑中

  • 對于Maven項目:檢查pom.xml中的依賴
  • 對于普通項目:確認(rèn)JAR文件在classpath下

修正方案二:捕獲并處理異常

try {
    Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
    // 提供友好的錯誤信息
    throw new RuntimeException("MySQL驅(qū)動未找到,請檢查是否添加了mysql-connector-java依賴", e);
}

修正方案三:使用ServiceLoader模式(Java 6+)

ServiceLoader<Driver> drivers = ServiceLoader.load(Driver.class);
for (Driver driver : drivers) {
    // 自動發(fā)現(xiàn)所有驅(qū)動實現(xiàn)
}

第二課時(下):綜合實戰(zhàn)與最佳實踐(約10分鐘)

3.1 復(fù)雜異常排查案例

案例:銀行轉(zhuǎn)賬系統(tǒng)中的異常鏈

public class BankingService {
    public void transfer(String fromAccount, String toAccount, double amount) 
            throws BusinessException {
        try {
            Account from = accountRepository.findByNumber(fromAccount);
            Account to = accountRepository.findByNumber(toAccount);
            
            if (from == null || to == null) {
                throw new IllegalArgumentException("賬戶不存在");
            }
            
            from.withdraw(amount);
            to.deposit(amount);
            
            transactionLog.log(fromAccount, toAccount, amount);
            
        } catch (IllegalArgumentException e) {
            throw new BusinessException("轉(zhuǎn)賬參數(shù)錯誤", e);
        } catch (InsufficientBalanceException e) {
            throw new BusinessException("余額不足", e);
        } catch (Exception e) {
            throw new BusinessException("轉(zhuǎn)賬失敗,請稍后重試", e);
        }
    }
}

異常排查思路

當(dāng)看到類似下面的異常堆棧時:

com.example.BusinessException: 轉(zhuǎn)賬失敗,請稍后重試
    at com.example.BankingService.transfer(BankingService.java:45)
    at com.example.BankingController.main(BankingController.java:18)
Caused by: java.sql.SQLException: Connection timed out
    at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2189)
    at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:795)
    at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:329)
    at java.sql.DriverManager.getConnection(DriverManager.java:664)
    at com.example.AccountRepository.findByNumber(AccountRepository.java:22)
    ... 5 more

排查步驟

  • 看頂層異常BusinessException,但消息太泛化,請稍后重試沒有實質(zhì)信息
  • 看Caused bySQLException: Connection timed out,這才是真正原因
  • 追溯源頭AccountRepository.java:22處的數(shù)據(jù)庫連接超時
  • 根本原因:數(shù)據(jù)庫連接失敗

解決方案

  • 檢查數(shù)據(jù)庫服務(wù)是否運行
  • 檢查網(wǎng)絡(luò)連接
  • 檢查數(shù)據(jù)庫連接池配置
  • 添加重試機(jī)制

教訓(xùn):包裝異常時不要丟失原始信息,提供具體的錯誤消息有助于排查。

3.2 異常處理最佳實踐總結(jié)

捕獲特定異常,而不是通用異常

// 不好的做法
try {
    // 業(yè)務(wù)代碼
} catch (Exception e) {
    // 捕獲所有異常,掩蓋了真正的問題
}

// 好的做法
try {
    // 業(yè)務(wù)代碼
} catch (FileNotFoundException e) {
    // 處理文件不存在
} catch (IOException e) {
    // 處理其他IO錯誤
}

避免空的catch塊

// 絕對不要這樣做
try {
    riskyOperation();
} catch (Exception e) {
    // 空的catch塊,異常被吞噬
}

// 至少記錄異常
try {
    riskyOperation();
} catch (Exception e) {
    logger.error("操作失敗", e); // 記錄日志
    throw e; // 或者重新拋出
}

使用try-with-resources自動關(guān)閉資源

// Java 7之前的方式
FileInputStream fis = null;
try {
    fis = new FileInputStream("file.txt");
    // 處理文件
} finally {
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// Java 7+ 推薦的方式
try (FileInputStream fis = new FileInputStream("file.txt")) {
    // 處理文件
} // 自動關(guān)閉

使用自定義異常增強(qiáng)業(yè)務(wù)語義

// 自定義業(yè)務(wù)異常
public class InsufficientBalanceException extends Exception {
    private double currentBalance;
    private double requiredAmount;
    
    public InsufficientBalanceException(double current, double required) {
        super(String.format("余額不足:當(dāng)前余額%.2f,需要%.2f", current, required));
        this.currentBalance = current;
        this.requiredAmount = required;
    }
    
    // getters...
}

// 使用
public void withdraw(double amount) throws InsufficientBalanceException {
    if (balance < amount) {
        throw new InsufficientBalanceException(balance, amount);
    }
    balance -= amount;
}

方法重寫時遵守異常聲明規(guī)則

  • 子類方法可以拋出與父類相同的異常、子類異常,或不拋出異常
  • 子類方法不能拋出比父類更寬泛的受檢異常
class Parent {
    public void process() throws IOException { }
}

class Child extends Parent {
    @Override
    public void process() throws FileNotFoundException { } // 允許,F(xiàn)ileNotFoundException是IOException的子類
    
    // @Override
    // public void process() throws Exception { } // 不允許,Exception比IOException更寬泛
}

記錄異常時包含上下文信息

try {
    processOrder(orderId, userId);
} catch (OrderException e) {
    // 記錄有用的上下文信息
    logger.error("處理訂單失敗: orderId={}, userId={}", orderId, userId, e);
    throw e;
}

不要用異常控制正常的程序流程

// 不好的做法:用異??刂屏鞒?
try {
    Integer.parseInt(userInput);
    // 是數(shù)字,繼續(xù)處理
} catch (NumberFormatException e) {
    // 不是數(shù)字,執(zhí)行其他邏輯
}

// 好的做法:使用條件判斷
if (userInput.matches("\\d+")) {
    int value = Integer.parseInt(userInput);
    // 是數(shù)字,繼續(xù)處理
} else {
    // 不是數(shù)字,執(zhí)行其他邏輯
}

異常處理的黃金法則總結(jié)

原則說明
精準(zhǔn)捕獲捕獲具體的異常類型,而不是籠統(tǒng)的Exception
絕不吞噬空的catch塊是萬惡之源,至少要記錄日志
及時釋放使用try-with-resources或finally確保資源釋放
保留原始異常包裝異常時要把原異常作為cause傳入
提供上下文異常消息要包含有助于排查的信息
區(qū)分異常類型可恢復(fù)用受檢異常,程序錯誤用運行時異常
文檔化用javadoc的@throws說明方法可能拋出的異常

3.3 Java 7+ 多異常捕獲

從Java 7開始,可以使用|在一個catch塊中捕獲多個異常類型,減少代碼重復(fù):

try {
    // 可能拋出多種異常的代碼
} catch (IOException | SQLException e) {
    // 統(tǒng)一處理IO和SQL異常
    logger.error("數(shù)據(jù)訪問錯誤", e);
    throw e; // Java 7+ 支持更精確的重拋類型檢查
}

注意:多異常捕獲時,catch參數(shù)隱式為final,不能修改。

3.4 異常處理與事務(wù)管理

在企業(yè)級應(yīng)用中,異常處理與事務(wù)管理密切相關(guān)。通常:

  • 運行時異常觸發(fā)事務(wù)回滾
  • 受檢異常不自動觸發(fā)事務(wù)回滾(在Spring中可通過rollbackFor配置)
@Service
public class AccountService {
    @Transactional(rollbackFor = {BusinessException.class, RuntimeException.class})
    public void transferMoney(String from, String to, double amount) 
            throws BusinessException {
        try {
            // 轉(zhuǎn)賬邏輯
        } catch (InsufficientBalanceException e) {
            // 業(yè)務(wù)異常,觸發(fā)事務(wù)回滾
            throw new BusinessException("轉(zhuǎn)賬失敗", e);
        }
    }
}

課程總結(jié)(約5分鐘)

知識體系回顧

通過兩個課時的學(xué)習(xí),我們?nèi)娓采w了:

  • 異?;A(chǔ):體系結(jié)構(gòu)、受檢與非受檢異常、異常信息解讀
  • 運行時異常
    • NullPointerException:空引用訪問 → 前置檢查、Optional
    • ArrayIndexOutOfBoundsException:數(shù)組越界 → 邊界檢查、增強(qiáng)for循環(huán)
    • ClassCastException:類型轉(zhuǎn)換錯誤 → instanceof檢查、泛型
    • ArithmeticException:算術(shù)異常 → 除零檢查
    • NumberFormatException:數(shù)字格式錯誤 → 輸入清洗、預(yù)驗證
    • IllegalArgumentException/IllegalStateException:參數(shù)/狀態(tài)錯誤 → 前置校驗
  • 受檢異常
    • IOException及其子類 → try-with-resources、文件存在性檢查
    • ClassNotFoundException → 檢查類路徑、依賴管理
  • 排查方法:堆棧分析、Caused by追蹤、異常鏈理解
  • 最佳實踐:精準(zhǔn)捕獲、避免吞噬、及時釋放、保留上下文等8大原則

異常排查思維導(dǎo)圖

遇到異常時,按以下順序思考:
┌─────────────────────────────────────┐
│ 1. 看類型:是什么異常?屬于哪一類?    │
├─────────────────────────────────────┤
│ 2. 看消息:異常說了什么?有什么線索?  │
├─────────────────────────────────────┤
│ 3. 看堆棧:第一行自己的代碼在哪?      │
├─────────────────────────────────────┤
│ 4. 看原因:有Caused by嗎?底層是什么? │
├─────────────────────────────────────┤
│ 5. 想來源:這個值從哪來的?誰傳的?    │
├─────────────────────────────────────┤
│ 6. 想方案:怎么修復(fù)?如何預(yù)防?       │
└─────────────────────────────────────┘

以上就是全面解析Java中常見Exception異常的錯誤排查與代碼修正的詳細(xì)內(nèi)容,更多關(guān)于Java常見Exception異常的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaWeb項目音頻資源播放實現(xiàn)方法詳解

    JavaWeb項目音頻資源播放實現(xiàn)方法詳解

    這篇文章主要介紹了JavaWeb項目音頻資源播放實現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • IDEA如何進(jìn)行全局搜索圖文教程

    IDEA如何進(jìn)行全局搜索圖文教程

    idea全稱為IntelliJ?IDEA,在業(yè)界被公認(rèn)為最好用的Java開發(fā)工具之一,下面這篇文章主要給大家介紹了關(guān)于IDEA如何進(jìn)行全局搜索的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • Java數(shù)據(jù)庫連接池技術(shù)的入門教程

    Java數(shù)據(jù)庫連接池技術(shù)的入門教程

    這篇文章主要給大家介紹了關(guān)于Java數(shù)據(jù)庫連接池技術(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Java?詳解分析鏈表的中間節(jié)點

    Java?詳解分析鏈表的中間節(jié)點

    鏈表是基本的數(shù)據(jù)結(jié)構(gòu)之一,面試題中鏈表占很大一部分,可見鏈表操作是非常重要的。本篇文章我們來探究一下如何獲取鏈表的中間節(jié)點
    2022-01-01
  • Java上傳下載文件并實現(xiàn)加密解密

    Java上傳下載文件并實現(xiàn)加密解密

    這篇文章主要介紹了Java上傳下載文件并實現(xiàn)加密解密,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • jdbc實現(xiàn)寵物商店管理系統(tǒng)

    jdbc實現(xiàn)寵物商店管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了jdbc實現(xiàn)寵物商店管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • Java使用?Spire.PDF實現(xiàn)合并PDF文檔并進(jìn)行加密與壓縮處理

    Java使用?Spire.PDF實現(xiàn)合并PDF文檔并進(jìn)行加密與壓縮處理

    無論是需要將零散的業(yè)務(wù)報告匯總,還是將多份合同掃描件整合成一個文件,掌握合并?PDF?的技巧都是提升辦公效率的必備技能,本文將介紹如何在?Java?中使用?Spire.PDF?庫高效合并?PDF?文件,感興趣的小伙伴可以了解下
    2026-04-04
  • 實戰(zhàn)干貨之基于SpringBoot的RabbitMQ多種模式隊列

    實戰(zhàn)干貨之基于SpringBoot的RabbitMQ多種模式隊列

    RabbitMQ 是一個由Erlang語言開發(fā)的AMQP的開源實現(xiàn),支持多種客戶端。用于在分布式系統(tǒng)中存儲轉(zhuǎn)發(fā)消息,在易用性、擴(kuò)展性、高可用性等方面表現(xiàn)不俗,下文將帶你深入了解 RabbitMQ 多種模式隊列
    2021-09-09
  • Java通過PowerMockito和Mokito進(jìn)行單元測試的實現(xiàn)

    Java通過PowerMockito和Mokito進(jìn)行單元測試的實現(xiàn)

    PowerMockito和Mockito都是Java語言中的測試框架,用于進(jìn)行單元測試和集成測試,本文就來詳細(xì)的介紹一下通過PowerMockito和Mokito進(jìn)行單元測試,感興趣的可以了解一下
    2023-08-08
  • 導(dǎo)入maven項目各個注解均報錯的解決方案

    導(dǎo)入maven項目各個注解均報錯的解決方案

    這篇文章主要介紹了導(dǎo)入maven項目各個注解均報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評論

灵宝市| 迁西县| 华宁县| 赫章县| 林西县| 鹤峰县| 靖安县| 宝兴县| 高雄市| 浦城县| 正安县| 微博| 夏邑县| 江口县| 普定县| 安陆市| 雅江县| 嘉峪关市| 虎林市| 南平市| 张家界市| 新绛县| 阿克陶县| 长治县| 抚顺县| 太谷县| 呼图壁县| 无极县| 四子王旗| 唐山市| 漳州市| 井陉县| 湛江市| 兰溪市| 台中市| 乡宁县| 湄潭县| 黄大仙区| 宁国市| 甘德县| 慈溪市|