java基礎(三):談談java異常的處理

1.知識點總結

1.1.異常分類

  異常就是java中出現的不正常的現象(錯誤與異常),按照繼承的體系結構,能夠分類以下html

  Throwable: 它是全部錯誤與異常的超類(祖宗類)java

    |- Error 錯誤,修改java源代碼數據庫

    |- Exception 編譯期異常, javac.exe進行編譯的時候報錯編程

      |- RuntimeException 運行期異常, java出現運行過程當中出現的問題api

1.2.異常處理方式

  1.出現問題,本身解決數組

    try{bash

      可能出現異常的代碼網絡

    } catch(異常類名 對象名){jvm

      異常處理代碼socket

    } finally {

      異常操做中必定要執行的代碼

    }

   2.出現問題,別人解決

    格式:

    修飾符 返回值類型 方法名(參數) throws 異常類名1,異常類名2,...{}

    public void method() throws ATMException{}

2.異常和錯誤的區別

異常:程序在運行期間發生了異常,一般能夠有針對性的處理方式的。

錯誤:程序在運行期間發生了錯誤,一般不會有針對性的處理方式。錯誤的的發生每每都是系統級別的問題,都是jvm所在系統發生的並反饋給jvm的。沒法針對處理,只能修正代碼。

異常代碼體現:

class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = new int[3];
        System.out.println(arr[0]);
        System.out.println(arr[3]);// 該句運行時發生了ArrayIndexOutOfBoundsException,致使程序沒法繼續執行。程序結束。
        System.out.println("over"); // 因爲上面代碼發生了異常,此句代碼不會執行
    }
}
複製代碼

錯誤的體現:

int[] arr = new int[1024*1024*100];//OutOfMemoryError 開闢了過大的數組空間,致使JVM在分配數組空間時超出了JVM內存空間,直接發生錯誤。
複製代碼

3.異常的發生過程

class ArrayTools{
    /*
    對給定的數組經過給定的角標獲取元素。
    */
    int getElement(int[] arr,int index)    {
        /*
        沒有找到4個角標。運行時發生了問題,這個問題JVM認識。這個問題java自己有
        描述:描述內容有:問題的名稱,問題的內容,問題的發生位置。既然有這麼多的信
        息。java就將這些信息直接封裝到對象中。ArrayIndexOutOfBoundsException
        */
        int element = arr[index];//throw new ArrayIndexOutOfBoundsException(index);
        return element;
    }
}
複製代碼
class ExceptionDemo2 {
    public static void main(String[] args)     {
        ArrayToolsd = new ArrayTools();
        int[] arr = {34,12,67};
        int num = d.getElement(arr,4);//收到 new ArrayIndexOutOfBoundsException(index);拋出jvm。jvm進行最終處理。將問題的名稱,信息,位置都顯示屏幕上。
        System.out.println("num="+num);
        System.out.println("over");
    }
}
複製代碼

4.異常的應用

在編寫程序時,必需要考慮程序的問題狀況。好比在寫功能時,須要接受參數,在使用功能中使用接受到的參數時,首先須要先對參數數據進行合法的判斷,數據若不合法,應該告訴調用者,傳遞合法的數據進來。這時須要使用異常這種方法來告訴調用者。因此定義程序須要考慮程序的健壯性

class ArrayTools{
    /*
    對給定的數組經過給定的角標獲取元素。
    */
    int getElement(int[] arr,int index)    {
        /*
        jvm出了問題,本身打包對象並拋出。可是它所提供的信息不夠給力。想要更清晰,
        須要本身寫。它的拋出不知足咱們的要求。準備本身拋。
        */
        if(arr==null)    {
            throw new NullPointerException("arr指向的數組不存在");
        }
        if(index<0 || index>=arr.length){
            //該條件若是知足,功能已經沒法繼續運算。這時就必須結束功能,並將問題告知給調用者。這時就須要經過異常來解決。
            //怎麼使用異常呢?
            //1,建立一個異常對象。封裝一些提示信息(自定義)。
            //2,須要將這個對象告知給調用者。怎麼告知呢?怎麼將這個對象傳遞到調用者處呢?經過關鍵字throw就能夠完成。 throw 異常對象;
            //3,throw用在函數內,拋出異常對象,並能夠結束函數。
            throw new ArrayIndexOutOfBoundsException("錯誤的角標,"+index+"索引在數組中不存在");
        }
        int element = arr[index];
        return element;
    }
}
複製代碼
class ExceptionDemo3 {
    public static void main(String[] args) {
        Demo d = new Demo();
        int[] arr = {34,12,67};
        int num = d.getElement(null,2);
        System.out.println("num="+num);
        System.out.println("over");
    }
}
複製代碼

