1. 程式人生 > 實用技巧 >Java(22)異常處理

Java(22)異常處理

異常處理

Java異常介紹

Java在程式執行過程中的不正常情況稱為異常。捕獲錯誤最理想時間是編譯時候,但是有些錯誤在執行時才會報錯。

Java程式執行過程中的異常主要分為兩大類:

  1. Error:JVM系統內部錯誤、資源耗盡等情況。(程式設計師只能處理Exception,對Error無能為力。)
  2. Exception:程式設計錯誤等一般問題,如空指標訪問、讀取不存在的檔案。。。
  • 異常案例1:陣列越界異常
public class Test01{
    public static int [] intarr={2,3,5};
    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) { // i迴圈了0,1,2,3,但是陣列長度只有3
            System.out.println(intarr[i]);
        }
    }
}
/*
編譯時不報錯,執行報錯:Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3 
*/
  • 異常案例2:空指標訪問異常
class Tennis{
    public int i=0;
}
public class Test01{
    public static void main(String[] args) {
        Tennis a=null; //a沒有指向任何東西
        System.out.println(a.i); 
        //編譯時不會報錯,執行時報錯:
        //Exception in thread "main" java.lang.NullPointerException
    }
}
  • 異常案例3:分母為0異常
public class Test01{
    public static int i=0;
    public static void main(String[] args) {
        float a=3/i;
    }
}
/*編譯時不會報錯,執行時報錯:
Exception in thread "main" java.lang.ArithmeticException: / by zero
*/

Java異常類的層級

執行異常是執行才能發現的異常,是我們的常見異常,上面舉的3個例子都是執行異常。

異常處理機制

Java異常處理:抓拋模型(捕獲或者丟擲)。

捕獲異常:

Java通過try...catch...finally捕獲異常,語法格式如下:

try{
	//可能產生異常的程式碼
}catch (ExceptionName1 e1){ //catch可以有多個
	// 當產生ExceptionName1異常時執行措施
}catch (ExceptionName2 e2){
	// 當產生ExceptionName2異常時執行措施
}finally { //finally部分可寫可不寫
	//無條件執行語句,無論有沒有異常都執行
}

案例1:

/**
 * 通過try...catch來捕獲處理:
 * try{
 *     
 * }catch(){
 *     
 * }
 */
public class Test01{
    public static int i=0;
    public static void main(String[] args) {
        //用try花括號括住可能出錯的程式碼
        try{   
            System.out.println(4/i); 
        }catch (Exception e){  //不知道捕獲什麼異常類時,可以使用異常的父類Exception
            System.out.println(e.getMessage()); //這行程式碼可以檢視捕獲的異常是什麼
            e.printStackTrace();  //這行程式碼也可以檢視捕獲的異常是什麼(列印方法呼叫棧)
            //catch(){}花括號裡面可以不寫任何內容
        }
        System.out.println("Kobe,this is for you");//這行不受上面是否報異常的影響,
    }
}
//執行結果為:Kobe,this is for you

案例2:

public class Test01{
    public static void main(String[] args) {
        String a=null;
        try{
            System.out.println(3/0);
            System.out.println(a.toString());
        }catch (java.lang.NullPointerException e1){
            System.out.println("捕獲到了java.lang.NullPointerException異常");
        }catch (java.lang.ArithmeticException e2){
            System.out.println("捕獲到了java.lang.ArithmeticException異常");
        }finally {
            System.out.println("emm");
        }
    }
}
/*執行結果為:
捕獲到了java.lang.ArithmeticException異常
emm
*/
/*
從執行輸出結果可知,程式並沒有把兩個異常都捕獲到了,只捕獲到了3/0產生的ArithmeticException異常,
這是因為捕獲異常本身的目的就是為了防止程式出現異常,如果try{}花括號裡面前面的內容出現異常,就不會執行後面的內容了,所以沒有捕獲到a.toString()的空指標異常。
*/
丟擲異常:

使用方式:throws+try...catch

案例1:

package day01;

public class Test01 {
    public static void main(String[] args) {
        Flower a=new Flower();
        //呼叫fun1()時進行捕獲異常:
        try{  
            a.fun1();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
class Flower{
    void fun1() throws Exception{  //已經知道會報錯,丟擲異常
        System.out.println(34/0);
    }
}

如果有丟擲,某個上層呼叫就要有捕獲處理。丟擲異常,是當前方法不處理異常,但是把異常往上層的呼叫棧傳遞,由上層選擇捕獲異常進行處理,還是選擇繼續往更上層丟擲。如果main()方法丟擲異常,異常交由虛擬機器JVM處理。

案例2:

package day01;

class Flower{
    static void fun1() throws Exception{  //異常從這裡開始被丟擲
        System.out.println(34/0);
    }
}
class Bee{
    static void fun2() throws Exception{  //fun1()被呼叫,但是異常沒有被捕獲處理,繼續丟擲
        Flower.fun1();
    }
}
public class Test01 {
    public static void main(String[] args) { //fun2()被呼叫,異常被捕獲處理
        try{
            Bee.fun2();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
/*執行結果為:
java.lang.ArithmeticException: / by zero
	at day01.Flower.fun1(Test01.java:5)
	at day01.Bee.fun2(Test01.java:10)
	at day01.Test01.main(Test01.java:16)
*/
/*
Main呼叫fun2(),fun2()呼叫fun1()
printStackTrace()打印出了方法的呼叫棧,並給出了原始碼的行號。
*/

案例3:如果異常被捕獲並且繼續丟擲不同型別的異常(異常型別轉換了),會發生什麼?

package day01;

class Vege{
    static void fun1(String s){
        if(s==null){
            throw new NullPointerException();
        }else {
            System.out.println(s);
        }
    }
}
class Ora{
    static void fun2(){
        try{
            Vege.fun1(null);
        }catch (NullPointerException e1){
            throw new IllegalArgumentException();
        }
    }
}
class Test01{
    public static void main(String[] args) {
        try {
            Ora.fun2();
        }catch (IllegalArgumentException e2){
            e2.printStackTrace();
        }
    }
}
/*執行結果為:
java.util.NoSuchElementException
	at day01.Ora.fun2(Test01.java:22)
	at day01.Test01.main(Test01.java:29)
*/
/*
從執行結果可以看到,看不到原始的空指標異常的資訊了,如果想要檢視完整異常棧,需要新增引數,將本原始碼的
第18行的throw new IllegalArgumentException();
改成throw new IllegalArgumentException(e1);
*/

在Java中已經可以捕獲異常了,為什麼還要丟擲異常再進行捕獲,這種機制有什麼作用?個人目前理解是,為了使已經知道可能出現異常的方法繼續用下去,把抓到的異常丟擲給呼叫者,讓呼叫者來處理。

異常的遮蔽:
package day01;

class Police{
    public static void main(String[] args) {
        try{
            System.out.println(4/0);
        }catch (Exception e1){
            throw new RuntimeException(e1);
        }finally {
            
        }
    }
}
/*此處進行了異常轉型,執行結果為:
Exception in thread "main" java.lang.IllegalArgumentException
	at day01.Police.main(Test01.java:10)
*/

//----------------------如果finally也丟擲異常:---------------
package day01;

class Police{
    public static void main(String[] args) {
        try{
            System.out.println(4/0);
        }catch (Exception e1){
            throw new RuntimeException(e1);
        }finally {
            throw new IllegalArgumentException();
        }
    }
}
/*執行結果為:
Exception in thread "main" java.lang.IllegalArgumentException
	at day01.Police.main(Test01.java:10)
*/

//從上面兩段原始碼執行結果可以知道,如果catch和finally同時準備丟擲異常,catch的異常會被遮蔽不丟擲