1. 程式人生 > 實用技巧 >實驗室第一次排位賽

實驗室第一次排位賽

0x01.異常

異常概念,體系,分類,異常產生過程解析

1、異常概念

異常,就是不正常的意思。在生活中:醫生說,你的身體某個部位有異常,該部位和正常相比有點不同,該部位的功能將受影響.在程式中的意思就是:

  • 異常 :指的是程式在執行過程中,出現的非正常的情況,最終會導致JVM的非正常停止。

注意: 在Java等面向物件的程式語言中,異常本身是一個類,產生異常就是建立異常物件並丟擲了一個異常物件。Java處理異常的方式是中斷處理。

異常指的並不是語法錯誤,語法錯了,編譯不通過,不會產生位元組碼檔案,根本不能執行.

2、 異常體系

異常機制其實是幫助我們找到程式中的問題,異常的根類是java.lang.Throwable

,其下有兩個子類:java.lang.Errorjava.lang.Exception,平常所說的異常指java.lang.Exception

Throwable體系:

  • Error:嚴重錯誤Error,無法通過處理的錯誤,只能事先避免,好比絕症。
  • Exception:表示異常,異常產生後程序員可以通過程式碼的方式糾正,使程式繼續執行,是必須要處理的。好比感冒、闌尾炎。

3、異常分類

我們平常說的異常就是指Exception,因為這類異常一旦出現,我們就要對程式碼進行更正,修復程式。

異常(Exception)的分類:根據在編譯時期還是執行時期去檢查異常?

  • 編譯時期異常:checked異常。在編譯時期,就會檢查,如果沒有處理異常,則編譯失敗。(如日期格式化異常)
  • 執行時期異常:runtime異常。在執行時期,檢查異常.在編譯時期,執行異常不會編譯器檢測(不報錯)。(如數學異常)

public class Test {
    public static void main(String[] args) {
        /*
            異常的概述:
                - 異常概念: 程式執行期間,出現的不正常情況,導致jvm終止程式執行
                  注意:
                    java是面嚮物件語言,異常本身也是一個類,當出現異常的時候,就會建立該異常類的物件並丟擲該異常物件
                    建立異常物件,該物件就會包裝異常的型別,異常的資訊,異常的位置等資訊

                - 異常體系
                    Throwable類:是 Java 語言中所有錯誤或異常的超類\父類
                        Error類: 表示錯誤,無法通過程式碼進行糾正,只能事先避免,相當於:絕症
                                 例如: 棧記憶體溢位錯誤,伺服器宕機,資料庫奔潰...
                        Exception類:表示異常,可以通過程式碼進行糾正,相當於: 感冒...

                - 異常分類
                    編譯異常:在編譯期間,出現的異常,導致程式無法通過編譯,這就是編譯異常
                            除了RuntimeException及其子類都是編譯異常
                    執行異常:在執行期間,才出現的異常,編譯期間不處理,編譯可以通過,這就是執行異常
                            RuntimeException及其子類都是執行異常
         */
        // 異常和錯誤
        System.out.println("開始");
        //System.out.println(1/0);// 異常
        //method();// StackOverflowError 錯誤
        System.out.println("結束");

        // 例如: 編譯異常
        //SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        //Date date = sdf.parse("1999-10-10");

        // 例如: 執行異常
        //System.out.println(1/0);// 異常

    }

    public static void method(){
        System.out.println("1");
        method();
    }
}

4、異常的產生過程解析

先執行下面的程式,程式會產生一個數組索引越界異常ArrayIndexOfBoundsException。我們通過圖解來解析下異常產生的過程。

測試類

public class Test {

    public static void main(String[] args) {
        int[] arr = { 34, 12, 67 };
        int num = getElement(arr, 4);
        System.out.println("num=" + num);
        System.out.println("over");
    }

    // 對給定的陣列通過給定的角標獲取元素。
    public static int getElement(int[] arr, int index) {
        int element = arr[index];
        return element;
    }
}