5.異常的分類

在查閱API時,發現異常有分類,而且有些編輯報錯,有些編譯不報錯,那究竟是爲何呢?

在上述自定義異常中繼承Exception和繼承RuntimeExcpetion爲何差距這麼大?

  一、Exception異常(非RuntimeException)

  在函數內拋出Exception,編譯失敗,由於編譯器在檢查語法時發生了錯誤。該程序已經出現問題,Java認爲這個程序自己存在隱患,須要捕獲或者聲明出來(你要麼把問題處理,要麼把問題標識出來讓調用知道)。

   二、RuntimeException異常

  爲何拋出RuntimeException,不須要捕獲,不要聲明呢?不是功能自己發生的異常,而是由於好比調用者傳遞參數錯誤而致使功能運行失敗。這也是問題,須要經過異常來體現,可是這個異常不要聲明出來的。聲明的目的是爲了讓調用者進行處理。不聲明的目的是不讓調用者進行處理,就是爲了讓程序中止,讓調用者看到現象,並進行代碼的修正。

  三、異常分兩種:

  a、編譯時異常:編譯器會檢測的異常。

  b、運行時異常:編譯器不會檢測的異常。不須要聲明。聲明也能夠,若是聲明瞭,無外 乎就是讓調用者給出處理方式。

6.聲明和捕獲

在研究異常分類時,發現有關於異常的聲明和捕獲,到底啥是異常聲明,啥是異常捕獲呢?

  聲明:將問題標識出來,報告給調用者。若是方法內經過throw拋出了編譯時異常,而沒有捕獲,那麼必須經過throws進行聲明,讓調用者去處理。

  捕獲:Java中對異常有針對性的語句進行捕獲。

捕獲格式:

try
{
    //須要被檢測的語句。
}
catch(異常類 變量)//參數。
{
    //異常的處理語句。
}
finally
{
    //必定會被執行的語句。
}
複製代碼
class Demo{
    /*
    若是定義功能時有問題發生須要報告給調用者。能夠經過在函數上使用throws關鍵字進行聲明。
    */
    void show(int x)throws Exception    {
        if(x>0){
            throw new Exception();
        } else {
            System.out.println("show run");
         }
    }
}
複製代碼
class ExceptionDemo{
    public static void main(String[] args) {//throws Exception//在調用者上繼續聲明。 
        Demo d = new Demo();
        try    {
            d.show(1);//當調用了聲明異常的方法時,必須有處理方式。要麼捕獲,要麼聲明。
        }
        catch (Exception ex) { //括號中須要定義什麼呢?對方拋出的是什麼問題,在括號中就定義什麼問題的引用。
            System.out.println("異常發生了");
        }
        System.out.println("Hello World!");
    }
}
複製代碼

聲明和捕獲的應用

class NoAgeException extends RuntimeException{
    NoAgeException() {
        super();
    }

    NoAgeException(String message)    {
        super(message);
    }
}
複製代碼
class Person{
    private String name;
    private int age;
    Person(String name,int age)//throws NoAgeException    {
        //加入邏輯判斷。
        if(age<0 || age>200)        {
            throw new NoAgeException(age+",年齡數值非法");
        }
        this.name = name;
        this.age = age;
    }
    //定義Person對象對應的字符串表現形式。覆蓋Object中的toString方法。
    public String toString()    {
        return "Person[name="+name+",age="+age+"]";
    }
}
複製代碼
class ExceptionDemo{
    public static void main(String[] args) {
        try {
            Person p = new Person("xiaoming",20);
            System.out.println(p);
        }
        catch (NoAgeException ex){
            System.out.println("異常啦");
        }
        System.out.println("over");
    }
}
複製代碼

