Java 學習(四)—— 異常處理
一、異常的概念
異常指的是執行期出現的錯誤,也就是當程式開始執行以後執行期出現的錯誤。出現錯誤時觀察錯誤的名字和行號最為重要。
異常發生的原因:
使用者輸入了非法資料。
要開啟的檔案不存在。
網路通訊時連線中斷,或者JVM記憶體溢位。
二、異常的分類
檢查性異常(Exception):使用者錯誤或問題引起的異常,這是程式設計師無法預見的。是異常的父類,子類需要使用者顯式宣告或捕獲;例如要開啟一個不存在檔案時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。
執行時異常(Runtime Exception)
錯誤(ERROR):由Java虛擬機器生成並丟擲,錯誤不是異常,而是脫離程式設計師控制的問題。錯誤在程式碼中通常被忽略。例如,棧溢位,一個錯誤就發生了,它們在編譯也檢查不到的;動態連結失敗;虛擬機器錯誤。
Exception的層次
所有的異常類是從java.lang.Exception類繼承的子類,Exception類是Throwable類的子類。除了Exception類外,Throwable還有一個子類Error 。
Throwable類:可以被丟擲的,是所有異常類的根基類,所有的異常類都是從它繼承。
Error類:是不能處理的錯誤。Java虛擬機器(JVM)執行時出錯,用來指示執行時環境發生的錯誤。例如,JVM記憶體溢位。一般地,程式不會從錯誤中恢復。
IOException......類:這些錯誤必須被處理;
Runtime Exception類:不一定都要處理,可以忽略;
三、異常的捕獲和處理
Java異常處理的五個關鍵字:try、catch、finally、throw、throws 。
捕獲異常
使用try和catch關鍵字可以捕獲異常。try/catch/finally程式碼塊放在異常可能發生的地方。
try/catch程式碼塊中的程式碼稱為保護程式碼,使用 try/catch/finally的語法如下:
try
{
// 可能丟擲異常的語句
}catch(ExceptionName e1)
{
//Catch 塊
}catch(ExceptionName e1)
{
//Catch 塊
}finally{
...
}
try語句:捕獲可能丟擲異常的語句,如果有例外異常,交給後面的catch語句進行相應處理,後面可以跟一個或多個catch程式碼段。
Catch語句:包含要捕獲異常型別的宣告。當保護程式碼塊中發生一個異常時,try後面的catch塊就會被檢查。
如果發生的異常包含在catch塊中,異常會被傳遞到該catch塊處理。在catch程式碼塊中可以用一些方法獲取異常資訊
getMessage()方法:用來得到有關異常事件的資訊。
printStackTrace()方法:一般使用這種方法,包括了getMessage()方法,用來跟蹤異常事件發生時執行堆疊的內容。使用前需要 new 一個錯誤物件再呼叫,因為它是專屬某個錯誤物件裡面的方法,
finally語句:為異常處理提供的統一出口,無論是否發生異常都執行,可以不寫。在finally語句中,可以進行資源的清除工作,例如,開啟關閉檔案、刪除臨時檔案...
例項
下面的例子中宣告有兩個元素的一個數組,當代碼試圖訪問陣列的第三個元素的時候就會丟擲一個異常。
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(Exception e1){ //捕獲陣列下標越界異常
System.out.println("陣列下標越界");
}catch(ArrayIndexOutOfBoundsException e2){
System.out.println("異常發生"); //輸出錯誤資訊
}
}
}
因為Exception類中包含ArrayIndexOutOfBoundsException異常,所以第二個catch異常重複,是不允許的,編譯不會通過。
以上程式碼編譯執行輸出結果如下:
Exception thrown :java.lang.ArrayIndexOutOfBoundsException:
throws/throw關鍵字:
如果一個方法沒有捕獲一個檢查性異常,那麼該方法必須使用throws 關鍵字來宣告。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
}
測試異常
package test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class testException {
/**
* 任何方法往外拋能處理的異常的時候都有一種簡單的寫法:“throws Exception”,
* 因為Exception類是所有能處理的異常類的根基類,因此丟擲Exception類就會丟擲所有能夠被處理的異常類裡了。
* 使用“throws Exception”丟擲所有能被處理的異常之後,這些被丟擲來的異常就是交給JAVA執行時系統處理了,
* 而處理的方法是把這些異常的相關錯誤堆疊資訊全部打印出來。
* @throws Exception
*/
void fn() throws Exception{
}
/**
* 已知異常的型別,方法宣告時使用throws把異常往外拋
* @param i
* @throws ArithmeticException
*/
void m1(int i) throws ArithmeticException{
}
/**
* 手動丟擲異常
* new了一個異常物件,在構建這個物件的時候還可以指定他相關的資訊,如這裡指明瞭異常資訊“i不能等於0”
* 這個物件丟擲去的時候使用getMessage()方法拿到的就是“i不能等於0”這種資訊。
* @param i
*/
void m2(int i) {
if (i==0) {
throw new ArithmeticException("i不能等於0");
}
}
/**
* 正常情況下如果這裡不寫try……catch語句那麼程式編譯時一定會報錯,
* 因為這裡有可能會產生兩個個必須要處理的異常:FileNotFoundException和IOException。
* 但由於在宣告方法f()時已經使用throws把可能產生的這兩個異常丟擲了,
* 所以這裡可以不寫try……catch語句去處理可能會產生的異常。
* f()方法把丟擲的異常交給下一個要呼叫它的方法去處理
* @throws FileNotFoundException
* @throws IOException
*/
void f()throws FileNotFoundException,IOException {
//這裡有可能會產生FileNotFoundException異常
FileInputStream fis=new FileInputStream("1.txt");
//這裡有可能會產生IOException異常
int b = fis.read();
while (b!=-1) {
System.out.println((char)b);
b=fis.read();
}
}
/**
* 在f2()方法裡面呼叫f()方法時必須要處理f()方法丟擲來的異常,
* 當然,如果f2()方法也沒有辦法處理f()方法丟擲來的異常,那麼f2()方法也可以使用throws把異常丟擲,
* 交給下一個呼叫了f2()的方法去處理f()方法丟擲來的異常。
* 這裡f2()呼叫f()方法時,選擇不處理f()方法中可能丟擲的異常,將異常繼續丟擲
* @throws Exception
*/
void f2() throws Exception {
f();
}
/**
* f3方法呼叫f方法捕獲f()方法丟擲的2個異常並進行處理
*/
void f3() {
try {
f();
} catch (FileNotFoundException e) {
System.out.println(e.getMessage()); //處理的方法是把錯誤資訊打印出來
} catch (IOException e) {
e.printStackTrace(); //處理的方法是使用printStackTrace()方法把錯誤的堆疊資訊全部打印出來。
}
}
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("MyFile.txt");
int b = fis.read(); //這個有可能會丟擲IOException異常
while (b != -1) {
System.out.println((char)b);
b = fis.read();
}
} catch (FileNotFoundException e) {
//使用catch捕獲FileNotFoundException類異常的異常物件e。並讓異常物件e自己呼叫printStackTrace方法打印出全部的錯誤資訊
e.printStackTrace();
} catch (IOException e) {
//再次使用catch捕獲IOException類的異常物件e,並讓異常物件e自己呼叫getMessage()方法將錯誤資訊打印出來。
System.out.println(e.getMessage());;
}finally{
try {
/**
* 前面已經把一個檔案打開了,不管開啟這個檔案時有沒有錯誤發生,即有沒有產生異常,最後都一定要把這個檔案關閉掉,
* 因此使用了finally語句,在finally語句裡面不管前面這個檔案開啟時是否產生異常,在finally這裡執行in.close()都能把這個檔案關閉掉,
* 關閉檔案也有可能會產生異常,因此在finally裡面也使用了try……catch語句去捕獲有可能產生的異常。
*/
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
以上程式碼編譯執行輸出結果如下:
java.io.FileNotFoundException: MyFile.txt (系統找不到指定的檔案。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at test.testException.main(testException.java:88)
Exception in thread "main" java.lang.NullPointerException
at test.testException.main(testException.java:107)
四、自定義異常
編寫自己的異常類時需要記住下面的幾點。
繼承java.lang.Exception;宣告自己的異常類。
如果希望寫一個檢查性異常類,則需要繼承Exception類。
如果你想寫一個執行時異常類,那麼需要繼承RuntimeException 類。
例項:自定義異常
package test;
public class MyException extends Exception{
private int id;
/**
* 自定義異常類的構造方法
* @param message
* @param id
*/
public MyException(String message,int id) {
super(message); //呼叫父類Exception的構造方法
this.id = id;
}
/**
* 獲取異常的程式碼
* @return
*/
public int getId() {
return id;
}
}
例項:自定義異常測試
package test;
import java.text.MessageFormat;
public class TestMyException {
//throws MyException,丟擲我們自定義的MyException類的異常。
public void regist(int num) throws MyException {
if (num < 0) {
//使用throw手動丟擲一個MyException類的異常物件。
throw new MyException("人數為負值,不合理", 1);
}
/**
* 注意:當我們丟擲了異常之後,
* System.out.println(MessageFormat.format("登記人數:{0}",num));是不會被執行的。
* 丟擲異常之後整個方法的呼叫就結束了。
*/
System.out.println(MessageFormat.format("登記人數:{0}",num));
}
public void manage() {
try {
regist(-100);
} catch (MyException e) {
System.out.println("登記失敗,錯誤碼:"+e.getId());
e.printStackTrace();
}
System.out.println("操作結束");
}
public static void main(String[] args) {
TestMyException t = new TestMyException();
t.manage();
}
}
登記失敗,錯誤碼:1
操作結束
test.MyException: 人數為負值,不合理
at test.TestMyException.regist(TestMyException.java:9)
at test.TestMyException.manage(TestMyException.java:21)
at test.TestMyException.main(TestMyException.java:32)
登記失敗,錯誤碼:1
操作結束
test.MyException: 人數為負值,不合理
at test.TestMyException.regist(TestMyException.java:9)
at test.TestMyException.manage(TestMyException.java:21)
at test.TestMyException.main(TestMyException.java:32)
文章參考:孤傲蒼狼