異常處理機制

引入

說到異常先寫一個demojava

public class Introduce {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("請輸入被除數:");
        int divisor = scanner.nextInt();
        System.out.print("請輸入除數:");
        int dividend = scanner.nextInt();
        System.out.println(div(divisor, dividend));
        System.out.println("繼續執行----------------");
    }

    public static int div(int divisor, int dividend) {
        return divisor / dividend;
    }
}

正常狀況下沒問題,好比程序員

萬一手欠把除數輸成0,那麼執行結果就變成這樣數據庫


或者:數組

像上面那樣在程序運行期間出現了不正常的狀況而致使程序沒法正常運行就稱做爲異常,出現異常以後的代碼也就不能執行!jvm

那如何解決呢?第一種方案是能夠用if-else來解決socket

public class Solution {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("請輸入被除數:");
        if (scanner.hasNextInt()) {
            int divisor = scanner.nextInt();
            System.out.print("請輸入除數:");
            if (scanner.hasNextInt()) {
                int dividend = scanner.nextInt();
                if (dividend != 0) {
                    System.out.println(div(divisor, dividend));
                } else {
                    System.out.println("除數不能爲0");
                }
            } else {
                System.out.println("錄入的不是int數據!");
            }
        } else {
            System.out.println("錄入的不是int數據!");
        }
        System.out.println("繼續執行----------------");
    }

    public static int div(int divisor, int dividend) {
        return divisor / dividend;
    }
}

try-catch語句

看完以後是否是感受這個if嵌套代碼有些臃腫,業務代碼也與處理異常的代碼混在一塊兒;可讀性還不好;最重要的是程序員沒法想到全部可能發生異常狀況。基於if-else處理機制的缺點,java提供了一套處理異常機制ide

public class Solution2 {
    public static void main(String[] args) {
        try {
            Scanner scanner = new Scanner(System.in);
            System.out.print("請輸入被除數:");
            int divisor = scanner.nextInt();
            System.out.print("請輸入除數:");
            int dividend = scanner.nextInt();
            System.out.println(div(divisor, dividend));
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("繼續執行----------------");
    }

    public static int div(int divisor, int dividend) {
        return divisor / dividend;
    }
}

這段代碼的try-catch塊包圍的代碼就是用來處理異常的也叫作捕獲異常,try塊用來處理可能出現異常的代碼,catch塊用來處理try塊中出現的異常3d

語法:try{
		可疑代碼,將異常生成對應的異常對象,傳遞給catch塊   
	 }catch(異常){
    	異常處理
	 }

發生狀況:try塊中出現異常,則異常以後的代碼不會執行,直接運行catch塊中的語句code

​ try塊中沒出現異常,catch塊中的語句不會執行對象

​ catch中的異常類型和你給出的異常類型匹配的話,會執行catch塊中的語句

​ catch中的異常類型和你給出的異常類型不匹配的話,則至關於沒進行異常處理,catch塊中和以後的代碼不會執行

//catch中處理異常
public class Catch {
    public static void main(String[] args) {
        try {
            int[] num = {42, 25, 32, 20, 14, 18};
            System.out.println(num[num.length]);
        } catch (Exception e) {
            //第一種:空處理,什麼都不寫

            //第二種:輸出自定義異常信息
            System.out.println("代碼出現異常!");

            //第三種:打印異常信息
            System.out.println(e.toString());//打印異常的全限定名(包名+類名)
            System.out.println(e.getMessage());// 顯示異常描述信息,這段代碼提示你數組長度是6
            e.printStackTrace();//顯示異常的詳細信息
            
            //第四種:拋出異常
            throw e;
        }
    }
}

另外try-catch語句還能夠進行嵌套,catch語句也能夠有多個造成多層catch

public class MultipleCatch {
    public static void main(String[] args) {
        try {
            Scanner scanner = new Scanner(System.in);
            System.out.print("請輸入被除數:");
            int divisor = scanner.nextInt();
            System.out.print("請輸入除數:");
            int dividend = scanner.nextInt();
            new MultipleCatch().div(divisor, dividend);
            //多層catch分別匹配InputMismatchException和ArithmeticException異常,匹配到哪個就執行哪個,若是沒匹配到至關於沒處理異常
        } catch (InputMismatchException e) {
            e.printStackTrace();
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("繼續執行----------------");
        }
    }