構造函數到底拋出這個NoAgeException是繼承Exception呢?仍是繼承RuntimeException呢?

繼承Exception,必需要throws聲明,一聲明就告知調用者進行捕獲,一旦問題處理了調用者的程序會繼續執行。

繼承RuntimeExcpetion,不須要throws聲明的,這時調用是不可能編寫捕獲代碼的,由於調用根本就不知道有問題。一旦發生NoAgeException,調用者程序會停掉,並有jvm將信息顯示到屏幕,讓調用者看到問題,修正代碼。

7.自定義異常

在上述代碼中,發現這些異常都是JDK內部定義好的,而且這些異常很差找。書寫時也很不方便,那麼能不能本身定義異常呢?

以前的幾個異常都是java經過類進行的描述。並將問題封裝成對象,異常就是將問題封裝成了對象。這些異常很差認,書寫也很不方便,能不能定義一個符合個人程序要求的問題名稱。既然JDK中是使用類在描述異常信息,那麼咱們也能夠模擬Java的這種機制,咱們本身定義異常的信息,異常的名字,讓異常更符合本身程序的閱讀。準確對本身所須要的問題進行類的描述。

class NoAgeException 
 {
     /*
     爲何要定義構造函數,看到Java中的異常描述類中有提供對問題對象的初始化方法。
*/
     NoAgeException()
     {
     }
     NoAgeException(String message)
     {
     }
 }

 class Person{
     private String name;
     private int age;
     Person(String name,int age)    {
         //加入邏輯判斷。
         if(age<0 || age>200){
             throw new NoAgeException(age+",年齡數值非法");
         }
         this.name = name;
         this.age = age;
     }
     //定義Person對象對應的字符串表現形式。覆蓋Object中的toString方法。
     public String toString(){
         return "Person[name="+name+",age="+age+"]";
     }
 }

 class ExceptionDemo{
     public static void main(String[] args)     {
         Person p = new Person("xiaoming",-20);
         System.out.println(p);
     }
 }
複製代碼

上述代碼發生編譯失敗。

ExceptionDemo.java:19: 錯誤: 不兼容的類型
      throw new NoAgeException(age+",年齡數值非法");
                               ^
   須要: Throwable
   找到:    NoAgeException
 1 個錯誤
複製代碼

提示中說須要Throwable,啥東西?搜索API。看到Throwable描述。發現。它是異常和錯誤的超類(父類),原來它是異常體系的頂層類。

經過閱讀:自定義異常被拋出,必須是繼承Throwable,或者繼承Throwable的子類。該對象才能夠被throw拋出。原來這個異常體系具有一個特有的特性:可拋性:能夠被throw關鍵字操做

異常繼承選擇父類時,更爲確切是繼承Exception。可是發現編譯又一次失敗了。

class NoAgeException extends  Exception{
    /*
    爲何要定義構造函數,由於看到Java中的異常描述類中有提供對問題對象的初始化方法。
    */
    NoAgeException()    {
    }

    NoAgeException(String message)    {
    }
}

ExceptionDemo.java:19: 錯誤: 未報告的異常錯誤NoAgeException; 必須對其進行捕獲或聲明以便拋出
                        throw new NoAgeException(age+",年齡數值非法");
                        ^
1 個錯誤
複製代碼

經過這個編譯失敗提示,發現自定義的異常和以前所使用的異常(空指針異常,角標越界異常,無效參數異常有不一樣),拋出哪些異常沒有這個失敗提示?那麼以前的異常和自定義的異常有什麼區別呢?

經過查看api的繼承體系發現,以前的異常都是Exception下面的RuntimeException子類的子類。閱讀RuntimeException描述中有明確說明,這個運行時異常以及其子類都無需進行聲明。

能夠將自定義的異常繼承RuntimeException.

class NoAgeException extends RuntimeException
{
    /*
    爲何要定義構造函數,由於看到Java中的異常描述類中有提供對問題對象的初始化方法。
    */
    NoAgeException()    {
    }

    NoAgeException(String message)    {
    }
}
複製代碼

發現編譯運行的確正常運行,可是異常提示信息沒有了,爲何呢,查閱異常源碼,發現本身父類構造函數中有關於異常信息的操做,那麼在本身定義的異常中須要將這些信息傳遞給父類,讓父類幫咱們進行封裝便可。

class NoAgeException extends RuntimeException{
    /*
    爲何要定義構造函數,由於看到Java中的異常描述類中有提供對問題對象的初始化方法。
    */
    NoAgeException()    {
        super();
    }
    NoAgeException(String message)    {
        super(message);// 若是自定義異常須要異常信息,能夠經過調用父類的帶有字符串參數的構造函數便可。
    }
}
複製代碼

8.throw、throws和finally

8.1.throw和throws的區別

  一、throw用在函數內。throws用在函數上。

  二、thorw拋出的是異常對象。throws用於進行異常類的聲明,後面異常類能夠有多個,用逗號隔開。

8.2.finally的使用

  有一些特定的代碼不管異常是否發生,都須要執行。由於異常會引起程序跳轉,致使有些語句執行不到。沒法知足這個需求。異常捕獲處理時java提供解決方案,finally就是解決這個問題的,這個代碼塊中存放的代碼都是必定會被執行的。

//自定義異常
class NoShowException extends Exception
{
    NoShowException(String message)
    {
        super(message);
    }
}
//模擬功能類
class Demo
{
    void show(int num) throws NoShowException
    {
        if(num < 5)
        {
            throw new NoShowException(num+"非法的數據");
        }
        System.out.println("show num"+num);
    }
}
//測試類
class ExceptionDemo 
{
    public static void main(String[] args) 
    {
        Demo d = new Demo();
        try
        {
            d.show(4);
        }
        catch (NoShowException ex)
        {
            System.out.println(ex); //打印異常信息
            //若是異常發生,處理完畢後,但願功能結束;
            //可使用return ;
        }
        finally
        {
            System.out.println("一直能執行到的代碼");
        }
        System.out.println("Hello World!");
    }
}
複製代碼

總結:finally到底何時用?

只要程序中使用到了具體的資源(數據庫鏈接,IO資源,網絡鏈接socket等)須要釋放,都必須定義在finally中。你在定義程序,只要問題發生與否,指定程序都須要執行時,就定義finally中。

8.3.異常細節

8.3.1.try catch finally組合

  try catch組合 : 對代碼進行異常檢測,並對檢測的異常傳遞給catch處理。異常捕獲處理。

void show(){ //不用throws 
    try{
        throw new Exception();
    }catch(Exception e){
//處理方式    
    }
}
複製代碼

   try finally 組合: 對代碼進行異常檢測,檢測到異常後由於沒有catch,因此同樣會被默認jvm拋出。異常是沒有捕獲處理的。可是功能所開啓資源須要進行關閉,全部finally。只爲關閉資源。

void show(){//須要throws 
    try{
        throw new Exception();
    }finally {
        //關閉資源
    }
}
複製代碼

  try catch finally組合:檢測異常,並傳遞給catch處理,並定義資源釋放。

8.3.2.異常在方法複寫中細節

異常在繼承或者實現中的使用細節:

一、子類覆蓋父類方法時,若是父類的方法聲明異常,子類只能聲明父類異常或者該異常的子類,或者不聲明。

二、當父類方法聲明多個異常時,子類覆蓋時只能聲明多個異常的子集。

三、當被覆蓋的方法沒有異常聲明時,子類覆蓋時沒法聲明異常的。

舉例:父類中會存在下列這種狀況,接口也有這種狀況,

問題:接口中沒有聲明異常,而實現的子類覆蓋方法時發生了異常,怎麼辦?

答:沒法進行throws聲明,只能catch的捕獲。萬一問題處理不了呢?catch中繼續throw拋出,可是隻能將異常轉換成RuntimeException子類拋出。

參考:

java編程思想

黑馬視頻

www.cnblogs.com/dz-boss/p/1…

相關文章
相關標籤/搜索