Java 教程

Java 控制語句

面向物件程式設計

Java 內建類

Java 檔案處理

Java 錯誤與異常

Java 多執行緒

Java 同步

Java 網路程式設計

Java 集合

Java 介面

Java 資料結構

Java 集合演算法

高階 Java

Java 雜項

Java API 和框架

Java 類參考

Java 有用資源

Java - 異常



什麼是 Java 中的異常?

異常(或異常事件)是在程式執行期間出現的問題。當異常發生時,程式的正常流程被打斷,程式/應用程式異常終止,這是不推薦的,因此,這些異常需要被處理。

為什麼發生異常?

異常可能由於多種原因發生。以下是發生異常的一些場景。

  • 使用者輸入了無效資料。

  • 需要開啟的檔案找不到。

  • 網路連線在通訊過程中斷開或 JVM 記憶體不足。

其中一些異常是由使用者錯誤引起的,另一些是由程式設計師錯誤引起的,還有一些是由以某種方式發生故障的物理資源引起的。

Java 異常類別

基於此,我們有以下幾類異常。您需要了解它們才能知道 Java 中的異常處理是如何工作的。

  • 受檢異常
  • 非受檢異常
  • 錯誤

Java 受檢異常

受檢異常是在編譯時由編譯器檢查(通知)的異常,這些異常也稱為編譯時異常。這些異常不能簡單地忽略,程式設計師應該注意(處理)這些異常。

示例:Java 中的受檢異常

例如,如果您在程式中使用FileReader類從檔案讀取資料,如果其建構函式中指定的檔案不存在,則會發生FileNotFoundException,編譯器會提示程式設計師處理該異常。

import java.io.File;
import java.io.FileReader;

public class FilenotFound_Demo {

   public static void main(String args[]) {		
      File file = new File("E://file.txt");
      FileReader fr = new FileReader(file); 
   }
}

如果您嘗試編譯上述程式,您將獲得以下異常。

輸出

C:\>javac FilenotFound_Demo.java
FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
      FileReader fr = new FileReader(file);
                      ^
1 error

注意 - 由於 FileReader 類的read()close()方法丟擲 IOException,您可以觀察到編譯器會通知處理 IOException,以及 FileNotFoundException。

Java 非受檢異常

非受檢異常是在執行時發生的異常。這些異常也稱為執行時異常。這些包括程式設計錯誤,例如邏輯錯誤或 API 的不正確使用。執行時異常在編譯時被忽略。

示例:Java 中的非受檢異常

例如,如果您在程式中聲明瞭一個大小為 5 的陣列,並嘗試呼叫陣列的第 6 個元素,則會發生ArrayIndexOutOfBoundsException異常。

public class Unchecked_Demo {
   
   public static void main(String args[]) {
      int num[] = {1, 2, 3, 4};
      System.out.println(num[5]);
   }
}

如果您編譯並執行上述程式,您將獲得以下異常。

輸出

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)

Java 錯誤

這些根本不是異常,而是超出使用者或程式設計師控制範圍的問題。在您的程式碼中通常會忽略錯誤,因為您很少能對錯誤做任何事情。例如,如果發生堆疊溢位,則會發生錯誤。它們在編譯時也被忽略。

Java 異常層次結構

所有異常類都是 java.lang.Exception 類的子型別。異常類是Throwable 類的子類。除了異常類之外,還有另一個稱為 Error 的子類,它派生自 Throwable 類。

錯誤是在發生嚴重故障時發生的異常情況,這些情況不會由 Java 程式處理。生成錯誤是為了指示執行時環境生成的錯誤。例如:JVM 記憶體不足。通常,程式無法從錯誤中恢復。

Exception 類有兩個主要的子類:IOException 類和 RuntimeException 類。

Exceptions1

以下是Java內建異常中最常見的一些已檢查異常和未檢查異常的列表。Java的內建異常

Java異常類方法

以下是Throwable類中一些重要方法的列表。

序號 方法及描述
1

public String getMessage()

返回關於發生的異常的詳細訊息。此訊息在Throwable建構函式中初始化。

2

public Throwable getCause()

返回異常的原因,以Throwable物件表示。

3

public String toString()

返回類的名稱與getMessage()結果的連線。

4

public void printStackTrace()

將toString()的結果以及堆疊跟蹤列印到System.err(錯誤輸出流)。

5

public StackTraceElement [] getStackTrace()

返回一個包含堆疊跟蹤中每個元素的陣列。索引為0的元素表示呼叫堆疊的頂部,陣列中的最後一個元素表示呼叫堆疊底部的呼叫方法。

6

public Throwable fillInStackTrace()