    public void div(int num1, int num2) {
        System.out.println(num1 / num2);
    }
}

下面這個InputStream和OutputStream不知道是什麼,這不要緊,重點是演示try-catch語句

public class NestedTryCatch {
    public static void main(String[] args) {
        //InputStream和OutputStream都是一個流
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(new File("F:\\study"));
            outputStream = new FileOutputStream(new File("F:\\study"));
            //多層catch,子類異常必須寫在父類異常上面
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            try {
                //流使用完畢須要關閉,這裏就是一個嵌套try-catch語句
                inputStream.close();
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

上面的多層匹配分別匹配FileNotFoundException、IOException和Exception異常,匹配到哪個就執行哪個

值得注意的是這幾個異常有繼承關係的,FileNotFoundException繼承自IOException,它也是接着繼承Exception的

異常體系

再看一下繼承體系

編譯時異常:就指程序編譯時產生的異常,必須處理,不然代碼不能經過運行

運行時異常:運行以後纔可能出現的異常,能夠不作處理,通常是程序的邏輯錯誤,儘可能避免!

越往上就是父類能處理的異常範圍越大,因此就能夠想象出爲何子類異常必須寫在父類,若是同級別的異常就不用關心順序

異常代碼以後可能還有代碼語句,但try-catch語句塊可能運行完以後後續代碼不會執行

  1. throw拋出異常

  2. catch語句塊沒有捕獲成功

  3. try語句塊中有return語句

public class Try {
    public static void main(String[] args) {
        try {
            String[] str = {"a", "b", "c", "d"};
            //System.out.println(str[str.length]);
            System.out.println(str.length);
            return;
        } catch (Exception e) {
            //throw e;
        }
        System.out.println("後續代碼-------------");
    }
}

finally語句

但就想異常代碼處理以後,不管異常信息是否捕獲成功,後續的代碼都會運行,能夠加finally語句

public class Finally {
    public static void main(String[] args) {
        int num = 10;
        try {
            System.out.println(num); //執行結果:10
            return;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("後續代碼-------------");
            num += 100;
            System.out.println(num); //執行結果:110,由於先執行finally再執行try中的return
        }
    }
}
//finally塊通常操做關閉數據庫資源,關閉IO流資源,關閉socket資源。

在進行異常捕獲的時候,return語句位置的不一樣其實可能會形成結果不一樣

//一、try中有return,finally中沒有return
public class Test01 {
    public static void main(String[] args){
        System.out.println(test());
    }

    private static int test(){
        int num = 10;
        try{
            System.out.println("try");
            return num += 80;
        }catch(Exception e){
            System.out.println("error");
        }finally{
            if (num > 20){
                System.out.println("num>20 : " + num);
            }
            System.out.println("finally");
        }
        return num;
    }
}

/*
執行結果:
	try
	num>20 : 90
	finally
	90
	
	這裏把return num += 80拆分紅兩個語句了,num+=80和return,看一下class反編譯的代碼
*/
public class Test1 {
    public TryTest() {
    }

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

    private static int test() {
        int num = 10;

        try {
            System.out.println("try");
            num += 80;
            int var1 = num;
            return var1;
        } catch (Exception var5) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20 : " + num);
            }

            System.out.println("finally");
        }
        return num;
    }
}
//二、try和finally中均有return
public class Test2 {
    public static void main(String[] args) {
        System.out.println(test());
    }

    private static int test() {
        int num = 10;
        try {
            System.out.println("try");
            return num += 80;
        } catch (Exception e) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20 : " + num);
            }
            System.out.println("finally");
            num = 100;
            return num;
        }
    }
}

