JavaSE學習筆記(二十四)—— 異常

1、異常概述

1.1 什麼是異常

  異常就是程序出現了不正常的狀況。html

  發現錯誤的理想時機是在編譯期。然而,編譯器並不能發現全部的錯誤,餘下的問題就須要在程序運行時解決。這就須要錯誤能經過某種方式,把適當的信息傳遞給特定的接收者處理。Java中的異常處理的目的在於經過使用少許的代碼來簡化大型、可靠的程序的生成,經過此方式讓你的應用中沒有未處理的錯誤,並且它還帶來了一個明顯的好處:下降錯誤處理代碼的複雜度。java

  異常,根據字面理解,有意外之意。把它置於代碼層面來理解,即阻止了當前方法或做用域繼續執行數據庫

  在Java中,異常被當作對象來處理,其基類是Throwable。數組

1.2 Java的異常體系

  Java中的全部不正常類都繼承於Throwable類。Throwable主要包括兩個大類,一個是Error類,另外一個是Exception類;網絡

  

  其中Error類中包括虛擬機錯誤和線程死鎖,一旦Error出現了,程序就完全的掛了,被稱爲程序終結者;這種問題通常都是很嚴重的,不是咱們處理的。jvm

  

  Exception類,也就是一般所說的「異常」。主要指編碼、環境、用戶操做輸入出現問題,Exception主要包括兩大類,編譯期異常和運行期異常(RuntimeException)ide

  

  RuntimeException異常主要包括如下四種異常(其實還有不少其餘異常,這裏不一一列出):空指針異常、數組下標越界異常、類型轉換異常、算術異常。RuntimeException異常會由java虛擬機自動拋出並自動捕獲(就算咱們沒寫異常捕獲語句運行時也會拋出錯誤!!),此類異常的出現絕大數狀況是代碼自己有問題應該從邏輯上去解決並改進代碼。學習

  不是RuntimeException的異常都是編譯期異常,引發該異常的緣由多種多樣,好比說文件不存在(IOException)、或者是鏈接錯誤(SQLException)等等。跟它的「兄弟」RuntimeException運行異常不一樣,該異常咱們必須手動在代碼裏添加捕獲語句來處理該異常,由於你不處理,編譯就不能經過。這也是咱們學習java異常語句中主要處理的異常對象。測試

  下面經過一個例子來理解上述異常分類:編碼

  今每天氣很好,班長出去旅遊。騎着自行車,去山裏面呼吸新鮮空氣。

  Error:走到半路上,發生山路塌陷,或者出現了泥石流,這個問題很嚴重,不是班長可以立馬解決的。嚴重的問題

  Exception:出門前,班長要看看車輪子以及車鏈子等是否還在。出發前就應該檢查的問題。

  RuntimeException:在騎車的過程當中,有好路不走,恰恰要走石子路,結果爆胎了。旅遊的過程當中出現的問題。

1.3 編譯時異常和運行時異常的區別

  Java中的異常被分爲兩大類:編譯時異常和運行時異常。全部的RuntimeException類及其子類的實例被稱爲運行時異常,其餘的異常就是編譯時異常
  編譯時異常:Java程序必須顯示處理,不然程序就會發生錯誤,沒法經過編譯。
  運行時異常:無需顯示處理,也能夠和編譯時異常同樣處理

2、Throwable中的方法

  • public String getMessage():獲取異常信息,返回字符串。
  • public String toString():獲取異常類名和異常信息,返回字符串。
    此對象的類的 name(全路徑名)
    ": "(冒號和一個空格) 
    調用此對象 getLocalizedMessage()方法的結果 (默認返回的是getMessage()的內容)
  • public String printStackTrace():獲取異常類名和異常信息,以及異常出如今程序中的位置。返回值void。
  • public String printStackTrace(PrintStream s):一般用該方法將異常內容保存在日誌文件中,以便查閱。
