1. 程式人生 > >學習java異常處理機制個人的一些理解

學習java異常處理機制個人的一些理解

學習java已經比較久一段時間了,其實對於java異常這方面的知識只停留在javac讓我throws一下或trycatch一下我就直接做了,其實並沒有真的瞭解整個java的異常處理機制是怎樣的,這幾天就深入的去了解了異常處理機制,下面我講一下對於java異常處理機制的一些個人的理解。

在講之前我先說一下學異常的時候我覺得異常處理沒什麼用的想法。看一些異常的講解,都說什麼除0異常,檔案不存在異常。那時我心裡就在覺得挺奇怪的,既然有可能出現除0,那我加個判斷不就可以了?既然檔案可能不存在,那我也加個判斷不就可以了?too young to simple,如果你也有我當時的疑惑,可以看下知乎的解答,這裡就不過多闡述。

連結:https://www.zhihu.com/question/27122172

異常(Exception)

    異常是什麼?不正常的事件。執行程式時,它沒有按照我們的預期執行,也就是不正常的運作,就叫異常。

首先

當我們寫java程式的時候,我們覺得這個地方可能會有異常(比如檔案不存在),我們得怎麼做?1.要麼自己處理吧?(try、catch)2.要麼丟擲去給呼叫者處理吧?(throws)

其次

當我們寫java程式的時候出現了異常,我們得知道異常的什麼? 1.是什麼異常得知道吧?(異常的分類) 2.異常出現在哪裡得知道吧?(異常鏈)

java解決上面需求的,就是我們所說的java異常處理機制

我將從以下方面講我所理解的java異常處理機制

1.異常類
2.異常處理語句及語法
3.異常鏈(異常棧)
4.常見異常
5.自定義異常

1.異常類

java中所有的事件、機制都由類描述,Java中的異常就是由java.lang包下的異常類描述的。

java異常類導圖


這是我根據java官方文件用Xmind做的JDK8的異常類導圖,可以看出java的異常都是繼承Throwable父類的。

Throwable有派生出Error和Exception子類

Error:

An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions. The ThreadDeath error, though a "normal" condition, is also a subclass of Error because most applications should not try to catch it

A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method but not caught, since these errors are abnormal conditions that should never occur. That is, Error and its subclasses are regarded as unchecked exceptions for the purposes of compile-time checking of exceptions.

以上兩段是官方文件對Error類的解釋,簡單來說就是這些錯誤都是不能試圖去捕獲或丟擲,因為這些錯誤是不能被程式設計師通過程式碼去處理的,比如記憶體不足之類的,JVM本身錯誤等。Error類及其子類都是非檢查異常。

Exception:

The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch.

The class Exception and any subclasses that are not also subclasses of RuntimeException are checked exceptions. Checked exceptions need to be declared in a method or constructor's throws clause if they can be thrown by the execution of the method or constructor and propagate outside the method or constructor boundary.

以上兩段也是官方文件對Exception類的描述,大體上說就是可以讓程式設計師通過程式碼去處理(try..catch / throws),並說明除了RuntimeException及其之類外的其他異常都是檢查異常。

上面提到的非檢查異常和檢查異常:

非檢查異常(unchecked exception):包括Error和RuntimeException以及他們的子類。java編譯器在編譯時,不會提示和發現這樣的異常,不要求在程式預處理這些異常(比如不會在寫程式時報小燈泡紅叉標識要你trycatch或者throws)。當然如果你覺得可能會有異常就可以使用try..catch處理或throws丟擲。

檢查異常(checked exception):包括除了Error和RuntimeException的其他異常。java編譯器強制要求程式設計師預處理(通過報小燈泡紅叉要求你必須使用try..catch或throws)不然編譯器就會報錯不讓你通過。

一般的,作為程式設計師的我們應該關注Exception類下的分支的異常。

以上便是自己對java異常類的一些理解以及分類的講解。

2.異常處理語句及語法

在寫程式碼處理異常的時候,有兩種不同的處理方式:1.使用try..catch..finally處理. 2.使用throws宣告拋給函式呼叫者去處理

1.try...catch..finally語句塊

try{
     //1.存放可能發生異常的程式碼。
     //2.如果沒有發生異常,執行finally塊程式碼(如果有finally塊)並往下執行程式碼,否則,直接嘗試去匹配catch塊。
 
}catch(IOException e1){
    //每一個catch塊用於捕獲並處理一個特定的異常,或者這異常型別的子類。
    //catch後面的括號定義了異常型別和異常引數(區域性的)。如果異常與之匹配且是最先匹配到的,則虛擬機器將使用這個catch塊來處理異常。
    //在catch塊中可以使用這個塊的異常引數來獲取異常的相關資訊(getMessage()、printStackTrace()、getCause())。
}catch(Exception e2){
    //...
}finally{
    //finally塊可要可不要。
   //一個try至少要有一個catch塊,否則, 至少要有1個finally塊。但是finally不是用來處理異常的,finally不會捕獲異常。
  //finally主要做一些清理工作,如流的關閉,資料庫連線的關閉等。 
}

需要注意的是:

1.try塊發生異常,那麼try塊中發生異常的那一行以下的程式碼都不會執行。

2.無論異常發生與否,異常是否有catch匹配處理,finally塊都會執行。

3.如果try..catch..中有return 先執行finally,再執行return

package test;

public class TestException3 {  
       