/*
執行結果:
	try
	num>20 : 90
	finally
	100
	
	這裏一樣把return num += 80拆分紅兩個語句了,num+=80和return,並且try中的return被省略了,直接執行finally中的return語句,獲得返回值。看一下class反編譯的代碼
*/
public class Test2 {
    public Test2() {
    }

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

    private static int test() {
        int num = 10;

        try {
            System.out.println("try");
            num += 80;
        } catch (Exception var5) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20 : " + num);
            }

            System.out.println("finally");
            int num = 100;
            return num;
        }
    }
}
//三、finally中改變返回值num,可是不返回
public class Test3 {
    public static void main(String[] args){
        System.out.println(test());
    }

    private static int test(){
        int num = 10;
        try{
            System.out.println("try");
            return num;
        }catch(Exception e){
            System.out.println("error");
        }finally{
            if (num > 20){
                System.out.println("num>20 : " + num);
            }
            System.out.println("finally");
            num = 100;
        }
        return num;
    }
}

/*
執行結果:
	try
	finally
	10
	
	finally沒有return時,這裏把num的值用第三方變量存儲起來了,finally執行結束return的是那個第三方變量,仍是看一下class反編譯的代碼
*/
public class Test3 {
    public Test3() {
    }

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

    private static int test() {
        byte num = 10;

        try {
            System.out.println("try");
            byte var1 = num;
            return var1;
        } catch (Exception var5) {
            System.out.println("error");
        } finally {
            if (num > 20) {
                System.out.println("num>20 : " + num);
            }

            System.out.println("finally");
            num = 100;
        }

        return num;
    }
}
//將num的值包裝在Num類中
public class Test4 {
    public static void main(String[] args){
        System.out.println(test().num);
    }

    private static Num test(){
        Num number = new Num();
        try{
            System.out.println("try");
            return number;
        }catch(Exception e){
            System.out.println("error");
        }finally{
            if (number.num > 20){
                System.out.println("number.num>20 : " + number.num);
            }
            System.out.println("finally");
            number.num = 100;
        }
        return number;
    }
}

class Num{
    public int num = 10;
}

/*
執行結果:
	try
	finally
	100

	這裏finally也沒有return,這裏第三方變量存儲的是建立對象的那個地址值,finally執行結束建立的對象改變值,仍是看一下class反編譯的代碼
*/
public class Test4 {
    public Test4() {
    }

    public static void main(String[] args) {
        System.out.println(test().num);
    }

    private static Num test() {
        Num number = new Num();

        try {
            System.out.println("try");
            Num var1 = number;
            return var1;
        } catch (Exception var5) {
            System.out.println("error");
        } finally {
            if (number.num > 20) {
                System.out.println("number.num>20 : " + number.num);
            }

            System.out.println("finally");
            number.num = 100;
        }

        return number;
    }
}

class Num {
    public int num = 10;

    Num() {
    }
}

throws

java除了提供了try-catch塊這種捕獲異常的解決方案,還提供了一種聲明拋出異常的解決方案throws,即本身不處理這些異常,而是丟給調用方處理,若是整個程序的運行過程當中都沒有異常的處理的話,最終異常會拋給jvm,不太友好,通常都要對異常進行處理

public class ThrowsDemo {
    public static void main(String[] args) throws Exception {
        new ThrowsDemo().test();
    }
    public void test() throws FileNotFoundException {
        FileInputStream inputStream=new FileInputStream("F:\\study\\test.txt");
    }
}

⾃定義異常

開發人員還能夠⾃定義異常,⼀般經過繼承Exception的⼦類的⽅式實現,本質上是覆蓋原有異常API的信息

public class CustomException extends Exception{

    static final long serialVersionUID = -70348971907L;

    public CustomException(String message) {
        super(message);
    }
}
//模擬一下餘額不足的狀況
public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int money = scanner.nextInt();
        new Test().getMoney(money, 2000);
    }

    public int getMoney(int money, int price) {
        if (money < price) {
            try {
                throw new CustomException("餘額不⾜!");
            } catch (CustomException e) {
                e.printStackTrace();
            }
        }
        System.out.println("繼續執行程序~~~");
        return money;
    }
}