public class ExceptionDemo {
    public static void main(String[] args) {
        String s = "2014-11-20";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Date d = sdf.parse(s); // 建立了一個ParseException對象,而後拋出去,和catch裏面進行匹配
            System.out.println(d);
        } catch (ParseException e) { // ParseException e = new ParseException();
            // ParseException
            // e.printStackTrace();

            // getMessage()
            // System.out.println(e.getMessage());
            // Unparseable date: "2014-11-20"

            // toString()
            // System.out.println(e.toString());
            // java.text.ParseException: Unparseable date: "2014-11-20"
            
            e.printStackTrace();
            //跳轉到某個指定的頁面(index.html)
        }
        
        System.out.println("over");
    }
}

3、異常處理方案

3.1 JVM的默認處理方案

  如何程序出現了問題,咱們沒有作任何處理,最終jvm會作出默認的處理。處理方式是:

  把異常的名稱,緣由及出現的問題等信息輸出在控制檯。

  同時會結束程序

 1 public class ExceptionDemo {
 2     public static void main(String[] args) {
 3         // 第一階段
 4         int a = 10;
 5         int b = 0;
 6         System.out.println(a / b);
 7 
 8         // 第二階段
 9         System.out.println("over");
10     }
11 }

  上述程序中,咱們並無作任何處理,運行到第6行時,程序報錯,並結束程序,致使"over"並無輸出。

  

3.2 try-catch

  格式:

try {
    可能出現問題的代碼;
}catch(異常名 變量) {
    針對問題的處理;
}

  格式說明:

  try塊:負責捕獲異常,一旦try中發現異常,程序的控制權將被移交給catch塊中的異常處理程序。try語句塊不能夠獨立存在,必須與 catch 或者 finally 塊同存

  catch塊:如何處理?好比發出警告:提示、檢查配置、網絡鏈接,記錄錯誤等。執行完catch塊以後程序跳出catch塊,繼續執行後面的代碼

public class ExceptionDemo {
    public static void main(String[] args) {
        // 第一階段
        int a = 10;
        // int b = 2;
        int b = 0;

        try {
            System.out.println(a / b);
        } catch (ArithmeticException ae) {
            System.out.println("除數不能爲0");
        }

        // 第二階段
        System.out.println("over");
    }
}

  注意:

  A:try裏面的代碼越少越好。由於try裏面的代碼要走異常處理機制,jvm就會開闢新的資源來管理,代碼越多,Java就要用更多的資源來處理它。
  B:catch裏面必須有內容,哪怕是給出一個簡單的提示

3.3 try-catch-catch 

  格式:

try{
    ...
}catch(異常類名 變量名) {
    ...
}
catch(異常類名 變量名) {
    ...
}
...

  注意事項:

  1. 能明確的儘可能明確,不要用大的來處理。
  2. 平級關係的異常誰前誰後無所謂,若是出現了子父關係,父必須在後面
public class ExceptionDemo {
    public static void main(String[] args) {
        // method1();
        method2();
    }

    public static void method2() {
        int a = 10;
        int b = 0;
        int[] arr = { 1, 2, 3 };

        // 爺爺在最後
        try {
            System.out.println(a / b);
            System.out.println(arr[3]);
            System.out.println("這裏出現了一個異常,你不太清楚是誰,該怎麼辦呢?");
        } catch (ArithmeticException e) {
            System.out.println("除數不能爲0");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("你訪問了不應的訪問的索引");
        } catch (Exception e) {
            System.out.println("出問題了");
        }

        // 爺爺在前面是不能夠的
        /*try {
            System.out.println(a / b);
            System.out.println(arr[3]);
            System.out.println("這裏出現了一個異常,你不太清楚是誰,該怎麼辦呢?");
        } catch (Exception e) {
            System.out.println("出問題了");
        } catch (ArithmeticException e) {
            System.out.println("除數不能爲0");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("你訪問了不應的訪問的索引");
        }*/

    }

