異常,在Java程序中指的是:因程序問題而中斷程序執行的現象。java
在Java中,爲了維護程序正常執行,Java提供了處理異常的異常機制(異常類)。數據庫
在Java提供的異常機制中,其中java.lang.Throwable是根類,而根類的派生類有java.lang.Error和java.lang.Excepiton兩個子類。編程
Error,錯誤(絕症),該類型異常在程序中沒法處理,只能儘可能避免。數組
Excepiton,編譯期異常(寫源代碼時)(小毛病,好比:相似感冒),該類型異常在程序中是可處理的。Excepiton類型還有一個子類RunTimeException,表示運行期異常(程序運行的過程當中),該類型異常在程序中也是可處理的。網絡
爲了更好的區分以上描述的異常分類,咱們看如下程序。ui
// 【Error異常】 // Exception in thread "main" java.lang.OutOfMemoryError: Java heap space // 內存溢出。超出了分配給JVM內存大小。 // 該程序只能修改源代碼解決問題。 int[]nums = new int[1024*1024*1024]; // 【Exception異常】 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); // Unhandled exception: java.text.ParseException // 此處在編寫源代碼時就發生異常,該異常後續能夠經過相關的處理機制處理和避免 Date date = format.parse("2083-10-10"); // 【RunTimeException異常】 int[] nums = {1,2,3}; // Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException // 在程序運行時出現的異常,數組下標超出。該異常能夠經過相關異常機制處理 System.out.println(nums[4]);
在編程中,爲了更好的處理異常,咱們首先要了解異常產生的過程。下面經過一段異常代碼來分析。spa
代碼:調試
// 需求:定義一個方法,用來獲取指定數組的指定位置的元素 public static void main(String[] args) { int[]nums = {1,2,3}; System.out.println(getElement(nums,4)); } public static int getElement(int[]arr,int index){ return arr[index]; }
分析:日誌
圖解:code
異常相關的關鍵字有:
做用:在指定的方法內部拋出異常。
格式:throw new xxxException(參數)
注意事項:
代碼:
public static void main(String[] args) { int[]nums = {1,2,3}; // getElement(nums,4); // 異常:java.lang.ArrayIndexOutOfBoundsException: 索引越界:4 int[]nums2=null; getElement(nums2,0); // 異常java.lang.NullPointerException: 傳入的數組對象爲null } public static int getElement(int[]arr,int index){ if(arr==null){ throw new NullPointerException("傳入的數組對象爲null"); } if(index<0||index>=arr.length){ throw new ArrayIndexOutOfBoundsException("索引越界:" + index); } return arr[index]; }
Objects中提供了一個方法,能夠檢測一個對象爲null並拋出異常,咱們來應用一下,並查看其源代碼更好的理解throw關鍵字的應用。
方法名稱:public static <T> T requireNonNull(T obj)
:查看指定引用對象不是null。
源代碼:
public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
應用:
public static void main(String[] args) { int[]nums = {1,2,3}; // getElement(nums,4); // 異常:java.lang.ArrayIndexOutOfBoundsException: 索引越界:4 int[]nums2=null; getElement(nums2,0); // 異常java.lang.NullPointerException } public static int getElement(int[]arr,int index){ Objects.requireNonNull(arr); // 應用 if(index<0||index>=arr.length){ throw new ArrayIndexOutOfBoundsException("索引越界:" + index); } return arr[index]; }
當程序拋出一個異常對象給調用者時,調用者必需要處理異常,此時能夠選擇throws關鍵字來處理。
throws關鍵字的做用就是把異常對象繼續拋給其餘調用者,若其餘調用者沒有處理異常,則最終會拋給JVM來處理。
格式:
/* 修飾符 返回值類型 方法名() throws XXX1Exception,XXX2Exception... { throw new XXX1Exception("產生的緣由"); throw new XXX2Exception("產生的緣由"); } */
注意事項:
代碼
// 需求:定義一個讀取文件的方法,檢測文件的路徑和後綴名 public static void main(String[] args) throws Exception { retFile("a.txt"); } // 讀取文件的方法 // private static void retFile(String path) throws FileNotFoundException,IOException { private static void retFile(String path) throws IOException { // 若是後綴名不是.txt則拋出 if(!path.contains(".txt")){ throw new FileNotFoundException("文件後綴名不是.txt"); } if(path.contains("c:")) { throw new IOException("文件路徑錯誤"); } }
throws聲明異常的弊端是:異常後續代碼沒法執行(由於交給了JVM,JVM會終止程序)。
try-catch可讓調用者處理異常,並會繼續執行後續程序。
格式
try { // 可能會發生異常的程序 // 若發生異常,try會檢測到異常對象 }catch(異常類型 變量) { // catch會接收到異常對象,能夠在此代碼塊中進行處理 }catch(異常類型 變量) { // catch會接收到異常對象,能夠在此代碼塊中進行處理 }...
throwable中經常使用的異常方法
代碼
// 需求:定義一個讀取文件的方法,檢測文件的路徑和後綴名 public static void main(String[] args) { try{ retFile("d.txt"); }catch (Exception e) { e.printStackTrace(); } System.out.println("後續代碼"); /* 執行結果: java.io.IOException: 文件路徑錯誤 at it.leilei.cn.demo01.Main01.retFile(Main01.java:27) at it.leilei.cn.demo01.Main01.main(Main01.java:12) 後續代碼 */ } // 讀取文件的方法 private static void retFile(String path) throws IOException { // 若是後綴名不是.txt則拋出 if(!path.contains(".txt")){ throw new FileNotFoundException("文件後綴名不是.txt"); } if(!path.contains("c:")) { throw new IOException("文件路徑錯誤"); } }
finally關鍵字的做用:有一些特定的代碼不管異常是否發生,都須要執行。另外,由於異常會引起程序跳轉,致使有些語句執行 不到。而finally就是解決這個問題的,在finally代碼塊中存放的代碼都是必定會被執行的。
finally使用格式:try...catch...finally自身須要處理異常,最終還得關閉資源。
finally關鍵字的應用場景:當咱們在try語句塊中打開了一些物理資源(磁盤文件/網絡鏈接/數據庫鏈接等),咱們都得在使用完以後,最終關閉打開的資源。
代碼:
// 需求:定義一個讀取文件的方法,檢測文件的路徑和後綴名 public static void main(String[] args) { try{ retFile("d.txt"); }catch (Exception e) { e.printStackTrace(); }finally { System.out.println("釋放資源"); } System.out.println("後續代碼"); } // 讀取文件的方法 private static void retFile(String path) throws IOException { // 若是後綴名不是.txt則拋出 if(!path.contains(".txt")){ throw new FileNotFoundException("文件後綴名不是.txt"); } if(!path.contains("c:")) { throw new IOException("文件路徑錯誤"); } }
多個異常使用捕獲又該如何處理呢?
通常咱們是使用一次捕獲屢次處理方式,格式以下:
格式:
try{ 編寫可能會出現異常的代碼 }catch(異常類型A e){ 當try中出現A類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }catch(異常類型B e){ 當try中出現B類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }
注意:這種異常處理方式,要求多個catch中的異常不能相同,而且若catch中的多個異常之間有子父類異
常的關係,那麼子類異常要求在上面的catch處理,父類異常在下面的catch處理。
運行時異常被拋出能夠不處理。即不捕獲也不聲明拋出(JVM默認會進行處理)。
若是finally有return語句,永遠返回finally中的結果,避免該狀況.
public static void main(String[] args) { int num = getNum(); System.out.println(num); // 100 } // 讀取文件的方法 public static int getNum() { int a = 10; try { return a; }catch (Exception e){} finally { a = 100; return a; } }
若是父類拋出了多個異常,子類重寫父類方法時,拋出和父類相同的異常或者是父類異常的子類或者不拋出異 常。
class Fu{ // 若是父類拋出了多個異常,子類重寫父類方法時,拋出和父類相同的異常或者是父類異常的子類或者不拋出異 常。 public void show1() throws IndexOutOfBoundsException{}; } class Zi extends Fu{ // 子類重寫父類方法時,拋出和父類相同的異常 // public void show1() throws IndexOutOfBoundsException{}; // 或者是父類異常的子類ArrayIndexOutOfBoundsException 繼承 IndexOutOfBoundsException // public void show1() throws ArrayIndexOutOfBoundsException{}; // 不拋出異常 public void show1() {}; }
咱們說了Java中不一樣的異常類,分別表示着某一種具體的異常狀況,那麼在開發中老是有些異常狀況是SUN沒有定義好的,此時咱們根據本身業務的異常狀況來定義異常類。例如年齡負數問題,考試成績負數問題等等。
在開發中根據本身業務的異常狀況來定義異常類.
自定義一個業務邏輯異常: RegisterException。一個註冊異常類。
要求:咱們模擬註冊操做,若是用戶名已存在,則拋出異常並提示:親,該用戶名已經被註冊。
public static void main(String[] args) { // 接收用戶註冊的用戶名 String name = new Scanner(System.in).next(); try{ check(name); }catch (RegisterException e){ e.printStackTrace(); return; } System.out.println("註冊成功"); } public static void check(String n) throws RegisterException{ String[]users={"張三","李四","王五"}; for (int i = 0; i < users.length; i++) { if (n.equals( users[i])) { throw new RegisterException("親,該用戶名已經註冊"); } } }
public class RegisterException extends Exception{ public RegisterException(){ super(); } public RegisterException(String message){ super(message); } }