1. 程式人生 > >Java 常用類之異常處理

Java 常用類之異常處理

Java異常類是對於程式中可能出現的錯誤或者異常的一種處理方式。在設計程式的過程中,對於可能出現的異常錯誤,比如說使用者輸入錯誤,裝置錯誤,磁碟滿了或者程式碼錯誤等等,通常採用異常處理的方式來進行處理可能的錯誤。 JAVA的異常處理機制:如果某個方法不能按照正常的途徑完成任務,就可以通過另一種路徑退出該方法,並處理可能出現的錯誤。在這種情況下會丟擲一個封裝了錯誤資訊的物件。 這個方法會立刻退出同時不返回任何值。另外,呼叫這個方法的其他程式碼也無法繼續執行,異常處理機制會將程式碼執行交給異常處理器。

(一)Java異常

異常:導致程式中斷執行的一種指令流。 在理想的情況下,程式完全按照我們設計的流程來執行,但是很多時候會出現這樣或者那樣的錯誤,如檔案找不到,磁碟滿了或者程式碼錯誤等,這些錯誤會影響程式的正常執行,對於這種情況,就有了異常處理情況,即使程式異常了,它也是按照某種邏輯在執行,只是沒有按照我們給它安排的邏輯執行。異常在Java中定義為Throwable類,其結構層次圖如下:
異常處理層次圖

由上圖可以看出,Thowable有兩個重要的子類,一個是Error類,另一個是Expection類,每一個子類下面還有很多小的分類。

Error類指的是Java執行時系統的內部錯誤或者資源耗盡錯誤, 這是程式無法處理的錯誤,表示執行應用程式中較嚴重問題,對於這類問題,JVM告知使用者,並盡力終止程式。
Expection類指的是 程式本身可以處理的異常。主要分為兩類,RuntimeException類異常或者其他異常,由程式錯誤導致的異常稱之為RuntimeExpection,比如說:錯誤的型別轉化,陣列訪問過界,訪問空指標等。而程式本身沒有錯誤,像I/O錯誤這類問題所導致的異常稱之為其他異常,比如說試圖開啟一個不存在的檔案或類。

Java 異常類的另一種分類方式是:可查的異常(checked exceptions)和不可查的異常(unchecked exceptions)。

可查的異常(checked exceptions):正確的程式在執行時,出現情理可容的異常,除了RuntimeException及其子類以外,其他的Exception類及其子類都屬於可查異常。對於此類異常,要麼用try-catch語句捕獲它,要麼用throws子句宣告丟擲它,否則編譯不會通過。
可不查的異常(unchecked exceptions): 包括RuntimeException及其子類和Error。 編譯器不要求強制處理的異常。
(二) 異常處理


在 Java 應用程式中,異常處理機制為:丟擲異常,捕捉異常

丟擲異常:在執行一個方法時,如果發生異常,則這個方法生成代表該異常的一個物件,並停止當前執行路徑,並將此異常提交給系統。首先像建立普通的java物件一樣,使用new在堆上建立一個異常物件;然後,當前的執行路徑被終止,並且從當前環境中彈出對異常物件的引用。此時,異常處理機制接管程式,並開始尋找一個恰當的地方繼續執行程式,這個恰當的地方就是異常處理程式或者異常處理器,它的任務是將程式從錯誤狀態中恢復,以使程式要麼換一種方式執行,要麼繼續執行下去。

捕捉異常:當系統捕捉到該異常後,尋求相應的程式碼來看處理該異常,在方法的呼叫棧中查詢合適·的異常處理器,從生成異常的方法開始回溯,直到找到相應的異常處理程式碼,並在控制檯上列印異常資訊,包括異常的資訊的堆疊的內容。

Java異常處理涉及到五個關鍵字,分別是:try、catch、finally、throw、throws

  • 處理方法1 捕捉異常:try-catch-finally語句
    try -catch-finally語句是常用的異常處理語句,其結構如下:
      try { 
      code1;
    // 可能會發生異常的程式程式碼 
       } catch (Type1 id1) {   
        code2
         // 捕獲並處理try丟擲的異常型別Type1 
          } catch (Type2 id2) {
         code3
                      // 捕獲並處理try丟擲的異常型別Type2  
                      } finally {  
                      code4 
                      // 無論是否發生異常,都將執行的語句塊
                        }