    public static void main(String args[]) { // 主方法  
    	TestException3 t=new TestException3();
    	int i=t.test();
    	System.out.println(i);
    }  
  
    public  int test(){
    	try {
    		int c=1/0;
    		return 0;
    	}catch(Exception e){
    		e.printStackTrace();
    		return 1;
    	}finally {
    		System.out.println("finally");
    	}
    }
}

執行結果:

java.lang.ArithmeticException: / by zero
	at test.TestException3.test(TestException3.java:16)
	at test.TestException3.main(TestException3.java:10)
finally
1

個人想法:catch塊中的異常型別,其實我覺得直接就一個Exception就好啦,什麼異常都直接捕獲,就不用再細分可能出現什麼異常了,一來程式碼簡潔二來防止漏掉異常。

2.throws宣告

    public void test2() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN
    { 
         //函式內部可以丟擲 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 類的異常,或者他們的子類的異常物件。
    }

throws是另一種異常處理的方式,作用是將函式中可能出現的異常拋給呼叫者來處理(向呼叫者宣告),自己不作處理。

一般使用throws有兩種原因:

1.方法本身不知道如何處理這樣的異常,或者說讓呼叫者處理更好,呼叫者需要為可能發生的異常負責。

2.分層思想:比如web開發的service和dao層,在大部分情況下,dao服務於service,但這都屬於開發組內部,所以,dao直接throw就可以了。但是到了service層,呼叫service的是另外的開發小組(比如客戶端小組),從架構上來說,就需要隱藏程式碼的內部細節,這個時候就必須要catch掉。底層不處理,往上拋,上層去try catch然後去處理對應異常型別。再根據需求展示給使用者。

3.異常鏈(異常棧)

Java這種向上傳遞異常資訊的處理機制,形成異常鏈

異常鏈的概念我們用一段程式碼來理解就好了

package test;
public class TestException3 {  
    public static void main(String args[]) { // 主方法  
    		test1();
    }  
    public static  void test1(){
    	try {
    		test2();
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    }
    public static void test2(){
    	try {
    		test3();
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    }
    public static  void test3(){
    	try {
    		int c=1/0;
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    }
}  

執行結果:

java.lang.ArithmeticException: / by zero
	at test.TestException3.test3(TestException3.java:27)
	at test.TestException3.test2(TestException3.java:19)
	at test.TestException3.test1(TestException3.java:11)
	at test.TestException3.main(TestException3.java:6)

我寫了三個測試函式,除0異常出現在test3,然後test2呼叫test3,test1呼叫test2,main函式呼叫test1。

從異常結果可以看出main函式呼叫了test1發生了異常,把異常位置壓棧以此類推直到找出異常的真正位置,然後按棧先進後出依次輸出。

官方的說法就是Java方法丟擲的可查異常將依據呼叫棧、沿著方法呼叫的層次結構一直傳遞到具備處理能力的呼叫方法,最高層次到main方法為止。

4.常見異常

看了下面的異常,是不是覺得很熟悉,很想打人哈哈哈。

   1、 ArrayIndexOutOfBoundsException
    陣列索引越界異常。當對陣列的索引值為負數或大於等於陣列大小時丟擲。
   2、ArithmeticException
    算術條件異常。譬如:整數除零等。
   3、NullPointerException
    空指標異常。當應用試圖在要求使用物件的地方使用了null時,丟擲該異常。譬如:呼叫null物件的例項方法、訪問null物件的屬性、計算null物件的長度、使用throw語句丟擲null等等
   4、java.lang.ClassNotFoundException
    找不到類異常。當應用試圖根據字串形式的類名構造類,而在遍歷CLASSPAH之後找不到對應名稱的class檔案就丟擲該異常。
   5、java.lang.NegativeArraySizeException  陣列長度為負異常
   6、java.lang.ArrayStoreException 陣列中包含不相容的值丟擲的異常
   7、java.lang.SecurityException 安全性異常
   8、java.lang.IllegalArgumentException 非法引數異常
   9、IOException:操作輸入流和輸出流時可能出現的異常。
  10、EOFException   檔案已結束異常
  11、FileNotFoundException   檔案未找到異常
  12、ClassCastException    型別轉換異常類
  13、ArrayStoreException  陣列中包含不相容的值丟擲的異常
  14、SQLException   操作資料庫異常類
  15、NoSuchFieldException   欄位未找到異常
  16、NoSuchMethodException   方法未找到丟擲的異常
  17、NumberFormatException    字串轉換為數字丟擲的異常
  18、StringIndexOutOfBoundsException 字串索引超出範圍丟擲的異常
  19、IllegalAccessException  不允許訪問某類異常

  20、InstantiationException  當應用程式試圖使用Class類中的newInstance()方法建立一個類的例項,而指定的類物件無法被例項化時,丟擲該異常

5.自定義異常

除了用java內建的異常類異常,我們還可以自定義自己所需要的異常。方法也很簡單

1.建立自己的異常類並寫自己的異常處理程式碼

2.繼承Exception類(extends Exception)

3.在異常發生處的catch塊的異常型別中使用

package test;
public class ExceptionTest extends  Exception{  
   public void test(){
	   System.out.println("自定義異常");
   }
} 
package test;
public class TestException3 {  
    public static void main(String args[]) { // 主方法  
    	int i=1;
    	if(i==1) {
    		try {
				throw new ExceptionTest();
			} catch (ExceptionTest e) {
				e.test();
			}
    	}
    }
}

執行結果:自定義異常