    public static void method1() {
        int a = 10;
        int b = 0;
        int[] arr = {1, 2, 3};

        try {
            System.out.println(a / b);
            System.out.println(arr[3]);
        } catch (ArithmeticException e) {
            System.out.println("除數不能爲0");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("你訪問了不應的訪問的索引");
        }
        System.out.println("over");
    }
}

  注意:

  一旦try裏面出了問題,就會在這裏把問題給拋出去,而後和catch裏面的問題進行匹配,一旦有匹配的,就執行catch裏面的處理,而後結束了try...catch,繼續執行後面的語句。

【JDK7新特性】

  JDK7出現了一個新的異常處理方案:

try{

}catch(異常名1 | 異常名2 | ...  變量 ) {
    ...
}

  注意:這個方法雖然簡潔,可是也不夠好。
    A:處理方式是一致的。(實際開發中,好多時候可能就是針對同類型的問題,給出同一個處理)
    B:多個異常間必須是平級關係。

public class ExceptionDemo {
    public static void main(String[] args) {
         method();
    }

    public static void method() {
        int a = 10;
        int b = 0;
        int[] arr = {1, 2, 3};

        /*try {
            System.out.println(a / b);
            System.out.println(arr[3]);
        } catch (ArithmeticException e) {
            System.out.println("除數不能爲0");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("你訪問了不應的訪問的索引");
        } catch (Exception e) {
            System.out.println("出問題了");
        }*/

        // JDK7的處理方案
        try {
            System.out.println(a / b);
            System.out.println(arr[3]);
        } catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {
            System.out.println("出問題了");
        }

        System.out.println("over");
    }
}

3.4 try-catch-finally

  格式:try...catch...finally...

  特色:被finally控制的語句體必定會執行。特殊狀況:若是在執行到finally以前jvm退出了,就不能執行了。

  做用:用於釋放資源,在IO流操做和數據庫操做中會見到

public class FinallyDemo {
    public static void main(String[] args) {
        String s = "2018--5-16";
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;
        try {
            date = format.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
            //System.exit(0);//jvm退出,finally的代碼不會執行
        }finally {
            System.out.println("這裏的代碼是能夠執行的");
        }

        System.out.println(date);
    }
}

【final,finally和finalize的區別】

  final:最終的意思,能夠修飾類,成員變量,成員方法
    修飾類,類不能被繼承
    修飾變量,變量是常量
    修飾方法,方法不能被重寫
  finally:是異常處理的一部分,用於釋放資源。
    通常來講,代碼確定會執行,特殊狀況:在執行到finally以前jvm退出了
  finalize:是Object類的一個方法,用於垃圾回收

【補充問題】

  若是catch裏面有return語句,請問finally裏面的代碼還會執行嗎?若是會,請問是在return前,仍是return後?

  答:會。在return前。(準確的說,應該是在中間)

public class FinallyDemo2 {
    public static void main(String[] args) {
        System.out.println(getInt());
    }

    public static int getInt() {
        int a = 10;
        try {
            System.out.println(a / 0);
            a = 20;
        } catch (ArithmeticException e) {
            a = 30;
            return a;
            /*
             * return a在程序執行到這一步的時候,這裏不是return a而是return 30;這個返回路徑就造成了。
             * 可是呢,它發現後面還有finally,因此繼續執行finally的內容,a=40
             * 再次回到之前的返回路徑,繼續走return 30;
             */
        } finally {
            a = 40;
            // return a;//若是這樣結果就是40了。
        }
        return a;
    }
}

3.5 throws

  有些時候,咱們是能夠對異常進行處理的,可是又有些時候,咱們根本就沒有權限去處理某個異常。或者說,我處理不了,我就不處理了。爲了解決出錯問題,Java針對這種狀況,就提供了另外一種處理方案:throws(拋出)。    

  格式:throws 異常類名

  注意:這個格式必須跟在方法的括號後面

