一区二区三区视频播放_三级xxxx_7777奇米成人四色影视_色综合久久久久久_欧洲黄色一级视频_成人啪啪18免费网站

異常是什么意思?異常情況的含義介紹及優雅處理異常的方法

1. 什么是異常?

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

  • 異常 :指的是程序在執行過程中,出現的非正常的情況,最終會導致JVM的非正常停止。
  • 異常指的并不是語法錯誤,語法錯了,編譯不通過,不會產生字節碼文件,根本不能運行。
  • 在Java等面向對象的編程語言中,異常本身是一個類,產生異常就是創建異常對象并拋出了一個異常對象。
  • Java為異常設計了一套異常處理機制,當程序運行過程中發生一些異常情況時,程序不會返回任何值,而是拋出封裝了錯誤信息的異常對象。這樣保證程序代碼更加優雅,并提高程序健壯性。為什么要設計異常呢?首先,引入異常之后,我們就可以把錯誤代碼從正常代碼中分離出來進行單獨處理,這樣使代碼變得更加整潔;其次,當出現一些特殊情況時,我們還可以拋出一個檢查異常,告知調用者讓其處理。

2. 異常的體系

  • 我們先來看下異常體系結構:
  • 可以看出異常類的整體繼承關系,當然上圖不是所有的異常,也有很多子類沒有列出,這里先列出了比較常用的異常類。當然,用戶自己也可以自定義異常實現。

2.1 Throwable

所有的異常都是從Throwable繼承而來的,是所有所有錯誤與異常的超類。Throwable包含了其線程創建時線程執行堆棧的快照,它提供了 printStackTrace()等接口用于獲取堆棧跟蹤數據等信息。

  • 而Throwable體系下包含有兩個子類,Error(錯誤)和Exception(異常),它們通常用于指示發生了異常情況。二者都是 Java 異常處理的重要子類,各自都包含大量子類。

2.2 Error(錯誤)

  • 定義:Error類及其子類。程序中無法處理的錯誤,表示運行應用程序中出現了嚴重的錯誤。大多數錯誤與代碼編寫者執行的操作無關,而表示代碼運行時 JVM出現的問題。
  • 特點:對于所有的編譯時期的錯誤以及系統錯誤都是通過Error拋出的。這些錯誤表示故障發生于虛擬機自身、或者發生在虛擬機試圖執行應用時。通常有如Virtual MachineError (虛擬機運行錯誤)等。當 JVM不再有繼續執行操作所需的內存資源時,將出現 OutOfMemoryError(內存不出錯誤),還有StackOverflowError(棧溢出錯誤)等。這些異常發生時,JVM一般會選擇線程終止
  • 注意:這些錯誤是不受檢異常,非代碼性錯誤,不可查的。因為它們在應用程序的控制和處理能力之 外,而且絕大多數是程序運行時不允許出現的狀況。對于設計合理的應用程序來說,即使確實發生了錯誤,本質上也不應該試圖去處理它所引起的異常狀況。因此,當此類錯誤發生時,應用程序不應該去處理此類錯誤。

2.3 Exception(異常)

Exception 是另外一個非常重要的異常子類。程序本身可以捕獲并且可以處理的異常。這類異常一旦出現,我們就要對代碼進行更正,修復程序。Exception這種異常又分為兩類:運行時異常和編譯時異常。

2.3.1 運行時異常

  • 定義:RuntimeException 類及其子類異常,如NullPointerException (空指針異常)、IndexOutOfBoundsException (下標越界異常)等,表示 JVM在運行期間可能出現的異常。
  • 特點:此類異常,Java 編譯器不會檢查它,屬于不受檢異常。一般是由程序邏輯錯誤引起的,此類程序應該從邏輯角度盡可能避免這類異常的發生。而當程序中可能出現這類異常,即使沒有用try-catch 語句捕獲它,也沒有用throws 子句聲明拋出它,也會編譯通過。在程序中可以選擇捕獲處理,也可以不處理。如果產生運行時異常,則需要通過修改代碼來進行避免。例如,若會發生除數為零的情況,則需要通過代碼避免該情況的發生!
  • 注意:RuntimeException 異常會由JVM自動拋出并自動捕獲(就算我們沒寫異常捕獲語句運行時也會拋出錯誤!!),此類異常的出現絕大數情況是代碼本身有問題,應該從邏輯上去解決并改進代碼。這里我們來看下運行時異常是怎樣的,這里我想說下,出現異常,不要緊張,把異常的簡單類名,拷貝到API中去查。然后看是什么異常。可以看出,我們的程序邏輯出現錯誤,所以出現了算術異常。我們只要修改int b = 10就行了,或者b不等于0都可以。所以遇到異常,我們不用擔心。可以先從查看異常類名開始,看是什么異常,看是什么原因,找到我們程序出錯的地方并進行修改就可以正常運行了。
  • 那我們什么都沒有處理,那出現異常時,是誰處理了這個異常呢?是JVM的默認處理:把異常的名稱,原因,位置等信息輸出在控制臺,但是呢程序就不能繼續執行了。

