你好!我不想提它,但程序員工作的很大一部分是處理錯誤。大多數情況下,他或她自己的。原來沒有不犯錯的人。而且也沒有這樣的程序。 當然,在處理錯誤時,最主要的是了解它的原因. 很多事情都可能導致程序中出現錯誤。在某些時候,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,則會發生此異常。
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
,catch
和finally
. 程序員認為可能發生異常的代碼放在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();
}
}
}
現在我們確信我們會處理資源,不管程序運行時發生了什麼。:) 關於異常,您需要了解的還不止這些。錯誤處理是編程中一個非常重要的話題。很多文章都專門介紹它。在下一課中,我們將了解有哪些類型的異常以及如何創建您自己的異常。:) 回頭見!
更多閱讀: |
---|
GO TO FULL VERSION