上述程式執行過程圖解:

0x02.異常的產生和處理

1、異常的產生

** 目標**

  • 能夠理解使用throw關鍵字產生異常

** 路徑**

  • throw關鍵字的作用
  • throw關鍵字的使用格式
  • 案例演示

講解:

throw關鍵字的作用

在java中,提供了一個throw關鍵字,它用來丟擲一個指定的異常物件。throw用在方法內,用來丟擲一個異常物件,將這個異常物件傳遞到呼叫者處,並結束當前方法的執行。

throw關鍵字的使用格式

throw new 異常類名(引數);

例如:

throw new NullPointerException("要訪問的arr陣列不存在");

throw new ArrayIndexOutOfBoundsException("該索引在陣列中不存在,已超出範圍");

案例演示

public class Student {

    //姓名
    private String name;
    //年齡
    private int age;

    //構造方法

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        //對年齡的判斷
        if(age >= 0) {
            this.age = age;
        }else{
            //如果年齡是負數,就讓他報錯
            throw new RuntimeException("年齡不能是" + age);
        }
    }
    //set get

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        //對年齡的判斷
        if(age >= 0) {
            this.age = age;
        }else{
            //如果年齡是負數,就讓他報錯
            throw new RuntimeException("年齡不能是" + age);
        }
    }

    //toString

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class Demo02_異常的產生 {
    public static void main(String[] args) {
        //正常情況下,年齡不可能是一個負數
        //要求:賦值正數正常賦值,賦值負數就讓程式碼報錯!

        //建立物件
        Student s = new Student("柳巖",36);
        System.out.println(s);


        //建立物件
        Student s2 = new Student();
        s2.setName("美美");
        s2.setAge(-20);
        System.out.println(s2);
    }
}

2、宣告處理異常

目標

  • 掌握宣告處理異常

路徑

  • 宣告處理異常的概述
  • 宣告處理異常格式

講解

宣告處理異常的概述

宣告處理異常:使用throws關鍵字將異常標識出來, 表示當前方法不處理異常,而是提醒給呼叫者, 讓呼叫者來處理....最終會到虛擬機器,虛擬機器直接結束程式,列印異常資訊。

宣告處理異常格式

修飾符 返回值型別 方法名(引數) throws 異常類名1,異常類名2…{  // 可以丟擲一個,也可以多個
}	

案例演示

public class Demo {
    public static void main(String[] args) throws IOException{
        method1();
    }

    public static void method3(int num) throws Exception {
        if(num == 1) {
            throw new IOException("IO異常");// 建立了一個編譯異常,並通過throw丟擲這個編譯異常
        }else{
            throw new ParseException("解析異常",1);
        }
    }

    public static void method2(int num) throws IOException,ParseException{
        if(num == 1) {
            throw new IOException("IO異常");// 建立了一個編譯異常,並通過throw丟擲這個編譯異常
        }else{
            throw new ParseException("解析異常",1);
        }
    }


    public static void method1() throws IOException{
        throw new IOException("IO異常");// 建立了一個編譯異常,並通過throw丟擲這個編譯異常
    }
}

0x03.捕獲處理異常try…catch

目標

  • 掌握捕獲處理異常

路徑

  • 捕獲處理異常的概述
  • 捕獲處理異常格式
  • 獲取異常資訊

講解

捕獲處理異常的概述

  • 捕獲處理異常:對異常進行捕獲處理 , 處理完後程序可以正常向下執行。

捕獲處理異常格式

try{
     編寫可能會出現異常的程式碼
}catch(異常型別  e){
     處理異常的程式碼
     //記錄日誌/列印異常資訊/繼續丟擲異常
}
執行步驟:
    1.首先執行try中的程式碼,如果try中的程式碼出現了異常,那麼就直接執行catch()裡面的程式碼,執行完後,程式繼續往下執行
    2.如果try中的程式碼沒有出現異常,那麼就不會執行catch()裡面的程式碼,而是繼續往下執行