自定義異常的步驟:

  1. 繼承於現的異常結構:RuntimeException 、Exception

  2. 提供全局常量:serialVersionUID

  3. 編寫構造方法,能夠傳入本身想打印的異常信息

  4. 調用的時候經過throw向外拋出異常

若是繼承的是運行時異常,那麼在使用的時候無需額外處理;若是繼承的是檢查異常,那麼使用的時候須要try-catch捕獲或者throws向上拋

throw和throws的區別:

  1. 位置不一樣:throw:方法內部,throws: 方法的聲明處
  2. 內容不一樣:throw+異常對象(檢查異常,運行時異常);throws+異常的類型(能夠多個類型,用,拼接)
  3. 做用不一樣:throw:異常出現的源頭,製造異常。throws:在方法的聲明處,告訴方法的調用者,這個方法中可能會出現我聲明的這些異常。而後調用者對這個異常進行處理,要麼本身處理要麼再繼續向外拋出異常

jdk7新寫法

  1. 在JDK1.7之後,異常新處理方式:能夠並列用|符號鏈接

  1. try-with-resources語句

Java裏,對於文件操做IO流、數據庫鏈接等開銷很是昂貴的資源,用完以後必須及時經過close方法將其關閉,不然資源會一直處於打開狀態,可能會致使內存泄露等問題。

關閉資源的經常使用方式就是在finally塊裏是釋放,即調用close方法。好比,咱們常常會寫這樣的代碼:

public static void test() {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        File f = new File("F:\\demo.txt");
        FileWriter fw = null;
        BufferedWriter bw = null;
        try {
            fw=new FileWriter(f);
            bw = new BufferedWriter(fw);
            String s = br.readLine();
            while (!s.equals("exit")) {
                bw.write(s);
                bw.newLine();//文件中換行
                s = br.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //4.關閉流:
        try {
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

從Java 7開始,jdk提供了一種更好的方式關閉資源,使用try-with-resources語句,改寫一下上面的代碼,效果以下:

public static void test2(){
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try(FileWriter fw=new FileWriter(new File("F:\\demo.txt"));
            BufferedWriter bw=new BufferedWriter(fw)){
            String s = br.readLine();
            while (!s.equals("exit")) {
                bw.write(s);
                bw.newLine();
                s = br.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

是否是感受簡潔了好多,其實這就是一個語法糖,它將在編譯時編譯成關閉資源的代碼。咱們將上述例子中的代碼編譯成class文件,再反編譯回java文件,就能看到以下代碼:

public static void test2() {
        InputStreamReader isr = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(isr);
        File f = new File("F:\\demo.txt");

        try {
            FileWriter fw = new FileWriter(f);
            Throwable var4 = null;

            try {
                BufferedWriter bw = new BufferedWriter(fw);
                Throwable var6 = null;

                try {
                    for(String s = br.readLine(); !s.equals("exit"); s = br.readLine()) {
                        bw.write(s);
                        bw.newLine();
                    }
                } catch (Throwable var31) {
                    var6 = var31;
                    throw var31;
                } finally {
                    if (bw != null) {
                        if (var6 != null) {
                            try {
                                bw.close();
                            } catch (Throwable var30) {
                                var6.addSuppressed(var30);
                            }
                        } else {
                            bw.close();
                        }
                    }

                }
            } catch (Throwable var33) {
                var4 = var33;
                throw var33;
            } finally {
                if (fw != null) {
                    if (var4 != null) {
                        try {
                            fw.close();
                        } catch (Throwable var29) {
                            var4.addSuppressed(var29);
                        }
                    } else {
                        fw.close();
                    }
                }
            }
        } catch (IOException var35) {
            var35.printStackTrace();
        }
    }

除了異常代碼,咱們看到其實關閉流的處理是底層幫咱們處理的

相關文章
相關標籤/搜索