用當前堆疊跟蹤填充此Throwable物件的堆疊跟蹤,並新增到堆疊跟蹤中的任何先前資訊。

捕獲異常:Java中的異常處理

方法使用trycatch關鍵字的組合來捕獲異常。try/catch塊放置在可能生成異常的程式碼周圍。try/catch塊中的程式碼稱為受保護程式碼,使用try/catch的語法如下所示:

語法

try {
   // Protected code
} catch (ExceptionName e1) {
   // Catch block
}

容易發生異常的程式碼放在try塊中。當發生異常時,發生的異常由與其關聯的catch塊處理。每個try塊都應該緊跟一個catch塊或finally塊。

catch語句涉及宣告您嘗試捕獲的異常型別。如果在受保護的程式碼中發生異常,則檢查try後面的catch塊(或塊)。如果發生的異常型別在catch塊中列出,則異常將傳遞到catch塊,就像引數傳遞到方法引數一樣。

示例:演示異常處理

在以下示例中,聲明瞭一個包含2個元素的陣列。然後程式碼嘗試訪問陣列的第3個元素,這將引發異常。

// File Name : ExcepTest.java
import java.io.*;

public class ExcepTest {

   public static void main(String args[]) {
      try {
         int a[] = new int[2];
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }
      System.out.println("Out of the block");
   }
}

輸出

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

多個Catch塊

try塊後面可以跟多個catch塊。多個catch塊的語法如下所示:

語法

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}

前面的語句演示了三個catch塊,但在單個try之後可以有任意數量的catch塊。如果在受保護的程式碼中發生異常,則異常將拋到列表中的第一個catch塊。如果丟擲的異常的資料型別與ExceptionType1匹配,則會在那裡捕獲它。如果不是,則異常傳遞到第二個catch語句。這種情況將持續下去,直到異常被捕獲或貫穿所有catch,在這種情況下,當前方法將停止執行,並且異常將拋到呼叫堆疊上的前一個方法。

示例

以下程式碼段顯示瞭如何使用多個try/catch語句。

try {
   file = new FileInputStream(fileName);
   x = (byte) file.read();
} catch (IOException i) {
   i.printStackTrace();
   return -1;
} catch (FileNotFoundException f) // Not valid! {
   f.printStackTrace();
   return -1;
}

捕獲多種型別的異常

從Java 7開始,您可以使用單個catch塊處理多個異常,此功能簡化了程式碼。以下是如何執行此操作:

catch (IOException|FileNotFoundException ex) {
   logger.log(ex);
   throw ex;

Throws/Throw關鍵字

如果方法不處理已檢查異常,則該方法必須使用throws關鍵字宣告它。throws關鍵字出現在方法簽名末尾。

您可以使用throw關鍵字丟擲異常,無論是新例項化的異常還是剛剛捕獲的異常。

嘗試理解throws和throw關鍵字之間的區別,throws用於推遲已檢查異常的處理,而throw用於顯式呼叫異常。

以下方法宣告它丟擲一個RemoteException:

示例

import java.io.*;
public class className {

   public void deposit(double amount) throws RemoteException {
      // Method implementation
      throw new RemoteException();
   }
   // Remainder of class definition
}

方法可以宣告它丟擲多個異常,在這種情況下,異常以逗號分隔的列表形式宣告。例如,以下方法宣告它丟擲一個RemoteException和一個InsufficientFundsException:

示例

import java.io.*;
public class className {

   public void withdraw(double amount) throws RemoteException, 
      InsufficientFundsException {
      // Method implementation
   }
   // Remainder of class definition
}

Finally塊

finally塊位於try塊或catch塊之後。無論是否發生異常,finally塊中的程式碼始終都會執行。

使用finally塊,您可以執行任何您希望執行的清理型別語句,無論受保護程式碼中發生了什麼。

finally塊出現在catch塊的末尾,並且具有以下語法:

語法

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}finally {
   // The finally block always executes.
}

示例

public class ExcepTest {

   public static void main(String args[]) {
      int a[] = new int[2];
      try {
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }finally {
         a[0] = 6;
         System.out.println("First element value: " + a[0]);
         System.out.println("The finally statement is executed");
      }
   }
}

輸出

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed

請注意以下幾點:

  • catch子句不能獨立存在,必須與try語句一起使用。

  • 在存在try/catch塊時,不一定需要finally子句。

  • try塊不能獨立存在,必須與catch子句或finally子句一起使用。

  • try、catch、finally塊之間不能存在任何程式碼。

Try-with-resources

通常,當我們使用任何資源(如流、連線等)時,我們必須使用finally塊顯式關閉它們。在下面的程式中,我們使用FileReader從檔案中讀取資料,並使用finally塊關閉它。

示例

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReadData_Demo {

   public static void main(String args[]) {
      FileReader fr = null;		
      try {
         File file = new File("file.txt");
         fr = new FileReader(file); char [] a = new char[50];
         fr.read(a);   // reads the content to the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }finally {
         try {
            fr.close();
         } catch (IOException ex) {		
            ex.printStackTrace();
         }
      }
   }
}

try-with-resources,也稱為自動資源管理,是Java 7中引入的一種新的異常處理機制,它會自動關閉try catch塊中使用的資源。

要使用此語句,您只需在括號內宣告所需的資源,並且建立的資源將在塊結束時自動關閉。以下是try-with-resources語句的語法。

語法

try(FileReader fr = new FileReader("file path")) {
   // use the resource
   } catch () {
      // body of catch 
   }
}

以下是使用try-with-resources語句讀取檔案中的資料的程式。

示例

import java.io.FileReader;
import java.io.IOException;

public class Try_withDemo {

   public static void main(String args[]) {
      try(FileReader fr = new FileReader("E://file.txt")) {
         char [] a = new char[50];
         fr.read(a);   // reads the contentto the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

在使用try-with-resources語句時,需要注意以下幾點。

  • 要將類與try-with-resources語句一起使用,它必須實現AutoCloseable介面,並且它的close()方法會在執行時自動呼叫。

  • 您可以在try-with-resources語句中宣告多個類。

  • 當您在try-with-resources語句的try塊中宣告多個類時,這些類將按相反的順序關閉。

  • 除了在括號內宣告資源之外,其他所有內容都與try塊的普通try/catch塊相同。

  • try中宣告的資源在try塊開始之前立即例項化。

  • 在try塊中宣告的資源隱式宣告為final。

Java中的使用者定義異常

您可以在Java中建立自己的異常。在編寫自己的異常類時,請牢記以下幾點:

  • 所有異常都必須是Throwable的子類。

  • 如果您想編寫由Handle or Declare Rule自動執行的已檢查異常,則需要擴充套件Exception類。

  • 如果您想編寫執行時異常,則需要擴充套件RuntimeException類。

語法

我們可以如下定義自己的異常類:

class MyException extends Exception {
}

您只需要擴充套件預定義的Exception類即可建立自己的異常。這些被認為是已檢查異常。以下InsufficientFundsException類是一個使用者定義的異常,它擴充套件了Exception類,使其成為已檢查異常。異常類與任何其他類一樣,包含有用的欄位和方法。

示例:建立使用者定義異常

// File Name InsufficientFundsException.java
import java.io.*;

public class InsufficientFundsException extends Exception {
   private double amount;
   
   public InsufficientFundsException(double amount) {
      this.amount = amount;
   }
   
   public double getAmount() {
      return amount;
   }
}

為了演示如何使用我們使用者定義的異常,以下CheckingAccount類包含一個丟擲InsufficientFundsException的withdraw()方法。

// File Name CheckingAccount.java
import java.io.*;

public class CheckingAccount {
   private double balance;
   private int number;
   
   public CheckingAccount(int number) {
      this.number = number;
   }
   
   public void deposit(double amount) {
      balance += amount;
   }
   
   public void withdraw(double amount) throws InsufficientFundsException {
      if(amount <= balance) {
         balance -= amount;
      }else {
         double needs = amount - balance;
         throw new InsufficientFundsException(needs);
      }
   }
   
   public double getBalance() {
      return balance;
   }
   
   public int getNumber() {
      return number;
   }
}

以下BankDemo程式演示了呼叫CheckingAccount的deposit()和withdraw()方法。

// File Name BankDemo.java
public class BankDemo {

   public static void main(String [] args) {
      CheckingAccount c = new CheckingAccount(101);
      System.out.println("Depositing $500...");
      c.deposit(500.00);
      
      try {
         System.out.println("\nWithdrawing $100...");
         c.withdraw(100.00);
         System.out.println("\nWithdrawing $600...");
         c.withdraw(600.00);
      } catch (InsufficientFundsException e) {
         System.out.println("Sorry, but you are short $" + e.getAmount());
         e.printStackTrace();
      }
   }
}

編譯上述三個檔案並執行BankDemo。這將產生以下結果:

輸出

Depositing $500...

Withdrawing $100...

Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
         at CheckingAccount.withdraw(CheckingAccount.java:25)
         at BankDemo.main(BankDemo.java:13)

常見的Java異常

在Java中,可以定義兩類異常和錯誤。

  • JVM異常 - 這些是JVM專門或邏輯上丟擲的異常/錯誤。例如:NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException。

  • 程式異常 - 這些異常由應用程式或API程式設計師顯式丟擲。例如:IllegalArgumentException、IllegalStateException。

廣告