1. 程式人生 > 實用技巧 >Java異常總結

Java異常總結

一、異常的概述

​ 異常定義:在程式中,發生“不正常”的事件,導致程式無法正常執行,並使JVM中斷,稱為異常

​ 生活中的異常:早上起床上課,平時騎車20分鐘可以到達教室,由於天氣原因或鬧鐘響了自動關閉,不能按時到達教室上課,遲到了,此時就屬於異常現象。

​ 捕獲異常:當程式在執行時,發生了異常,為了讓程式正常執行,需要對異常捕獲(catch),稱之為捕獲異常

​ Java是面向物件的語言,異常本身就是一個類(Exception),當發生異常時會建立異常物件,捕獲的就是該物件。

 System.out.println("請輸入一個數字");
        Scanner sc = new Scanner(System.in);
        // 對可能發生的異常 進行處理
        int num = sc.nextInt();
        if(num%2==0){
            System.out.println("這個數是偶數");
        }

​ 以上程式碼可能發生異常,當用戶輸入非數字時,導致程式丟擲一個異常物件:

Exception in thread "main" java.util.InputMismatchException
	at java.util.Scanner.throwFor(Scanner.java:864)

二、異常捕獲(異常關鍵字以及層次關係)

a、try:試一試,將可能發生的程式碼使用try包裹,try不能單獨出現

b、catch:捕獲異常,當發生指定的異常物件時,執行catch程式碼

  System.out.println("請輸入一個數字");
        Scanner sc = new Scanner(System.in);
        // 對可能發生的異常 進行處理
        try {
            int num = sc.nextInt();  // 發生異常後,try裡面的程式碼不再執行
            if (num % 2 == 0) {
                System.out.println("這個數是偶數");
            }
            System.out.println("結束");
        }catch(Exception ee){// 對應的異常類 來捕獲對應的異常物件  ,不能確定異常類,可以使用父類Exception
            System.out.println("你的輸入不正確");
        }

        System.out.println("程式繼續執行直到結束。。。。");

一個try+多個catch

//  丟擲的異常 不能被catch捕獲,會發生什麼?
        try {
            int[] num = {1, 2, 3};
            System.out.println(num[1]); // 沒有捕獲該異常物件,JVM依然終止執行
            System.out.println(10/0);
        }catch(NullPointerException ee){
            System.out.println("這是空指標異常");
        }catch(ArrayIndexOutOfBoundsException  ee){
            System.out.println("陣列下標越界異常");
        }catch(Exception ee){
            // 輸出異常 堆疊訊息  方便程式設計師排錯(儘可能避免使用者看見)
            ee.printStackTrace();
            System.out.println("系統繁忙!"+ee.getMessage());
        }
        System.out.println("程式結束");

c、finally:異常之後的最終處理(無論是否發生異常,程式都執行)

try......finally結構

 try{
            System.out.println("請輸入兩個數 ,計算兩個數相除");
            Scanner sc = new Scanner(System.in);
            int  num1 =  sc.nextInt();
            int num2 = sc.nextInt();
            double  s = num1/num2; // 可能出錯
            System.out.println(" try裡面結束,結果:"+s);
        }finally{
            System.out.println("無論是否發生異常,都會執行這個語句塊,一般用於資源回收");
        }

try...catch...finaally結構

 try {
            System.out.println("請輸入兩個數 ,計算兩個數相除");
            Scanner sc = new Scanner(System.in);
            int num1 = sc.nextInt();
            int num2 = sc.nextInt();
            double s = num1 / num2; // 可能出錯
            System.out.println(" try裡面結束,結果:" + s);
        }catch(ArithmeticException ee){
            ee.printStackTrace();
            System.out.println("除數不能為0 !!");
        }catch(Exception ee){
            ee.printStackTrace();
            System.out.println("系統繁忙!!!");
        }finally {
            System.out.println("用於資源回收。");
        }

三、丟擲異常

 /**
     * 根據下標訪問陣列元素
     * @param array
     * @param index
     * @return
     */
    public static int getEleByIndex(int [] array , int index){
         // 丟擲異常: 可以在異常發生時 或發生之前 建立一個異常物件並丟擲
        //  手動丟擲一個異常  throw new 異常類([異常訊息]);
        if(index <0 || index > array.length-1){
            //丟擲異常
            throw new ArrayIndexOutOfBoundsException("你的下標越界了");
        }
        int n =  array[index];
        return n;
    }

public static void main(String[] args) {
          //陣列
        int  [] array = {2,1,4,5};
        int index=4;
        // 定義方法訪問下標的元素  此時會產生異常 並丟擲給方法的呼叫者
        try {
            int num = getEleByIndex(array, index);
            System.out.println("訪問的元素:" + num);
        }catch(ArrayIndexOutOfBoundsException ee){
            System.out.println(ee.getMessage());
        }
        System.out.println("結束。。。");

    }

四、異常分類

由於有些異常時不能直接丟擲的,需要先宣告才可以丟擲,異常可以分為兩大類:

1、編譯期異常(check異常或者檢查異常):在編譯期間檢查異常,如果沒有處理異常,則編譯出錯。

 //建立一個檔案類的物件
        File  file = new File("d:/aaa.txt");
         // 在寫程式碼(編譯之前)時 一定要處理的異常(try..catch 或者 throws),就是編譯時異常 
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }

​ 這裡的IOException就是編譯期異常,需要手動處理的

2、執行期異常(runtime異常或者執行異常):在執行期間檢查異常,編譯期可以不處理異常。

// 在執行期間丟擲異常  不需要事先處理的  NullPointException是執行異常
        String str=null;
        System.out.println(str.length());


Exception中常用的異常類

  • RuntimeException
    • ArrayIndexOutOfBoundsException:陣列下標越界異常
    • NullPointerException:空指標異常
    • ArithmeticException:算數異常
    • NumberFormatException:數字格式化異常
    • ClassNotFoundException:類沒有找到異常
    • ClassCastException:類轉換異常
  • 檢查異常(check Exception)
    • IOException:IO操作異常
    • FileNotFoundException:檔案未找到異常
    • SQLException:資料庫異常
    • EOFEXception:讀寫檔案尾異常
    • ParseException:日期格式化異常
    • SocketException:資料傳輸異常

注意:對於丟擲檢查異常,需要使用throws宣告,對於丟擲執行時異常,必須要使用throws宣告

宣告丟擲異常語法:

宣告丟擲異常語法:
public	... 方法名([引數]) throws 異常類1,異常類2{
	//通過throws丟擲 或 處理 檢查異常
}
/**
     *   宣告丟擲異常語法:
     *       public ...  方法名([引數]) throws 異常類1,異常類2{
     *
     *       }
     */
    //建立檔案
    public  static void createFile() throws FileNotFoundException ,IOException {
        File file = new File("d:/hello.txt");
        if(file.exists()){
             // 不能建立  ,需要提示使用者  該檔案存在
            throw new FileNotFoundException("這個檔案已存在,不能建立");
        }else{
            //建立
            file.createNewFile();
        }
    }

throws用於進行異常類的宣告,若該方法可能有多種異常情況產生,那麼在throws後面可以寫多個異常類,用逗號隔開。
面試題:關於finally和return的執行順序問題?

回答:當方法有返回值時,先執行finally,再return,但是finally的程式碼不會改變return結果

  /**
     *  方法有返回值 有 finally
     * @param n
     * @return
     */
    public static  int  getNum(int n){
          try{
                if(n%2==0){
                    n++;
                }else{
                    n--;
                }
      
             return n;    
          }catch(Exception ee){
              System.out.println("catch--"+n);
            return 0;
          }finally {
              // return 如果放在 try或catch中,不會受finally的改變
              //  如果放在最下面,會受finally的改變
              n++; // 5
              System.out.println("fially----n:" + n); // 5
          }  
    }
	public static void main(String[] args) {
        int m=getNum(5);
        System.out.println(m);
    }

結果返回

finally----n:5
4

當只有在try或者catch中呼叫退出JVM的相關方法,此時finally才不會執行,否則finally永遠會執行。

異常注意事項

a、執行時異常被丟擲可以不處理。即不捕獲也不宣告丟擲。
b、如果finally有return語句,永遠返回finally中的結果,避免該情況.
c、如果父類丟擲了多個異常,子類重寫父類方法時,丟擲和父類相同的異常或者是父類異常的子類或者不丟擲異
常。
d、父類方法沒有丟擲異常,子類重寫父類該方法時也不可丟擲異常。此時子類產生該異常,只能捕獲處理,不
能宣告丟擲

五、自定義異常

5.1、為什麼需要使用自定義異常

​ 在Java中每一個異常類都表示特定的異常型別,例如NullPointerException表示空指標,ArithmeticException表示算數異常,但是sun公司提供的API中不可能將實際專案中的業務問題全部定義為已知的異常類,這時需要程式設計師根據業務需求來定製異常類,例如使用者註冊,可以定義使用者註冊異常(RegisterException),分數不可能為負數也可以定製異常(ScoreException)。

5.2、什麼是自定義異常

在開發中根據自己的業務情況來定製異常類,靈活性較高,且方便易用。

5.3、如何實現自定義異常

a、定義編譯期異常類,建立一個類繼承java.lang.Exception;

b、定義執行期異常類,建立一個類繼承java.lang.RuntimeException;

5.4、案例分析:自定義異常應用

​ 要求:模擬使用者註冊操作,使用者輸入使用者名稱,驗證使用者名稱是否存在,如果存在,則丟擲一個異常訊息“親,該使用者已存在,不能註冊”,通過自定義異常提示訊息

public class RegisterException  extends  Exception {
    public RegisterException(){

    }

    public RegisterException(String message){
        // 將message 賦值給父類的構造
        super(message); //  將message賦值給父類的 屬性,可通過getMessage()方法

    }
}
public class TestRegister {
     // 模擬已存在的使用者
    String []  users = {"袁魏巍","王麻子","王小花"};

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("請輸入你要註冊的使用者:");
        String uname = sc.next();
        TestRegister obj = new TestRegister();

        try {
            // 呼叫方法
            obj.checkUserName(uname);
            System.out.println("註冊成功");
        } catch (RegisterException e) {
            System.out.println("註冊失敗");
            System.out.println(e.getMessage());
        }


    }

    /**
     * 檢查使用者是否存在
     * @param username
     * @return   true  表示通過
     *    異常表示不通過
     */
    public boolean  checkUserName(String username) throws RegisterException{
         // 使用foreach遍歷
        /**
         *   for(資料型別 變數名  : 陣列名/集合名 ){
         *        迴圈中的 變數名代表的就是陣列的元素
         *   }
         */
        for(String  u : users){
            // 判斷u是否與 username相等 ,相等說明使用者存在,需要丟擲異常
            if(u.equals(username)){
                throw new RegisterException("親,"+username+" 已存在,不能註冊");
            }
        }
        return true;

    }

}