public class ExceptionDemo {
    public static void main(String[] args) {
        System.out.println("今每天氣很好");
        try {
            method();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("可是就是不應有霧霾");

        method2();
    }

    // 運行期異常的拋出
    public static void method2() throws ArithmeticException {
        int a = 10;
        int b = 0;
        System.out.println(a / b);
    }

    // 編譯期異常的拋出
    // 在方法聲明上拋出,是爲了告訴調用者,你注意了,我有問題。
    public static void method() throws ParseException {
        String s = "2018-11-20";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = sdf.parse(s);
        System.out.println(d);
    }
}

  小結:

  編譯期異常拋出,未來調用者必須處理。
  運行期異常拋出,未來調用能夠不用處理。

3.6 throw

  在功能方法內部出現某種狀況,程序不能繼續運行,須要進行跳轉時,就用throw把異常對象拋出。

public class ExceptionDemo {
    public static void main(String[] args) {
         // method();
        try {
            method2();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 若是throw的是運行期異常,方法上就不須要throws了
     */
    public static void method() {
        int a = 10;
        int b = 0;
        if (b == 0) {
            throw new ArithmeticException();
        } else {
            System.out.println(a / b);
        }
    }

    /**
     * 若是throw的是編譯時期異常,由於你既然寫了throw,說明你不想在這裏try...catch了,因此要在方法上throws出該異常
     * @throws Exception
     */
    public static void method2() throws Exception {
        int a = 10;
        int b = 0;
        if (b == 0) {
            throw new Exception();
        } else {
            System.out.println(a / b);
        }
    }
}

【throws和throw的區別】

  throws:

    • 用在方法聲明後面,跟的是異常類名
    • 能夠跟多個異常類名,用逗號隔開
    • 表示拋出異常,由該方法的調用者來處理
    • throws表示出現異常的一種可能性,並不必定會發生這些異常

  throw:

    • 用在方法體內,跟的是異常對象名
    • 只能拋出一個異常對象名
    • 表示拋出異常,由方法體內的語句處理
    • throw則是拋出了異常,執行throw則必定拋出了某種異常

4、自定義異常

  java不可能對全部的狀況都考慮到,好比考試成績必須在0-100之間,若是成績超過100或成績小於0的話,都屬於異常,很明細Java中沒有對應的異常,這時候就須要咱們本身來作一個異常。

  而咱們本身隨意的寫一個類,是不能做爲異常類來看的,要想你的類是一個異常類,就必須繼承自Exception或者RuntimeException

public class MyException extends Exception {
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}

  定義一個類:

public class Teacher {
    public void check(int score) throws MyException {
        if (score > 100 || score < 0) {
            throw new MyException("分數必須在0-100之間");
        } else {
            System.out.println("分數沒有問題");
        }
    }
}

  自定義異常測試類

public class StudentDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("請輸入學生成績:");
        int score = sc.nextInt();

        Teacher t = new Teacher();
        try {
            t.check(score);
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}

5、異常的注意事項

  1. 子類重寫父類方法時,子類的方法必須拋出相同的異常或父類異常的子類。(父親壞了,兒子不能比父親更壞)
  2. 若是父類拋出了多個異常,子類重寫父類時,只能拋出相同的異常或者是他的子集,子類不能拋出父類沒有的異常
  3. 若是被重寫的方法沒有異常拋出,那麼子類的方法絕對不能夠拋出異常,若是子類方法內有異常發生,那麼子類只能try,不能throws
public class ExceptionDemo {

}

class Fu {
    public void show() throws Exception {
    }

    public void method() {
    }
}

class Zi extends Fu {
    @Override
    public void show() throws ArithmeticException {

    }

    @Override
    public void method() {
        // String s = "2014-11-20";
        // SimpleDateFormat sdf = new SimpleDateFormat();
        // Date d = sdf.parse(s);
        // System.out.println(d);
    }
}

 

 

 

 

參考博文:http://www.javashuo.com/article/p-neeftakf-nt.html

相關文章
相關標籤/搜索