JavaRush /Java Blog /Random-TW /Java中的異常:捕獲和處理

Java中的異常:捕獲和處理

在 Random-TW 群組發布
你好!我不想告訴你,但程式設計師工作的很大一部分就是處理錯誤。最常見的是 - 與他們自己的。偏偏沒有不犯錯的人。而且也沒有這樣的程序。當然,處理錯誤時最重要的是了解原因。程序中可能有很多原因。Java 的創建者一度面臨一個問題:如何處理程式中這些非常潛在的錯誤?完全避免它們是不現實的。程式設計師可以寫一些無法想像的東西:)這意味著有必要在語言中建構一個處理錯誤的機制。換句話說,如果程式中出現錯誤,則需要腳本來進行進一步的工作。當發生錯誤時程式到底該做什麼?今天我們就來熟悉這個機制。這就是所謂的「例外

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。結論:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
在 Java 中,每個異常都由一個單獨的類別表示。所有異常類別都來自一個共同的「祖先」—父類Throwable。異常類別的名稱通常簡要反映其發生的原因:
  • FileNotFoundException(文件未找到)
  • ArithmeticException(執行數學運算時例外)
  • ArrayIndexOutOfBoundsException(數組單元的數量指定超出其長度)。例如,如果您嘗試將長度為 10 的陣列 array 輸出到控制台。
Java 中有將近 400 個這樣的類別!為什麼這麼多?正是為了讓程式設計師更方便的使用它們。想像一下:您編寫了一個程序,當它運行時,它拋出一個如下所示的異常:
Exception in thread "main"
呃呃:/什麼都不清楚。目前還不清楚它是什麼類型的錯誤以及它來自哪裡。沒有有用的信息。但由於類別的種類繁多,程式設計師自己可以得到主要的資訊 - 錯誤的類型及其可能的原因,這些資訊包含在類別的名稱中。畢竟,在控制台中看到的是完全不同的東西:
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
問題可能是什麼以及「往哪個方向挖掘」來解決問題立刻就清楚了!與任何類別實例一樣,異常也是物件。

捕獲和處理異常

為了在 Java 中處理異常,有一些特殊的程式碼區塊:trycatchfinally例外:攔截和處理 - 2程式設計師期望發生異常的程式碼被放置在一個區塊中try。這並不意味著該位置一定會發生異常。這意味著它可能在那裡發生,並且程式設計師意識到了這一點。您期望收到的錯誤類型被放置在一個區塊中catch(“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另一個異常類別傳遞到區塊括號中,它將不會被捕獲。
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!");
   }
}
結論:

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системе не удается найти указанный путь)
該區塊中的程式碼catch不起作用,因為我們“配置”了該區塊以攔截ArithmeticException,並且該區塊中的程式碼try拋出了另一種類型 - FileNotFoundException。我們沒有為編寫腳本FileNotFoundException,因此程式在控制台中顯示的是預設顯示的資訊FileNotFoundException。這裡需要注意3件事。 第一的。 一旦try區塊中的任何一行程式碼發生異常,則該行後面的程式碼將不再被執行。程式的執行會立即「跳轉」到該區塊catch。例如:
public static void main(String[] args) {
   try {
       System.out.println("Divide a number 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("Not");
       System.out.println("will");
       System.out.println("done!");

   } 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。此後,該區塊的第 6-10 行將try不再執行。正如我們所說,程式立即開始執行該區塊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。如果,try出現在區塊中FileNotFoundException,則將執行第一個區塊catch。如果發生ArithmeticException,第二個將被執行。你可以編寫至少 50 個區塊catch。但是,當然,最好不要編寫可能引發 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 行,您從文件中讀取文字。在這個過程中,輸入輸出(Input-Output)過程中很容易出現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("Error while inputting/outputting data from file!");
       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 at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
錯誤,程式碼無法編譯!printFirstString()我們沒有在方法中編寫錯誤處理腳本。因此,這個任務就落在了使用這種方法的人的肩上。也就是說,該方法yourColleagueMethod()現在面臨相同的兩個選項:它必須處理使用「飛向」它的兩個異常try-catch,或進一步轉發它們。
public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   //...the method does something

   //...and at one moment calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Eugene\\Desktop\\testFile.txt");
}
在第二種情況下,處理將落在堆疊上下一個方法的肩上 - 將呼叫 的方法yourColleagueMethod()。這就是為什麼這種機制被稱為“向上拋出異常”,或“傳遞到頂部”。當您使用 引發異常時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 is the finally block!");
   }
}
在此範例中,區塊內的程式碼finally在兩種情況下都會執行。如果區塊中的程式碼try完全執行且沒有拋出異常,則該區塊將在最後觸發finally。如果裡面的程式碼try被中斷,程式跳到該區塊catch,執行完裡面的程式碼後catch,該區塊仍然會被選取finally。為什麼需要它?它的主要目的是執行程式碼所需的部分;無論情況如何都必須完成的部分。例如,它經常釋放程式使用的一些資源。在我們的程式碼中,我們打開一個流來從文件中讀取訊息並將其傳遞給BufferedReader. 我們reader需要關閉並釋放資源。在任何情況下都必須這樣做:程式是否按預期工作或拋出異常並不重要。在區塊中執行此操作很方便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 is the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
現在我們絕對確定我們已經處理了佔用的資源,無論程式運行時發生什麼:) 這並不是您需要了解的有關異常的全部資訊。錯誤處理是程式設計中一個非常重要的主題:不只一篇文章專門討論它。在下一課中,我們將學習有哪些類型的異常以及如何創建您自己的異常:) 再見!
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION