CodeGym /Java Blog /Toto sisi /異常:捕獲和處理
John Squirrels
等級 41
San Francisco

異常:捕獲和處理

在 Toto sisi 群組發布
你好!我不想提它,但程序員工作的很大一部分是處理錯誤。大多數情況下,他或她自己的。原來沒有不犯錯的人。而且也沒有這樣的程序。 例外:捕捉和處理 - 1 當然,在處理錯誤時,最主要的是了解它的原因. 很多事情都可能導致程序中出現錯誤。在某些時候,Java 的創建者問自己應該如何處理最有可能的編程錯誤?完全避免它們是不現實的,程序員有能力寫出你甚至無法想像的東西。:) 因此,我們需要為語言提供一種處理錯誤的機制。換句話說,如果您的程序出現錯誤,您需要某種腳本來指示下一步該做什麼。當錯誤發生時,程序究竟應該做什麼?今天我們將熟悉這種機制。它被稱為“ Java 中的異常”。

什麼是例外?

異常是程序運行時發生的異常的、計劃外的情況。 有很多例外。例如,您編寫了從文件中讀取文本並顯示第一行的代碼。

public class Main {

   public static void main(String[] args) throws IOException {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   }
}
但是如果沒有這個文件怎麼辦!該程序將生成異常:FileNotFoundException。Output: Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (系統找不到指定的路徑) 在Java中,每個異常都由一個單獨的類表示。 所有這些異常類都派生自一個共同的“祖先”——Throwable父類。異常類的名稱通常簡明地反映異常發生的原因:
  • FileNotFoundException(找不到文件)

  • ArithmeticException(執行數學運算時發生異常)

  • ArrayIndexOutOfBoundsException(索引超出了數組的範圍)。例如,如果您嘗試顯示只有 10 個元素的數組的位置 23,則會發生此異常。
Java 總共有將近400 個這樣的類!為什麼這麼多?讓程序員更方便地使用它們。想像一下:您編寫了一個程序,當它運行時會生成如下所示的異常:

Exception in thread "main"
呃。:/ 這沒什麼用。目前尚不清楚錯誤的含義或來源。這裡沒有有用的信息。但是 Java 中的大量異常類為程序員提供了最重要的東西:錯誤的類型及其可能的原因(嵌入在類名中)。這是另一回事

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
立即清楚問題可能是什麼以及從哪裡開始挖掘以解決問題! 與任何類的實例一樣,異常是對象。

捕獲和處理異常

Java 有處理異常的特殊代碼塊:try,catchfinally. 例外:捕捉和處理 - 2 程序員認為可能發生異常的代碼放在try塊中。這並不意味著這裡會發生異常。這意味著它可能會發生在這裡,並且程序員意識到了這種可能性。您期望發生的錯誤類型被放置在catch塊中。這還包含發生異常時應執行的所有代碼。這是一個例子:

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");
   }
}
輸出: 錯誤!文件未找到! 我們將代碼分為兩塊。在第一個塊中,我們預計可能會出現“找不到文件”錯誤。這是try塊。第二步,我們告訴程序如果發生錯誤該怎麼辦。以及具體的錯誤類型:FileNotFoundException。如果我們將不同的異常類放在塊的括號中catch,則FileNotFoundException不會被捕獲。

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (ArithmeticException e) {

       System.out.println("Error! File not found!");
   }
}
Output: Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)塊 中的代碼catch沒有運行,因為我們“配置”這個塊要捕獲ArithmeticException,並且塊中的代碼try拋出了不同的類型:FileNotFoundException。我們沒有編寫任何代碼來處理FileNotFoundException,因此程序顯示 的默認信息FileNotFoundException。這裡需要注意三點。 第一。一旦塊中某行發生異常try,後面的代碼將不會執行。程序的執行立即“跳轉到”catch塊。例如:

public static void main(String[] args) {
   try {
       System.out.println("Divide by zero");
       System.out.println(366/0);// This line of code will throw an exception

       System.out.println("This");
       System.out.println("code");
       System.out.println("will not");
       System.out.println("be");
       System.out.println("executed!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
輸出: 除以零 程序跳轉到 catch 塊!錯誤!你不能除以零! 在該塊的第二行try,我們嘗試除以 0,結果是一個ArithmeticException. 因此,該try塊​​的第 3-9 行將不會執行。正如我們所說,程序立即開始執行catch塊。 第二。可以有幾個catch塊。如果塊中的代碼try可能拋出的不是一個,而是幾種不同類型的異常,您可以catch為它們中的每一個編寫一個塊。

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       System.out.println(366/0);
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
      
       System.out.println("Error! File not found!");
      
   } catch (ArithmeticException e) {

       System.out.println("Error! Division by 0!");
      
   }
}
在這個例子中,我們寫了兩個catch塊。如果塊FileNotFoundException中出現a,則將執行try第一個塊。catch如果ArithmeticException發生 an,將執行第二個塊。catch如果你願意,你可以寫 50 個塊。當然,最好不要編寫可能引發 50 種不同異常的代碼。:) 第三。您如何知道您的代碼可能拋出哪些異常? 好吧,您也許能夠猜出其中的一些,但您不可能將所有內容都記在腦海中。 因此,Java 編譯器知道最常見的異常以及它們可能發生的情況。 例如,如果您編寫的代碼編譯器知道可能會拋出兩種類型的異常,那麼在您處理它們之前,您的代碼將無法編譯。我們將在下面看到這方面的例子。現在談談異常處理。有兩種處理異常的方法。我們已經遇到了第一個:該方法可以在一個塊中處理異常本身catch()。還有第二種選擇:該方法可以將異常重新拋出調用堆棧。這意味著什麼?例如,我們有一個具有相同printFirstString()方法的類,它讀取一個文件並顯示其第一行:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
目前,我們的代碼無法編譯,因為它有未處理的異常。在第 1 行中,您指定文件的路徑。編譯器知道這樣的代碼很容易產生一個FileNotFoundException. 在第 3 行,您從文件中讀取了文本。此過程很容易導致IOException(輸入/輸出錯誤)。現在編譯器對你說,“伙計,我不會批准這段代碼,我不會編譯它,直到你告訴我如果發生這些異常之一我應該怎麼做。而且它們肯定會根據你編寫的代碼發生!” 沒有辦法解決它:您需要同時處理這兩個問題!我們已經知道第一種異常處理方法:我們需要將我們的代碼放在一個try塊中,並添加兩個catch塊:

public static void printFirstString(String filePath) {

   try {
       BufferedReader reader = new BufferedReader(new FileReader(filePath));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error, file not found!");
       e.printStackTrace();
   } catch (IOException e) {
       System.out.println("File input/output error!");
       e.printStackTrace();
   }
}
但這不是唯一的選擇。我們可以簡單地拋出更高的異常,而不是在方法中編寫錯誤處理代碼。throws這是使用方法聲明中的 關鍵字完成的:

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
在關鍵字之後throws,我們指明了該方法可能拋出的所有異常類型的逗號分隔列表。為什麼?現在,如果有人想調用printFirstString()程序中的方法,他或她(而不是你)將不得不執行異常處理。例如,假設您的一位同事在程序的其他地方編寫了一個調用您的printFirstString()方法的方法:

public static void yourColleagueMethod() {

   // Your colleague's method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
我們得到一個錯誤!這段代碼無法編譯!我們沒有在printFirstString()方法中編寫異常處理代碼。因此,這項任務現在落在了使用該方法的人的肩上。 換句話說,該methodWrittenByYourColleague()方法現在有兩個相同的選項:它必須要么使用一個try-catch塊來處理這兩個異常,要么重新拋出它們。

public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   // The method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
在第二種情況下,調用堆棧中的下一個方法(調用的方法methodWrittenByYourColleague())將必須處理異常。這就是為什麼我們稱之為“向上拋出或傳遞異常”的原因。如果您使用關鍵字向上拋出異常throws,您的代碼將通過編譯。此時,編譯器似乎在說,“好吧,好吧。你的代碼包含一堆潛在的異常,但我會編譯它。但我們會回到這個話題!” 當您調用任何具有未處理異常的方法時,編譯器會履行其承諾並再次提醒您有關它們的信息。最後,我們將討論finally塊(抱歉是雙關語)。這是異常處理三巨頭的最後一部分try-catch-finally.

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error! File not found!");
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
   }
}
在此示例中,塊內的代碼finally將在兩種情況下執行。 如果塊中的代碼try完全運行且沒有拋出任何異常,則該finally塊將運行到最後。如果塊內的代碼try被異常中斷,程序跳轉到catch塊內,塊finally內的代碼仍然會在塊內的代碼之後運行catch為什麼這是必要的? 它的主要目的是執行強制代碼:無論在什麼情況下都必須執行的代碼。例如,它經常釋放一些程序使用的資源。在我們的代碼中,我們打開一個流以從文件中讀取信息並將其傳遞給BufferedReader對象。 我們必須關閉閱讀器並釋放資源。無論如何都必須這樣做——當程序正常工作時,以及當它拋出異常時。該finally塊是執行此操作的一個非常方便的地方:

public static void main(String[] args) throws IOException {

   BufferedReader reader = null;
   try {
       reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
現在我們確信我們會處理資源,不管程序運行時發生了什麼。:) 關於異常,您需要了解的還不止這些。錯誤處理是編程中一個非常重要的話題。很多文章都專門介紹它。在下一課中,我們將了解有哪些類型的異常以及如何創建您自己的異常。:) 回頭見!
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION