1. 程式人生 > 實用技巧 >異常--異常體系、異常的處理、異常的捕獲、finally語句塊和自定義異常

異常--異常體系、異常的處理、異常的捕獲、finally語句塊和自定義異常

異常

異常的概念

  異常,就是不正常的意思。在生活中,意思說你的身體某個部位出現異常了,該部位和正常情況不一樣,該部位的功能將會受到影響,在程式中的意思是:

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

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

  備註:異常指的並不是語法錯誤,語法錯誤編譯無法通過,就不會產生位元組碼檔案,根本不會執行。

異常體系

  異常機制其實是幫助我們找到程式中出現的問題,異常的根類是 java.lang.Throwble ,它的下面有兩個子類: java.lang.Error 和 java.lang.Exception ,平常說的異常知道是 java.lang.Exception 。

  • Error:嚴重錯誤Error,無法通過,好比絕症,只能是事先預防。

  • Exception:異常。異常產生後程序員可以通過程式碼的方式進行糾正處理,是程式能夠繼續進行下去,好比感冒,闌尾炎。

異常產生的過程解析:

public static void main(String[] args) {
		// 建立int型別的陣列
		int[] arr = {1,2,3};
		int e = getElement(arr, 3);
		System.out.println(e);	
	}
	/*
	 *   引數:
	 * 		int[]  arr
	 *       int   index
	 */
	public static int getElement(int[] arr,int index) {
		int e = arr[index];
		return e;
	}

異常的處理

 Java異常處理的無關關鍵字:try catch finally throw throws

丟擲異常throw

  當我們在編寫程式的時候,我們必須要考慮程式可能出現問題的情況。比如說:在定義方法的是,方法需要接受引數,那麼對於呼叫者藍色,當呼叫方法的時候需要接收引數,首先需要對引數資料進行合法的判斷,若不合法,就應該概述呼叫者,傳遞合法的資料進來。這時候就需要使用丟擲異常的方式來告訴呼叫者。

  在Java當中,提供了一個關鍵字throw,它用來丟擲一個指定的異常物件,步驟:

  1. 建立一個異常物件,封裝一些提示資訊(資訊可以自己編寫)。

  2. 需要將這個異常物件告知給呼叫者,通過關鍵字throw就可以完成。throw丟擲一個異常物件
     throw-般我們用在方法內,用來丟擲一個具體的異常物件,將這個異常物件丟擲給呼叫者處,並結束當前方法的執行。

使用格式:

     throw new 異常類名(引數)

示例:

      //給呼叫者丟擲一個空指標異常
      throw new NillPointerException("要訪問的arr陣列不存在,值為unull")
      //給呼叫者丟擲一個索引越界異常
      throw new IndexOutOfBounds Except ion( "該索引超出了引用的範圍");

Objects非空判斷

  在該類中,提供了一些靜態的實用方法,這時方法是null-save(空指標安全)或者null-tolerant(容忍空指標),那麼在它的原始碼中,對 物件的null值進行了丟擲異常的操作。

  • public static T requireNonNul(T obj):檢視指定的引用物件不是null值
      public static <T> T requireNonNul1(T obj) {
            if (obj == nul1)
                  throw new NullPointerException();
            return obj;
      }

宣告異常處理

宣告異常:將問題標識出來,報告給呼叫者,如果方法內通過throw丟擲了一個編譯異常,又沒有通過捕獲處理(try..catch),那麼必須通過throw進行宣告,讓呼叫者取處理。

  關鍵字throws運用在方法的宣告上,用於表示不處理異常T提醒呼叫者該方法的呼叫攜帶的有異常資訊,誰呼叫誰處理。

宣告異常處理的格式

      修飾符 返回值型別 方法名(引數列表) throw 異常類名1,異常類名2...{}