2.3.2 非運行時異常(編譯時異常)

  • 定義:Exception中除 RuntimeException 及其子類之外的異常。
  • 特點:此類異常, Java 編譯器會檢查它。如果程序中出現此類異常,從程序語法角度講是必須進行處理的異常。例如:ClassNotFoundException(沒有找到指定的類異常),IOException(IO流異常),要么通過throws 進行聲明拋出,要么通過try-catch進行捕獲處理,否則不能通過編譯。
  • 注意:在程序中,通常我們不會自定義該類異常,而是直接使用系統提供的異常類。該異常我們必須手動在代碼里添加捕獲語句來處理該異常。通過注釋可以看到,createNewFile() 方法是處理了IOException異常的,而IOException異常又繼承來自Exception,是非運行時異常,所以必須處理異常。所以我們如果是編譯時異常,在編譯時期就報錯了,必須處理這個異常,不然程序不能編譯通過。

2.4 受檢異常與非受檢異常

通常,Java的異常(Throwable)分為受檢異常(checked exceptions)和非受檢異常(unchecked exceptions)。

2.4.1 受檢異常

編譯器要求必須處理的異常

  • 正確的程序在運行過程中,經常容易出現的、符合預期的異常情況。一旦發生此類異常,就必須采用某種方式進行處理。除了Exception中的 RuntimeException 及其子類以外,其他的 Exception類及其子類異常就是非運行時期異常都屬于受檢異常。
  • 這種異常編譯器會檢查它,也就是說當編譯器檢查到應用中的某處可能會此類異常時,將會提示你處理本異常——要么使用try-catch捕獲,要么使用方法簽名中用 throws 關鍵字拋出,否則編譯不通過。

2.4.2 非受檢異常

編譯器不會進行檢查并且不要求必須處理的異常。

  • 此類異常,就是當程序中出現此類異常時,即使我們沒有try-catch捕獲它,也沒有使用throws 拋出該異常,編譯也會正常通過。該類異常包括運行時異常(RuntimeException 極其子類)和錯誤( Error)。RuntimeException 發生的時候,表示程序中出現了編程錯誤,所以應該找出錯誤修改程序,而不是去捕獲RuntimeException 。

3. 異常的處理機制(重點)

在 Java 應用程序中,異常處理機制為:拋出異常,捕捉異常。

3.1 Java異常處理

異常是什么意思?異常情況的含義介紹及優雅處理異常的方法
  • 在Java中,一旦方法拋出異常,系統自動根據該異常對象尋找合適異常處理器(Exception Handler)來處理該異常,把各種不同的異常進行分類,并提供了良好的接口。
  • 在 Java 中,每個異常都是一個對象,它是 Throwable類或其子類的實例。當一個方法出現異常后便拋出一個異常對象,該對象中包含有異常信息,調用這個對象的方法可以捕獲到這個異常并可以對其進行處理。
  • Java 的異常處理涉及了 5 個關鍵詞:try、catch、 finally、throw 和throws。
  • 在Java應用中,異常的處理機制分為聲明異常throws,拋出異常throw 和捕獲異常try、catch、 finally。接下來讓我為大家詳細講述吧。

3.2 異常處理的關鍵詞

  • throw : 用于拋出異常。
  • try : 用于監聽。將要被監聽的代碼(可能拋出異常的代碼)放在try語句塊之內,當try語句塊內發生異常時,異常就被拋出。
  • catch :用于捕獲異常。catch用來捕獲try語句塊中發生的異常。
  • finally : finally語句塊總是會被執行。它主要用于回收在try塊里打開的資源(如數據庫連接、網絡連接和磁盤文件)。注意:只有finally塊,執行完成之后,才會回來執行try或者catch塊中的return或者throw 語句,如果finally中使用了return或者throw等終止方法的語句,則就不會跳回執行,直接停止。
  • throws: 用在方法簽名中,用于聲明該方法可能拋出的異常。