其邏輯框圖如下:
在這裡插入圖片描述

  • try: 制定一段程式碼,即一次性捕獲並處理的範圍,用於監聽。將要被監聽的程式碼(可能丟擲異常的程式碼)放在try語句塊之內,當try語句塊內發生異常時,(1)程式將跳出try語句塊中的其餘程式碼,(2)轉而執行catch中的處理器程式碼,異常就被丟擲 。
    注:當異常程式碼執行完成以後,try語句塊中尚未執行的語句不會再執行。
    一個try語句必須有至少一個catch或finally語句。
  • catch用於處理不同型別的異常。捕獲順序為:越是底層的類,越放在下面。常見的方法有:
    toString():顯示異常的類名和產生的原因。
    geyMessage( ) : 顯示異常的原因。
    printstackTrack() :顯示異常發生時堆疊的內容。
  • finally:不管異常處理與否,必須要執行的程式碼塊。比如說關閉外界的資源。
  • 當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。在以下4種特殊情況下,finally塊不會被執行:
    1)在finally語句塊中發生了異常。
    2)在前面的程式碼中用了System.exit()退出程式。
    3)程式所在的執行緒死亡。
    4)關閉CPU。
    異常執行圖示:
    在這裡插入圖片描述
    程式碼示例:
public class TestException 
 { 
     public static void main(String args[]) 
     {  int i = 0; 
      String greetings[] = { " Hello world !", " Hello World !! ",    " HELLO WORLD !!!" }; 
       while (i < 4) { 
         try {    // 特別注意迴圈控制變數i的設計,避免造成無限迴圈   
          System.out.println(greetings[i++]);   
          } 
          catch (ArrayIndexOutOfBoundsException e) {
              System.out.println("陣列下標越界異常");  
               } finally 
               {    
               System.out.println("--------------------------");  
               }
           }
       }
 }
  • 處理方法2 丟擲異常 Throws、Throw 用法
    如果遇到無法處理的情況,Java便會丟擲一個異常,即告訴編譯器需要返回什麼值,以及可能出現哪些錯誤。

public void test() throws FileNotFoundException {
method();
}
public void method() throws FileNotFoundException {
//一個會丟擲異常的方法
method2();
}
//這裡 方法後是throws
public void method2() throws FileNotFoundException {
//這裡是throw
throw new FileNotFoundException();
}

  • Throw總是出現在函式體中, 表示手動丟擲一個異常,丟擲異常的時候,直接在 Throw 後面新增異常的例項即可。 用來丟擲一個Throwable型別的異常。程式會在throw語句後立即終止,它後面的語句執行不到,然後在包含它的所有try塊中(可能在上層呼叫函式中)從裡向外尋找含有與其匹配的catch子句的try塊。
 
public class Test  {

    public static void main(String[] args) {
        try {
            throw new TestException20180809("自定義異常:天王蓋地虎");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • Throws 宣告過的方法表示此方法不處理異常,找到一個合適的異常類,建立這個類的物件,並將物件丟擲。
import java.lang.Exception;
public class TestException {
	static void pop() throws NegativeArraySizeException {
		// 定義方法並丟擲NegativeArraySizeException異常
		int[] arr = new int[-3]; // 建立陣列
	}
 
	public static void main(String[] args) { // 主方法
		try { // try語句處理異常資訊
			pop(); // 呼叫pop()方法
		} catch (NegativeArraySizeException e) {
			System.out.println("pop()方法丟擲的異常");// 輸出異常資訊
		}
	}
  • 自定義異常類:在程式中,如果標準異常類並不能夠充分的描述問題,則需要自己來定義一個異常類來解決問題,只需繼承 Exception 即可。可以通過以下步驟定義異常類(1)建立自定義異常類。(2)在方法中通過throw關鍵字丟擲異常物件。(3)如果在當前丟擲異常的方法中處理異常,可以使用try-catch語句捕獲並處理;否則在方法的宣告處通過throws關鍵字指明要丟擲給方法呼叫者的異常,繼續進行下一步操作。(4)在出現異常方法的呼叫者中捕獲並處理異常。
import java.lang.Exception;
public class TestException {
	static int quotient(int x, int y) throws MyException { // 定義方法丟擲異常
		if (y < 0) { // 判斷引數是否小於0
			throw new MyException("除數不能是負數"); // 異常資訊
		}
		return x/y; // 返回值
	}
	public static void main(String args[]) { // 主方法
		int  a =3;
		int  b =0; 
		try { // try語句包含可能發生異常的語句
			int result = quotient(a, b); // 呼叫方法quotient()
		} catch (MyException e) { // 處理自定義異常
			System.out.println(e.getMessage()); // 輸出異常資訊
		} catch (ArithmeticException e) { // 處理ArithmeticException異常
			System.out.println("除數不能為0"); // 輸出提示資訊
		} catch (Exception e) { // 處理其他異常
			System.out.println("程式發生了其他的異常"); // 輸出提示資訊
		}
	}
}
class MyException extends Exception {            // **建立自定義異常類**
	String message; // 定義String型別變數
	public MyException(String ErrorMessagr) { // 父類方法
		message = ErrorMessagr;
	} 
	public String getMessage() { // 覆蓋getMessage()方法
		return message;
	}
} 

(三)Java常見異常
五種常見的執行時異常

ClassCastException(類轉換異常)
IndexOutOfBoundsException(陣列越界)
NullPointerException(空指標)
ArrayStoreException(資料儲存異常,運算元組時型別不一致)
IO操作的BufferOverflowException異常

非執行時異常必須得捕獲,否則編譯不過去,java編譯器要求程式設計師必須對這種異常進行catch,Java認為Checked異常都是可以被處理(修復)的異常,所以Java程式必須顯式處理Checked異常。常見的非執行異常有io異常和sql異常。

IOException、FileNotFoundExcetion 和SQLException

(四)當異常遇到return
在一個方法中,無論 Try 塊中有沒有異常、Return,只要 Finally 塊中有 Return,那麼函式的返回值都由 Finally 塊提供。
Java異常總結:(1)一個圖:異常型別圖。(2)五個關鍵字,try, catch finally throws throw
(3)繼承關係:先大後小,(4)異常和重寫:子類重寫異常範圍不能超出父類。
例項說明:

public class TestException {
	public TestException() {
	}
 
	boolean testEx() throws Exception {
		boolean ret = true;
		try {
			ret = testEx1();
		} catch (Exception e) {
			System.out.println("testEx, catch exception");
			ret = false;
			throw e;
		} finally {
			System.out.println("testEx, finally; return value=" + ret);
			return ret;
		}
	}
 
	boolean testEx1() throws Exception {
		boolean ret = true;
		try {
			ret = testEx2();
			if (!ret) {
				return false;
			}
			System.out.println("testEx1, at the end of try");
			return ret;
		} catch (Exception e) {
			System.out.println("testEx1, catch exception");
			ret = false;
			throw e;
		} finally {
			System.out.println("testEx1, finally; return value=" + ret);
			return ret;
		}
	}
 
	boolean testEx2() throws Exception {
		boolean ret = true;
		try {
			int b = 12;
			int c;
			for (int i = 2; i >= -2; i--) {
				c = b / i;
				System.out.println("i=" + i);
			}
			return true;
		} catch (Exception e) {
			System.out.println("testEx2, catch exception");
			ret = false;
			throw e;
		} finally {
			System.out.println("testEx2, finally; return value=" + ret);
			return ret;
		}
	}
 
	public static void main(String[] args) {
		TestException testException1 = new TestException();
		try {
			testException1.testEx();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

執行結果:

i=2
i=1
testEx2, catch exception
testEx2, finally; return value=false
testEx1, finally; return value=false
testEx, finally; return value=false