示例程式碼:

      public class DemoThros{
            public static void main(String[] args){
                  read("C:/a.txt");
            }

             public static void read(String path){
                  //校驗如果你傳遞的路徑不是以.txt的結尾的,丟擲呼叫名檔案找不到異常
                  if (path. endsWith(" . txt")){
                        //丟擲一個檔案找不到異常
                        throw new FileNotFoundException(“檔案找不到");
                   }
            }
      }

捕獲異常try...catch

  如果異常出現的話,會立刻終止程式。

  1. 如果使用throw關鍵字來宣告式處理,由該方法呼叫者來處理

  2. 在方法中使用try...catch的語句來處理異常

try...catch的方式就是捕獲異常

  • 捕獲異常:java中對異常有針對性的語句塊來進行捕獲,可以對出現的異常進行指定的方式處理。

捕獲異常的格式:

      try{
           //編寫的可能會出現的異常程式碼
           //.....
      } catch (異常型別e) {
            //處理異常的邏輯程式碼
            //記錄日誌//列印異常資訊//繼續往上拋
 
      }

try:該語句塊中可能出現異常的程式碼

catch:用來進行某種異常的捕獲,實現對捕獲到的異常進行處理。

備註: try和catch都不能單獨使用,一般建議連用。

程式碼演示:

public class TryCatchDemo {
      public static void main(String[] args) {
            try{
                  readFile("D:\\a.txt");
            }catch(FileNotFoundException e){//括號中需要定義什麼異常型別?
                  //try中丟擲的是什麼型別,在括號中就定義成什麼樣的異常型別
                  //列印異常資訊
                  System.out.println(e);
            }
            System.out.println("程式往下載入了。。。");
      }
      public static void readFile(String path) {
            if (!path. startsWith("C:")){
                throw new FileNotFoundException(“傳遞的檔案不是在C盤中,檔案找不到異常");  
            }
      }
}

如何獲取異常的資訊

 Throwable類中定義了一些常用的api方法

  • public String getMessage():獲取異常的描述資訊,原因(提示給使用者看的,提示錯誤資訊)

  • public String toString():獲取異常的型別、異常的描述資訊

  • public void printStackTrace():列印異常的跟蹤棧資訊並輸出到控制檯中。

包含了異常的型別,異常的原因,還包括異常出現的位置,在開發和除錯階段般都是使用printStackTrace()方法

finally語句塊

finally:有一些它的的程式碼無論是否發生,都需要執行。當程式傳送異常時描繪引發程式的跳躍性,導致有一些程式碼載入不到,而finally語句塊就是用來解決這樣的問題的,在finally語句塊中存放的程式碼一般都是一定會被執行到的

什麼樣的程式碼最終一定會把執行到的?

比如說,在try中打開了一些物理資源(磁碟檔案/網路連線/資料庫的連線),我們一般在使用完畢後必須關閉掉,可以使用finally語句塊來實現。

finally語句塊語法格式:

      trr{
            //
      }catch(異常型別 e){
            //...
      }
      ....
      finally{
            //....
      }

備註:中間的catch語句塊可以省略,finally不能單獨使用,建議連用。

程式碼演示:

      public class FinallyDemo06{
            public static void main(String[] args){
                try {
                     system.out. printIn("開啟IO流");
                         readFile("b.txt");
                } catch (IOException e) {
                     e .printStackTrace();
                }finally{
                        System out .println("不管程式如何執行,此處程式碼塊定會被載入到");
                        System. out . println("關閉IO流");
                }
                  System. out . println("over");
            }
            public static void readFile(String path) throw IOException{
                  if ( lpath. endsWith(" .txt")) {
                        throw new IOException("檔案的字尾名不對。 。。")
                  }
                  System. out . println("檔案的字尾名正確,讀取到了該檔案");
            }
      }

備註:

  1. 如果finally語句塊有return語句,永遠返回的是finally語句塊中的內容。

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

異常的注意事項

 當程式中出現了多個異常那麼該如何捕獲有該如何處理?

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

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

      try{
            //可能出現一次的程式碼 多個異常
      }catch(異常型別A e){//當try中出現A異常的時候,就該catch來捕獲
            //處理異常的邏輯
      }catch(異常型別B b){//當try中出現B異常的時候,就該catch來捕獲
            //處理異常的邏輯
      }
      ....
      ....

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

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

  • 如果finally中有return語句,那麼永遠返回finally語句塊中的結果值

  • 如果父類丟擲了多個異常,子類重寫父類方法時,丟擲和父類相同的異常或者說是父類異常的子類或者不丟擲異常

  • 父類方法如果沒有丟擲異常,子類重寫該方法也不可能丟擲異常。此時子類方法內產生了異常,只能捕獲處理,不能宣告丟擲。

自定義異常

概述

  Java中根據不同的情況提供了不同的異常類,JDK官方提供的異常類始終是有限的,大多數我們需要根據自己的業務需要自定義異常。例如:年齡負數異常。成績負數異常,登入異常,註冊異常等等。

什麼是自定義異常類?

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

例如:我想自定義一個業務邏輯異常類:註冊異常 RegisterException

異常類如何定義?

  1. 自定義一個編譯期異常,自定義類繼承於 java.lang.Exception 。

  2. 自定義一個執行期異常類,自定義繼承於 java.lang.RuntimeException 。

自定義異常的練習:

模擬註冊操作,如果使用者存在,則丟擲異常並提示:親,你的名字已經被註冊了,再換個名字吧!

// 首先 定義一個註冊異常類  RegisterException  --->業務邏輯異常類
public class RegisterException extends RuntimeException {
    
    // 空參構造
    public RegisterException(){}
    
    // 有參構造
    public RegisterException(String message) {
        super(message);
    }
}

public class Demo {
    // 使用陣列模擬資料庫 已存在多個賬戶名稱
    public static String[] names = {"小孫","小王","小趙"};
    
    public static void main(String[] args) {
        // Scanner
        Scanner scan = new Scanner(System.in);
        String name = scan.next();   
        // 校驗賬戶是否已被註冊過
       try {
           // 可能會引發異常的程式碼
           checkName(name);// 如果沒有發生異常,就代表註冊成功
           System.out.println("註冊成功!")
           names = Arrays.copyOf(names,names.length+1);
           names[names.length-1] = name;
       } catch (RegisterException e) {
          // 異常的處理邏輯
           e.printStackTrace();
       }           
    }
     
    // 校驗賬戶是否已被註冊
    public static boolean checkName(String usernmae) throws RegisterException {
        for (String name : names) {
            if (name.equals(username)) {
                // 表明 名字已經註冊過了,就丟擲註冊異常
                throw new RegisterException("親,您的名字已經被註冊了,再換個名字吧!");
            }
        }
        return true;
    }
}