這里先了解下關鍵詞,具體定義格式和使用方法在下面介紹:

3.3 拋出異常throw

  • 那什么時候使用呢?作為一個合格的程序員(這不就是我嗎),在編寫程序時,我們必須要考慮程序出現問題的情況。比如,在定義方法時,方法需要接受參數。那么,當調用方法使用接受到的參數時,首先需要先對參數數據進行合法的判斷,數據若不合法,就應該告訴調用者,傳遞合法的數據進來。這時需要使用拋出異常的方式來告訴調用者。或者當你覺得解決不了某些異常問題,且不需要調用者處理,那么你也可以拋出異常。
  • throw的作用:在方法內部拋出一個Throwable 類型的異常。任何Java代碼都可以通過throw語句拋出異常。
  • 具體如何拋出一個異常呢?創建一個異常對象。封裝一些提示信息(信息可以自己編寫)。需要將這個異常對象告知給調用者。怎么告知呢?怎么將這個異常對象傳遞到調用者處呢?通過關鍵字throw就可以完成。throw異常對象。throw用在方法內,用來拋出一個異常對象,將這個異常對象傳遞到調用者處,并結束當前方法的執行。
  • 定義格式:throw new 異常類名(參數);
  • 例子:throw new NullPointerException(“要訪問的arr數組不存在”??;
    throw new ArrayIndexOutOfBoundsException(“該索引在數組中不存在,已超出范圍”??;

接下來用程序具體演示下吧:

public class ThrowDemo {
    public static void main(String[] args) {
        //創建一個數組
        int[] arr = {2,4,52,2};

        //根據索引找對應的元素
        int index = 4;
        int element = getElement(arr, index);

        System.out.println(element);
        System.out.println("over");
    }
    /*
     * 根據 索引找到數組中對應的元素
     */
    public static int getElement(int[] arr,int index){
        //判斷索引是否越界
        if(index<0 || index>arr.length-1){
             /*
                判斷條件如果滿足,當執行完throw拋出異常對象后,方法已經無法繼續運算。
                這時就會結束當前方法的執行,并將異常告知給調用者。這時就需要通過異常來解決。
              */
            throw new ArrayIndexOutOfBoundsException("老弟,你的索引越界了,別這么干");
        }
        int element = arr[index];
        return element;
    }
}

運行后輸出結果為:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 老弟,你的索引越界了,別這么干
    at com.it.test2.ThrowDemo.getElement(ThrowDemo.java:25)
    at com.it.test2.ThrowDemo.main(ThrowDemo.java:10)

可以看到我定義了索引為4,但是數組的長度只有4。所以會報錯。

注意:所以如果產生了問題,我們就會throw將問題描述類即異常進行拋出,也就是將問題返回給該方法的調用者。結果是
ArrayIndexOutOfBoundsException 的數組索引越界的問題。

那么對于調用者來說,該怎么處理呢?一種是進行捕獲處理,另一種就是繼續講問題聲明出去,使用throws聲明處理。

3.4 聲明異常throws

如果一個方法可能會出現異常,但沒有能力處理這種異常,可以在方法聲明處用throws子句來聲明拋出異常。例如汽車在運行時它可能會出現故障,汽車本身沒辦法處理這個故障,那就讓開車的人來處理。

  • 聲明異常:將問題標識出來,報告給調用者。如果方法內通過throw拋出了編譯時異常,而沒有捕獲處理(稍后講解該方式),那么必須通過throws進行聲明,讓調用者去處理。關鍵字throws運用于方法聲明之上,用于表示當前方法不處理異常,而是提醒該方法的調用者來處理異常(拋出異常).
  • 定義格式: throws語句用在方法定義時聲明該方法要拋出的異常類型,如果拋出的是Exception異常類型,則該方法被聲明為拋出所有的異常。多個異常可使用逗號分割。修飾符 返回值類型 方法名(參數) throws 異常類名1,異常類名2…{ } 注意:當方法拋出異常列表的異常時,方法將不對這些類型及其子類類型的異常作處理,而拋向調用該方法的方法,由他去處理。使用throws關鍵字將異常拋給調用者后,如果調用者不想處理該異常,可以繼續向上拋出,但最終要有能夠處理該異常的調用者。比如汽車壞了,開車的人也不會修理,只能叫修車公司來修理了。
  • 演示一下:一般來說,throws和 throw通常是成對出現的,例如:public class ThrowsDemo { public static void main(String[] args) throws FileNotFoundException { readFile(“a.txt”??; }public class ThrowsDemo {
    public static void main(String[] args) throws FileNotFoundException { readFile(“a.txt”??; } // 如果定義功能時有問題發生需要報告給調用者。可以通過在方法上使用throws關鍵字進行聲明 public static void readFile(String path) throws FileNotFoundException { if(!path.equals(“a.txt”??) {//如果不是 a.txt這個文件 // 我假設 如果不是 a.txt 認為 該文件不存在 是一個錯誤 也就是異常 throw throw new FileNotFoundException(“文件不存在”??; } }}

而throws用于進行異常類的聲明,若該方法可能有多種異常情況產生,那么在throws后面可以寫多個異常類,用逗號隔開。

public class ThrowsDemo2 {
    public static void main(String[] args) throws IOException {
        readFile("a.txt");
    }

    //若該方法可能有多種異常情況產生,那么在throws后面可以寫多個異常類,用逗號隔開
    //若有異常a是異常b的子類,也可以直接省略,寫b異常
    private static void readFile(String path) throws FileNotFoundException, IOException {
        if (!path.equals("a.txt")) {//如果不是 a.txt這個文件
            // 我假設  如果不是 a.txt 認為 該文件不存在 是一個錯誤 也就是異常  throw
            throw new FileNotFoundException("文件不存在");
        }
        if (!path.equals("b.txt")) {
            throw new IOException();
        }
    }
}

throws拋出異常的規則:

  1. 如果是非受檢異常(unchecked exception),即Error、RuntimeException或它們的子類,那么可以不使用throws關鍵字來聲明要拋出的異常,編譯仍能順利通過,但在運行時會被系統拋出。
  2. 如果一個方法可能出現受檢異常(checked exception),要么用try-catch語句捕獲,要么用throws子句聲明將它拋出,否則會導致編譯錯誤。
  3. 只有當拋出了異常時,該方法的調用者才必須處理或者重新拋出該異常。若當方法的調用者無力處理該異常的時候,應該繼續拋出。
  4. 調用方法必須遵循任何可查異常的處理和聲明規則。若覆蓋一個方法,則不聲明與覆蓋方法不同的異常。聲明的任何異常必須是被覆蓋方法所聲明異常的同類或子類。

3.5 捕獲異常try 、finally 、catch

這三個關鍵字主要有下面幾種組合方式try-catch 、try-finally、try-catch-finally。

注意:catch語句可以有一個或者多個或者沒有,finally至多有一個,try必要有。

那這里你會問有沒有單獨try模塊出現,那我想問下你,try是用來監聽是否有異常,那如果發生了異常,誰來捕獲呢?所以沒有try單獨出現。而且編譯不能通過。

所以跟try模塊一樣,例如catch,finally也不能單獨使用出現

異常是什么意思?異常情況的含義介紹及優雅處理異常的方法
  • 程序通常在運行之前不報錯,但是運行后可能會出現某些未知的錯誤,不想異常出現導致程序終止,或者不想直接拋出到上一級,那么就需要通過try-catch等形式進行異常捕獲,之后根據不同的異常情況來進行相應的處理。
  • 捕獲異常:Java中對異常有針對性的語句進行捕獲,可以對出現的異常進行指定方式的處理。

捕獲異常語法如下:

3.5.1 try-catch 形式:

try{
     編寫可能會出現異常的代碼
}catch(異常類型  e){
     處理異常的代碼
     //記錄日志/打印異常信息/繼續拋出異常
}

例如:

public class TryCatchDemo {
    public static void main(String[] args) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            //當產生異常時,必須有處理方式。要么捕獲,要么聲明。
            Date date = simpleDateFormat.parse("2020-10-06");
        } catch (ParseException e) {// 括號中需要定義什么呢?
            //try中拋出的是什么異常,在括號中就定義什么異常類型
            e.printStackTrace();//記錄日志/打印異常信息/繼續拋出異常
        }
        /*
             public Date parse(String source) throws ParseException{}
             //parse拋出了ParseException異常
             public class ParseException extends Exception {}
         */
    }
}

如何獲取異常信息:

Throwable類中定義了一些查看方法:

  • public String getMessage():獲取異常的描述信息,原因(提示給用戶的時候,就提示錯誤原因。
  • public String toString():獲取異常的類型和異常描述信息。
  • public void printStackTrace():打印異常的跟蹤棧信息并輸出到控制臺。
  • 具體我們可以來看下:
public class TryCathDemo2 {
 public static void main(String[] args) {
     SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
     try {
         //當產生異常時,必須有處理方式。要么捕獲,要么聲明。
         //演示下獲取異常信息,修改了格式。
         Date date = simpleDateFormat.parse("2020年10月06");
     } catch (ParseException e) {
         //public String getMessage():獲取異常的描述信息,原因(提示給用戶的時候,就提示錯誤原因
         System.out.println(e.getMessage());//Unparseable date: "2020年10月06"
         System.out.println(e.toString());//java.text.ParseException: Unparseable date: "2020年10月06"
         e.printStackTrace();//輸出信息而且飄紅!!!
         /*
         java.text.ParseException: Unparseable date: "2020年10月06"
             at java.text.DateFormat.parse(DateFormat.java:366)
             at com.it.test3.TryCathDemo2.main(TryCathDemo2.java:13)
          */
     }
 }
}

而如果有多個異常使用捕獲我們又該如何處理呢?

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

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

try{
     編寫可能會出現異常的代碼
}catch(異常類型A  e){  當try中出現A類型異常,就用該catch來捕獲.
     處理異常的代碼
     //記錄日志/打印異常信息/繼續拋出異常
}catch(異常類型B  e){  當try中出現B類型異常,就用該catch來捕獲.
     處理異常的代碼
     //記錄日志/打印異常信息/繼續拋出異常
}

例如:

public class TryCatchDemo3 {
    public static void main(String[] args) {
        //test();
        test2();
    }

    //多個異常一次捕獲,多次處理。
    public static void test2(){
        int[] arr = {11, 22, 66, 0};
        try {
            //System.out.println(arr[5]);//一旦這個報錯,下面的代碼就不會執行
            System.out.println(arr[2]);
            System.out.println(arr[0] / arr[arr.length-1]);
        } catch (ArithmeticException e) {
             System.out.println("除數不為0");
        }catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("數組下標越界");
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    //分別處理的方式
    public static void test() {
        int a = 10;
        int b = 0;

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

        int[] arr = {1, 2, 3};
        try {
            System.out.println(arr[4]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("數組下標越界");//數組下標越界
        }
    }
}

注意:一次捕獲,多次處理的異常處理方式,要求多個catch中的異常不能相同,并且若catch中的多個異常之間有子父類異常的關系,那么子類異常要求在上面的catch處理,父類異常在下面的catch處理。

例如:

異常是什么意思?異常情況的含義介紹及優雅處理異常的方法

3.5.2 try-finally 形式:

 try{   
  //(嘗試運行的)程序代碼   
}finally{   
  //異常發生,總是要執行的代碼   
}  

try-finally表示對一段代碼不管執行情況如何,都會走 finally 中的代碼,

例如:

public class TryFinallyDemo {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        try{
            System.out.println(a / b);
            System.out.println("會走try嗎");
        }finally{
            System.out.println("會finally嗎");//會finally嗎
        }

        System.out.println("會走外面嗎");
        /*
            沒有捕獲的話,他只會走finally語句然后報出異常。
            會finally嗎
            Exception in thread "main" java.lang.ArithmeticException: / by zero
                at com.it.test3.TryFinallyDemo.main(TryFinallyDemo.java:8)
         */
    }
}

可以看到程序異常了,還是會去走finally語句塊的代碼。

3.5.3 try-catch-finally 形式:

try {  
    // 可能會發生異常的程序代碼  
} catch (異常類型A  e){  
    // 捕獲并處置try拋出的異常類型A
} finally {  
    // 無論是否發生異常,都將執行的語句塊  
} 

跟try-finally一樣表示對一段代碼不管執行情況如何,都會走 finally 中的代碼。

當方法中發生異常,異常處之后的代碼不會再執行,如果之前獲取了一些本地資源需要釋放,則需要在方法正常結束時和 catch 語句中都調用釋放本地資源的代碼,顯得代碼比較繁瑣,finally 語句可以解決這個問題。

例如:

public class TryCatchFinallyDemo {
    public static void main(String[] args) {
        test();
    }

    public static void test() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        try {
            //date  = simpleDateFormat.parse("2020-10-06");//第一次運行成功
            date  = simpleDateFormat.parse("2020年10月06日");
        } catch (ParseException e) {
            e.printStackTrace();
        }finally{
            System.out.println("finally這里一定會執行");
        }

        System.out.println("會走外面這里嗎" + date);
    }
}

運行成功的代碼后結果:

finally這里一定會執行
會走外面這里嗎Tue Oct 06 00:00:00 CST 2020

運行失敗的代碼后結果:

java.text.ParseException: Unparseable date: "2020/10/06"
    at java.text.DateFormat.parse(DateFormat.java:366)
    at com.it.test3.TryCatchFinallyDemo.test(TryCatchFinallyDemo.java:19)
    at com.it.test3.TryCatchFinallyDemo.main(TryCatchFinallyDemo.java:12)
finally這里一定會執行
會走外面這里嗎null

可以看到,無論失敗,都會執行finally語句塊的代碼。

  • 注意:try-catch-finally中,如果catch中 return了,finally還會執行嗎?public class TryCatchFinallyDemo2 {
    public static void main(String[] args) { test(); } public static void test() { int a = 10; try{ System.out.println(a / 0); }catch(ArithmeticException e) { e.printStackTrace(); return ; }finally { System.out.println(“finally”??; } }}

運行結果:

java.lang.ArithmeticException: / by zero
    at com.it.test3.TryCatchFinallyDemo2.test(TryCatchFinallyDemo2.java:11)
    at com.it.test3.TryCatchFinallyDemo2.main(TryCatchFinallyDemo2.java:5)
finally

可以看到,就算catch中 return了,finally也會執行。

那finally是在return前呢,還是return后呢?

讓我們看下面的代碼?

public class TryCatchFinallyDemo2 {
    public static void main(String[] args) {
//        test();
        System.out.println(test2()); // 我有執行到嗎 try
        System.out.println(test3()); // 我有執行到嗎 catch
    }

    public static String test3() {
        String str = "";
        try {
            str = "try";
            System.out.println(10 / 0);
            return str;
        }catch(Exception e) {
            str = "catch";
            return str;
        }finally {
            str = "finally";
            System.out.println("我有執行到嗎");
        }
    }

    public static String test2() {
        String str = "";
        try {
            str = "try";
            return str;
        }catch(Exception e) {
            str = "catch";
            return str;
        }finally {
            str = "finally";
            System.out.println("我有執行到嗎");
        }
    }
}

運行結果:

我有執行到嗎
try
我有執行到嗎
catch

看到這里發現無論是否異常,finally都會執行,但是都在在return之前就執行了代碼。可是可是,為什么返回出來的字符串不是finally呢?讓我們一起來思考思考:

  • 我們看test2()方法,程序執行try語句塊,把變量str賦值為”try”,由于沒有發現異常,接下來執行finally語句塊,把變量str賦值為”finally”,然后return str,則t的值是finally,最后str的值就是”finally”,程序結果應該顯示finally,但是實際結果為“try”。
  • 實際上,在try語句的return塊中,當我們執行到return str這一步的時候呢,這里不是return str 而是return “try”了,這個放回路徑就已經形成了。相當于return返回的引用變量(str是引用類型)并不是try語句外定義的引用變量str,而是系統重新定義了一個局部引用str2 ,返回指向了引用str2 對應的值,也就是”try”字符串。但是到這里呢,它發現后面還有finally,所以繼續執行finally的內容,str = “finally”; System.out.println(“我有執行到嗎”??;, 再次回到以前的路徑,繼續走return “try”,形成返回路徑之后,這里的return的返回引用就不是變量str 了,而是str2引用的值”try”字符串。

是不是對這個現象有了一定的了解。嘿嘿,這里我們再轉換下:

public class TryCatchFinallyDemo2 {
    public static void main(String[] args) {
//        test();
//        System.out.println(test2()); // try
//        System.out.println(test3()); // catch
        System.out.println(test4()); 

    }

    public static String test4() {
        String str = "";
        try {
            str = "try";
            return str;
        }catch(Exception e) {
            str = "catch";
            return str;
        }finally {
            str = "finally";
            return str;
        }
    }
}

這里我們猜測下,結果是什么呢?

運行結果:finally

  • 我們發現try語句中的return語句給忽略。可能JVM認為一個方法里面有兩個return語句并沒有太大的意義,所以try中的return語句給忽略了,直接起作用的是最后finally中的return語句,就又重新形成了一條返回路徑,所以這次返回的是“finally”字符串。

再看一下: 我們是不是知道finally語句是一定執行的,但是能有辦法使他不執行嗎?

既然我說了,那么就是一定有的啦。

public class TryCatchFinallyDemo3 {
    public static void main(String[] args) {
        try{
            System.out.println(10 / 0);
        }catch(Exception e) {
            e.printStackTrace();
            System.exit(0);
        }finally {
            System.out.println("finally我有執行到嗎");
        }
    }
}

執行結果:

java.lang.ArithmeticException: / by zero
    at com.it.test3.TryCatchFinallyDemo3.main(TryCatchFinallyDemo3.java:6)

可以發現:

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

3.5.4 小結

  1. try塊:用于捕獲異常。其后可接零個或多個catch塊,如果沒有catch塊,則必須跟一個finally塊。
  2. catch塊:用于處理try捕獲到的異常。
  3. finally塊:無論是否捕獲或處理異常,finally塊里的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。在以下4種特殊情況下,finally塊不會被執行:在finally語句塊中發生了異常。在前面的代碼中用了System.exit()退出程序。程序所在的線程死亡。關閉CPU。

3.6 如何選擇異常類型

可以根據下圖來選擇是捕獲異常,聲明異常還是拋出異常

異常是什么意思?異常情況的含義介紹及優雅處理異常的方法

我們在日常處理異常的代碼中,應該遵循的原則:

  • 不要捕獲類似Exception 之類的異常,而應該捕獲類似特定的異常,方便排查問題,而且也能夠讓其他人接手你的代碼時,會減少罵你的次數。
  • 不要生吞異常。這是異常處理中要特別注重的事情。如果我們不把異常拋出來,或者也沒有輸出到日志中,程序可能會在后面以不可控的方式結束。有時候需要線上調試代碼。

4 JDK1.7有關異常新特性

4.1 try-with-resources

Java 類庫中有許多資源需要通過 close 方法進行關閉。比如 InputStream、OutputStream等。作為開發人員經常會忽略掉資源的關閉方法,導致內存泄漏。當然不是我啦!

在JDK1.7之前呢,try-catch-finally語句是確保資源會被關閉的最佳方法,就算異常或者返回也一樣可以關閉資源。

  • 讓我們先看看之前我們如何關閉資源吧:public static String readFile(String path) {
    BufferedReader br = null; try { br = new BufferedReader(new FileReader(path)); return br.readLine(); } catch (IOException e) { e.printStackTrace(); } finally {//必須在這里關閉資源 if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } return null; }}

是不是我們必須finally語句塊中手動關閉資源,否則會導致資源的泄露

在JDK1.7及以后的版本:

JDK1.7 中引入了try-with-resources 語句。

  • try-with-resources 語句是一個聲明一個或多個資源的try語句。try-with-resources 語句確保在語句的最后每個資源都被關閉,只要是實現了AutoCloseable接口或者是Closeable接口的對象都可以使用try-with-resources 來實現異常處理和關閉資源。
  • 實際上,在編譯時也會進行轉化為try-catch-finally語句。

那我們來看看怎么使用吧:

格式:

try (創建流對象語句,如果多個,使用';'隔開) {
    // 讀寫數據
} catch (IOException e) {
    e.printStackTrace();
}

演示下:

/**
 * JDK1.7之后就可以使用try-with-resources,不需要
 * 我們在finally塊中手動關閉資源
 */
public class TryWithResourcesDemo {
    public static String readLineFormFile(String path) {
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            return br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

兩者的對比:

  • 代碼精煉,在JDK1.7之前都有finally塊,如果使用一些框架可能會將finally塊交由框架處理,如Spring。JDK1.7及以后的版本只要資源類實現了AutoCloseable或Closeable程序在執行完try塊后會自動close()所使用的資源無論br.readLine()是否拋出異常。
  • 代碼更完全。在出現資源泄漏的程序中,很多情況是開發人員沒有或者開發人員沒有正確的關閉資源所導致的。JDK1.7之后采用try-with-resources 的方式,則可以將資源關閉這種與業務實現沒有很大直接關系的工作交給JVM 完成。省去了部分開發中可能出現的代碼風險。
  • 以readLineFormFile方法為例,如果調用 readLine()和 close()方法都拋出異常,后一個異常就會被禁止,以保留第一個異常。

4.2 catch多種異常并拋出新的異常

  • 在JDK1.7之前catch 多個異常是這樣的:try{
    編寫可能會出現異常的代碼
    }catch(異常類型A e){ 當try中出現A類型異常,就用該catch來捕獲.
    處理異常的代碼
    }catch(異常類型B e){ 當try中出現B類型異常,就用該catch來捕獲.
    處理異常的代碼
    }
  • JDK1.7及以后可以這樣:try{
    編寫可能會出現異常的代碼
    }catch (異常類型A | 異常類型B e) {
    處理異常的代碼
    }但是呢,這個是同類型異常才可以這樣定義。

5 自定義異常

5.1 為什么需要自定義異常類:

我們說了Java中不同的異常類,分別表示著某一種具體的異常情況,那么在開發中,當Java內置的異常都不能明確的說明異常情況的時候,需要創建自己的異常。例如年齡負數問題,考試成績負數問題。

什么是自定義異常類呢:

在開發中根據自己業務的異常情況來自己定義異常類。例如:登錄系統中,年齡能為負數嗎,不能就需要自己定義一個登錄異常類。

5.2 怎樣定義自定義異常類

  1. 自定義一個編譯期異常: 自定義類并繼承于java.lang.Exception 。
  2. 自定義一個運行時期的異常類:自定義類并繼承于java.lang.RuntimeException 。
  • 一般定義一個異常類需要包含兩個構造函數:一個無參構造函數和一個帶有詳細描述信息的構造函數
public class MyException extends Exception {
    public MyException(){ }
    public MyException(String message){
        super(message);
    }  
}

5.3 自定義異常的例子

需求:我們模擬登陸操作,如果用戶名已存在,則拋出異常并提示:親,該用戶名已經被注冊。這個相信大家也經常見到吧。

  1. 首先定義一個登陸異常類LoginException :/**
    * 登陸異常類*/public class LoginException extends Exception { public LoginException() { } public LoginException(String message) { super(message); }}

模擬登陸操作,使用數組模擬數據庫中存儲的數據,并提供當前注冊賬號是否存在方法用于判斷。

public class LoginTest {
    // 模擬數據庫中已存在賬號
    private static String[] names = {"hello", "world", "fish"};
   
    public static void main(String[] args) {     
        //調用方法
        try{
            // 可能出現異常的代碼
            checkUsername("fish");
            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;
    }
}

執行結果:注冊成功。

聲明:本文由網站用戶竹子發表,超夢電商平臺僅提供信息存儲服務,版權歸原作者所有。若發現本站文章存在版權問題,如發現文章、圖片等侵權行為,請聯系我們刪除。

(0)
上一篇 2022年12月14日 22:08:30
下一篇 2022年12月14日 22:12:18

相關推薦

發表回復

您的電子郵箱地址不會被公開。 必填項已用*標注

主站蜘蛛池模板: 四色永久访问 | 日韩欧美在线免费 | 欧美日韩国产在线一区 | 午夜天堂 | 综合久久一区 | 欧美人与性动交α欧美精品济南到 | 亚洲第一视频 | 99精品视频在线免费观看 | 欧美三级a| 国产精品a久久久久 | 交视频在线观看国产 | 国产日韩欧美在线 | 日韩国产成人av | 亚洲一区在线视频 | 亚洲精品国产综合99久久夜夜嗨 | 成人动漫免费观看 | 麻豆国产在线 | 国产精品久久久久久久久久免费看 | 二区视频在线 | 99re久久 | 欧美日韩小视频 | 免费电影天堂 | 日本老妇成熟 | 麻豆精品国产91久久久久久 | 国产2页 | 国产视频高清 | 国产99精品| 成人免费视频观看视频 | 日韩欧美国产高清 | 国产99视频精品免费视频7 | 国产91在线播放精品91 | 久久人人爽亚洲精品天堂 | 久久福利网 | 国产精品亚洲成人 | 欧美三级视频 | 久久久久久久久久久网站 | 日韩精品 | 日韩亚洲一区二区 | 精品欧美国产 | 九一网站在线观看 | 亚洲狼人综合 |