注意:

  1. try和catch都不能單獨使用,必須連用。
  2. try中的程式碼出現了異常,那麼出現異常位置後面的程式碼就不會再執行了
  3. 捕獲處理異常,如果程式出現了異常,程式會繼續往下執行

​ 宣告處理異常,如果程式出現了異常,程式就不會繼續往下執行

演示如下:

public class Demo {
    public static void main(String[] args) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date date = sdf.parse("1999年10月12日");
            System.out.println(date);
        }catch (Exception e){
            System.out.println("出現了異常....");
        }
        System.out.println("結束");
    }
}

獲取異常資訊

Throwable類中定義了一些檢視方法:

  • public String getMessage():獲取異常的描述資訊,原因(提示給使用者的時候,就提示錯誤原因。
  • public String toString():獲取異常的型別和異常描述資訊(不用)。
  • public void printStackTrace():列印異常的跟蹤棧資訊並輸出到控制檯。

​ ** 包含了異常的型別,異常的原因,還包括異常出現的位置,在開發和除錯階段,都得使用printStackTrace。**
在開發中呢也可以在catch將編譯期異常轉換成執行期異常處理。

0x04.finally 程式碼塊

finally程式碼塊的概述

finally:有一些特定的程式碼無論異常是否發生,都需要執行。另外,因為異常會引發程式跳轉,導致有些語句執行不到。而finally就是解決這個問題的,在finally程式碼塊中存放的程式碼都是一定會被執行的。

** finally程式碼塊的語法格式**

try{
    可能會出現異常的程式碼
}catch(異常的型別 變數名){
    處理異常的程式碼或者列印異常的資訊
}finally{
    無論異常是否發生,都會執行這裡的程式碼(正常情況,都會執行finally中的程式碼,一般用來釋放資源)
}

執行步驟:
 1.首先執行try中的程式碼,如果try中的程式碼出現了異常,那麼就直接執行catch()裡面的程式碼,執行完後會執行finally中的程式碼,然後程式繼續往下執行
 2.如果try中的程式碼沒有出現異常,那麼就不會執行catch()裡面的程式碼,但是還是會執行finally中的程式碼,然後程式繼續往下執行

注意:finally不能單獨使用。

案例演示

public class Test {
    public static void main(String[] args) {
        /*
              注意:
                即使catch中有return,finally中的程式碼還是會執行
         */
       Scanner sc = null;
        try{
            sc = new Scanner(System.in);
            String s = sc.next();
            System.out.println(1/0);// 出現異常,throw new ArithmeticException("");

        }catch (Exception e){
            System.out.println("出現了異常");
            //return;// 結束方法 finally會執行
            // System.exit(0);// 系統退出 finally不會執行
        }finally{
            sc.close();// 不會執行
            System.out.println("關閉...");
        }

        System.out.println("======================");
       /* Scanner sc2 = null;
        try{
            sc2 = new Scanner(System.in);
            String s = sc2.next();
            System.out.println(1/1);// 1 沒有異常

        }catch (Exception e){
            System.out.println("出現了異常");
        }finally{
            sc2.close();// 不會執行
            System.out.println("關閉...");
        }*/

        System.out.println("結束");
    }
}


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

0x05.異常處理事項

  • 執行時異常被丟擲可以不處理。即不捕獲也不宣告丟擲。

  • 如果父類的方法丟擲了多個異常,子類覆蓋(重寫)父類方法時,只能丟擲相同的異常或者是他的子集。

  • 父類方法沒有丟擲異常,子類覆蓋父類該方法時也不可丟擲異常。此時子類產生該異常,只能捕獲處理,不能宣告丟擲

  • 當多異常分別處理時,捕獲處理,前邊的類不能是後邊類的父類

  • 在try/catch後可以追加finally程式碼塊,其中的程式碼一定會被執行,通常用於資源回收。

  • 多個異常使用捕獲又該如何處理呢?

    1. 多個異常分別處理。
    2. 多個異常一次捕獲,多次處理。
    3. 多個異常一次捕獲一次處理。

    一般我們是使用一次捕獲多次處理方式,格式如下:

    try{
         編寫可能會出現異常的程式碼
    }catch(異常型別A  e){  當try中出現A型別異常,就用該catch來捕獲.
         處理異常的程式碼
         //記錄日誌/列印異常資訊/繼續丟擲異常
    }catch(異常型別B  e){  當try中出現B型別異常,就用該catch來捕獲.
         處理異常的程式碼
         //記錄日誌/列印異常資訊/繼續丟擲異常
    }
    

    注意:這種異常處理方式,要求多個catch中的異常不能相同,並且若catch中的多個異常之間有子父類異常的關係,那麼子類異常要求在上面的catch處理,父類異常在下面的catch處理。

程式碼如下:

public class Demo {
    public static void main(String[] args) {
		System.out.println(1/0);
    }

    /**
     * 多個異常一次捕獲一次處理
     * @param num
     */
    public static void method3(int num) {
        try {
            if(num == 1) {
                throw new IOException("IO異常");
            }else{
                throw new ParseException("解析異常",1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 多個異常一次捕獲,多次處理。
     * @param num
     */
    public static void method2(int num) {
        try {
            if(num == 1) {
                throw new IOException("IO異常");
            }else{
                throw new ParseException("解析異常",1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    /**
     * 多個異常分別處理。
     * @param num
     */
    public static void method1(int num) {
        if(num == 1) {
            try {
                throw new IOException("IO異常");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            try {
                throw new ParseException("解析異常",1);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }
}

0x06.自定義異常

自定義異常概述

為什麼需要自定義異常類:

我們說了Java中不同的異常類,分別表示著某一種具體的異常情況,那麼在開發中總是有些異常情況是SUN沒有定義好的,例如年齡負數問題,考試成績負數問題.這些異常在JDK中沒有定義過,此時我們根據自己業務的異常情況來定義異常類。

什麼是自定義異常類:

在開發中根據自己業務的異常情況來定義異常類.

自定義一個業務邏輯異常: RegisterException。一個註冊異常類。

異常類如何定義:

  1. 自定義一個編譯期異常: 自定義類 並繼承於java.lang.Exception
  2. 自定義一個執行時期的異常類:自定義類 並繼承於java.lang.RuntimeException

自定義異常的練習

要求:我們模擬註冊操作,如果使用者名稱已存在,則丟擲異常並提示:親,該使用者名稱已經被註冊。

首先定義一個註冊異常類RegisterException:

// 業務邏輯異常
public class RegisterException extends Exception {
    /**
     * 空參構造
     */
    public RegisterException() {
    }

    /**
     *
     * @param message 表示異常提示
     */
    public RegisterException(String message) {
        super(message);
    }
}

模擬登陸操作,使用陣列模擬資料庫中儲存的資料,並提供當前註冊賬號是否存在方法用於判斷。

public class Demo {
    // 模擬資料庫中已存在賬號
    private static String[] names = {"bill","hill","jill"};
   
    public static void main(String[] args) {     
        //呼叫方法
        try{
              // 可能出現異常的程式碼
            checkUsername("nill");
            System.out.println("註冊成功");//如果沒有異常就是註冊成功
        }catch(LoginException e){
            //處理異常
            e.printStackTrace();
        }
    }

    //判斷當前註冊賬號是否存在
    //因為是編譯期異常,又想呼叫者去處理 所以宣告該異常
    public static boolean checkUsername(String uname) throws LoginException{
        for (String name : names) {
            if(name.equals(uname)){//如果名字在這裡面 就丟擲登陸異常
                throw new LoginException("親"+name+"已經被註冊了!");
            }
        }
        return true;
    }
}