目錄javascript
@java
異常 :簡單說就是不正常運行,最終致使JVM的非正常中止。程序員
在Java等面向對象的編程語言中,異常自己是一個類,產生異常就是建立異常對象並拋出了一個異常對象。Java處理異常的方式是中斷處理。數據庫
異常指的並非語法錯誤,語法錯了,編譯不經過,不會產生字節碼文件,根本不能運行.編程
異常機制實際上是幫助咱們找到程序中的問題,異常的根類是java.lang.Throwable
,其下有兩個子類:java.lang.Error
與java.lang.Exception
,日常所說的異常指java.lang.Exception
。數組
Throwable中的經常使用方法:安全
一、 public void printStackTrace()
:打印異常的詳細信息。網絡
包含了異常的類型,異常的緣由,還包括異常出現的位置,在開發和調試階段,都得使用printStackTrace。編程語言
二、 public String getMessage()
:獲取發生異常的緣由。工具
提示給用戶的時候,就提示錯誤緣由。
三、 public String toString()
:獲取異常的類型和異常描述信息(不用)。
出現異常,最簡單的方式就是把異常的簡單類名,拷貝到API中去查。
咱們日常說的異常就是指Exception
,由於這類異常一旦出現,咱們就要對代碼進行更正,修復程序。
異常(Exception)的分類:根據在編譯時期仍是運行時期去檢查異常?
先運行下面的程序,程序會產生一個數組索引越界異常ArrayIndexOfBoundsException
。咱們經過圖解來解析下異常產生的過程。
工具類
public class ArrayTools { // 對給定的數組經過給定的角標獲取元素。 public static int getElement(int[] arr, int index) { int element = arr[index]; return element; } }
測試類
public class ExceptionDemo { public static void main(String[] args) { int[] arr = { 34, 12, 67 }; intnum = ArrayTools.getElement(arr, 4) System.out.println("num=" + num); System.out.println("over"); } }
上述程序執行過程圖解:
Java異常處理的五個關鍵字:try
、catch
、finally
、throw
、throws
在編寫程序時,做爲一個優秀的程序員必需要考慮程序出現問題的狀況。舉個栗子,在定義方法時,方法須要接受參數。那麼,當調用方法使用接受到的參數時,首先須要先對參數數據進行合法的判斷,數據若不合法,就應該告訴調用者,傳遞合法的數據進來。這時須要使用拋出異常的方式來告訴調用者。這個時候throw就派上用場了!
在java中,提供了一個throw關鍵字,Throw用來拋出一個指定的異常對象。從而能夠
一、建立一個異常對象。封裝一些提示信息(信息能夠本身編寫)。
二、經過關鍵字
throw
就能夠將這個異常對象告知給調用者,還能夠將這個異常對象傳遞到調用者處。
throw
用在方法內,後面接一個異常對象,使用格式爲throw new 異常類名(參數);
,將這個異常對象傳遞到調用者處,並結束當前方法的執行。
throw使用的格式以下:
~~~java
throw new NullPointerException("要訪問的arr數組不存在");
throw new ArrayIndexOutOfBoundsException("該索引在數組中不存在,已超出範圍");
~~~
使用throw的實例使用:
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; } }
throw
運行效果分析:
再來看看不使用throw的實例使用的運行:
public static void main(String[] args) { //建立一個數組 int[] arr = {2,4,52,2}; //根據索引找對應的元素 int index = 4; //注意索引4已經越界了 int ele=arr[index]; System.out.println(ele); System.out.println("over"); }
運行結果
這樣一對比throw
的優點就不言而喻了~
還記得Objects
類嗎,曾經提到過它由一些靜態的實用方法組成,這些方法是null-save
(空指針安全的)或null-tolerant
(容忍空指針的),那麼在它的源碼中,對對象爲null的值進行了拋出異常操做。
public static <T> T requireNonNull(T obj)
:查看指定引用對象不是null。
查看源碼發現這裏對爲null的進行了拋出異常操做:
public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
若是產生了問題,咱們就會
throw
將問題描述類即異常進行拋出,也就是將問題返回給該方法的調用者。那麼對於調用者來講,該怎麼處理呢?一種是進行捕獲處理,另外一種就是繼續講問題聲明出去,使用
throws
聲明處理。
聲明異常:將問題標識出來,報告給調用者。若是方法內經過throw拋出了編譯時異常,而沒有捕獲處理(稍後講解該方式),那麼必須經過throws進行聲明,讓調用者去處理。
關鍵字throws運用於方法聲明之上,throws
格式爲修飾符 返回值類型 方法名(參數) throws 異常類名1,異常類名2…{ }
,用於表示當前方法不處理異常,而是提醒該方法的調用者來處理異常(拋出異常).
聲明異常的代碼演示:
public class ThrowsDemo { public static void main(String[] args) throws FileNotFoundException { read("a.txt"); } // 若是定義功能時有問題發生須要報告給調用者。能夠經過在方法上使用throws關鍵字進行聲明 public static void read(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 { read("a.txt"); } public static void read(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(); } } }
throw:
一、表示方法內拋出某種異常對象
二、若是異常對象是非 RuntimeException
則須要在方法申明時加上該異常的拋出 即須要加上throws
語句 或者 在方法體內try catch
處理該異常,不然編譯報錯
三、執行到throw
語句則後面的語句塊再也不執行
throws:
一、方法的定義上使用
throws
表示這個方法可能拋出某種異常
二、須要由方法的調用者進行異常處理
若是異常出現的話,會馬上終止程序,因此咱們得處理異常:
throws
)。try-catch
的語句塊來處理異常。try-catch的方式就是捕獲異常。
捕獲異常:Java中對異常有針對性的語句進行捕獲,能夠對出現的異常進行指定方式的處理。
捕獲異常語法以下:
try{ 編寫可能會出現異常的代碼 }catch(異常類型 e){ 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }
try
:該代碼塊中編寫可能產生異常的代碼。
catch
:用來進行某種異常的捕獲,實現對捕獲到的異常進行處理。
注意:
try
和catch
都不能單獨使用,必須連用。
演示以下:
public class TryCatchDemo { public static void main(String[] args) { try {// 當產生異常時,必須有處理方式。要麼捕獲,要麼聲明。 read("b.txt"); } catch (FileNotFoundException e) {// 括號中須要定義什麼呢? //try中拋出的是什麼異常,在括號中就定義什麼異常類型 System.out.println(e); } System.out.println("over"); } /* * * 咱們 當前的這個方法中 有異常 有編譯期異常 */ public static void read(String path) throws FileNotFoundException { if (!path.equals("a.txt")) {//若是不是 a.txt這個文件 // 我假設 若是不是 a.txt 認爲 該文件不存在 是一個錯誤 也就是異常 throw throw new FileNotFoundException("文件不存在"); } } }
如何獲取異常信息:
Throwable類中定義了一些查看方法:
一、 public String getMessage()
:獲取異常的描述信息,緣由(提示給用戶的時候,就提示錯誤緣由。
二、 public String toString()
:獲取異常的類型和異常描述信息(不用)。
三、 public void printStackTrace()
:打印異常的跟蹤棧信息並輸出到控制檯。
包含了異常的類型,異常的緣由,還包括異常出現的位置,在開發和調試階段,都得使用printStackTrace。
finally:有一些特定的代碼不管異常是否發生,都須要執行。另外,由於異常會引起程序跳轉,致使有些語句執行不到。而finally
就是解決這個問題的,在finally
代碼塊中存放的代碼都是必定會被執行的。
何時的代碼必須最終執行?
當咱們在try
語句塊中打開了一些物理資源(磁盤文件/網絡鏈接/數據庫鏈接等),咱們都得在使用完以後,最終關閉打開的資源。
finally的語法:
try...catch....finally
:自身須要處理異常,最終還得關閉資源。
注意:
finally
不能單獨使用。
好比在IO
流中,當打開了一個關聯文件的資源,最後程序無論結果如何,都須要把這個資源關閉掉。
finally
代碼參考以下:
public class TryCatchDemo4 { public static void main(String[] args) { try { read("a.txt"); } catch (FileNotFoundException e) { //抓取到的是編譯期異常 拋出去的是運行期 throw new RuntimeException(e); } finally { System.out.println("無論程序怎樣,這裏都將會被執行。"); } System.out.println("over"); } /* * * 咱們 當前的這個方法中 有異常 有編譯期異常 */ public static void read(String path) throws FileNotFoundException { if (!path.equals("a.txt")) {//若是不是 a.txt這個文件 // 我假設 若是不是 a.txt 認爲 該文件不存在 是一個錯誤 也就是異常 throw throw new FileNotFoundException("文件不存在"); } } }
當只有在try或者catch中調用退出JVM的相關方法,此時finally纔不會執行,不然finally永遠會執行。
還有個特別重要的點就是在try
塊或catch
塊中遇到return
語句時,finally語句塊將在方法返回以前被執行,另外finally
語句中也能夠有return
語句,可是儘可能避免有return
語句(會報警告)
多個異常使用捕獲又該如何處理呢?
多個異常分別處理。
- 多個異常一次捕獲,屢次處理。
多個異常一次捕獲一次處理。
通常咱們是使用一次捕獲屢次處理方式,格式以下:
try{ 編寫可能會出現異常的代碼 }catch(異常類型A e){ 當try中出現A類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }catch(異常類型B e){ 當try中出現B類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }
注意:這種異常處理方式,要求多個
catch
中的異常不能相同,而且若catch
中的多個異常之間有子父類異常的關係,那麼子類異常要求在上面的catch
處理,父類異常在下面的catch
處理。
異常注意小結:
運行時異常被拋出能夠不處理。即不捕獲也不聲明拋出。
若是finally
有return
語句,永遠返回finally
中的結果,避免該狀況. 上面也提到過!
若是父類拋出了多個異常,子類重寫父類方法時,拋出和父類相同的異常或者是父類異常的子類或者不拋出異常。
父類方法沒有拋出異常,子類重寫父類該方法時也不可拋出異常。此時子類產生該異常,只能捕獲處理,不能聲明拋出
爲何須要自定義異常類:
Java
中不一樣的異常類,分別表示着某一種具體的異常狀況,在開發中老是有些異常狀況是SUN
沒有定義好的,此時咱們根據本身業務的異常狀況來定義異常類。例如程序員頭髮數爲負數、小明考試成績負數問題等等。
在以前程序代碼中,發現這些異常都是JDK內部定義好的,可是實際開發中也會出現不少異常,這些異常極可能在JDK中沒有定義過,例如程序員老婆數量爲負數、程序員老王智商爲負數問題.那麼能不能本身定義異常呢?答案是能滴!
什麼是自定義異常類:
在開發中根據本身業務的異常狀況來定義異常類.
自定義一個業務邏輯異常: RegisterException。一個註冊異常類。
異常類如何定義:
java.lang.Exception
。java.lang.RuntimeException
。好比說咱們模擬註冊操做,若是用戶名已存在,則拋出異常並提示:親,該用戶名已經被註冊。
首先定義一個登錄異常類RegisterException
:
// 業務邏輯異常 public class RegisterException extends Exception { /** * 空參構造 */ public RegisterException() { } /** * * @param message 表示異常提示 */ public RegisterException(String message) { super(message); } }
模擬登錄操做,使用數組模擬數據庫中存儲的數據,並提供當前註冊帳號是否存在方法用於判斷。
public class Demo { // 模擬數據庫中已存在帳號 private static String[] names = {"bill","hill","jill"}; public static void main(String[] args) { //調用方法 try{ // 可能出現異常的代碼 checkUsername("nill"); System.out.println("註冊成功");//若是沒有異常就是註冊成功 }catch(RegisterException e){ //處理異常 e.printStackTrace(); } } //判斷當前註冊帳號是否存在 //由於是編譯期異常,又想調用者去處理 因此聲明該異常 public static boolean checkUsername(String uname) throws RegisterException { for (String name : names) { if(name.equals(uname)){//若是名字在這裏面 就拋出登錄異常 throw new RegisterException("親"+name+"已經被註冊了!"); } } return true; } }
用戶名不存在,登陸成功,效果分析以下:
用戶名存在,登陸失敗,效果分析以下:
本篇文章到這裏基本就看完了,博主我也不知道各位吸取了多少,我得對各位們負責鴨,不能講完就完事了,因而仍是以爲讓各位鞏固鞏固一下比較好,若是各位可以作出下面這道典型的異常題,說明異常方面的知識對你來講就沒多大問題了,若是以爲哪裏還不是特別懂,能夠及時告訴我,凡是看到了都會第一時間回覆。好了,試想一下下面程序運行結果是啥~
package com.gx.Expetion; public class TestException { public TestException() { } boolean testEx() throws Exception { boolean ret = true; try { ret = testEx1(); } catch (Exception e) { System.out.println("testEx, catch exception"); ret = false; throw e; } finally { System.out.println("testEx, finally; return value=" + ret); return ret; } } boolean testEx1() throws Exception { boolean ret = true; try { ret = testEx2(); if (!ret) { return false; } System.out.println("testEx1111, at the end of try"); return ret; } catch (Exception e) { System.out.println("testEx1111, catch exception"); ret = false; throw e; } finally { System.out.println("testEx1111, finally; return value=" + ret); return ret; } } boolean testEx2() throws Exception { boolean ret = true; try { int b = 12; int c; for (int i = 2; i >= -2; i--) { c = b / i; System.out.println("i=" + i); } return true; } catch (Exception e) { System.out.println("testEx2222, catch exception"); ret = false; throw e; } finally { System.out.println("testEx2222, finally; return value=" + ret); return ret; } } public static void main(String[] args) { TestException testException1 = new TestException(); try { testException1.testEx(); } catch (Exception e) { e.printStackTrace(); } } }
運行結果:
i=2 i=1 testEx2222, catch exception testEx2222, finally; return value=false testEx1111, finally; return value=false testEx, finally; return value=false
若是以爲哪裏還不是特別懂,能夠及時告訴我,凡是看到了都會第一時間回覆!!!
到這裏,不少小白同窗依舊木有感覺到博主「死了都要try,不淋漓盡致地catch我不痛快!」的那個feel,對咩?行,博主就知足知足小白的那個feel,feelfeel一下 ~=感覺感覺一下~
/** * 把多條數據的ResultSet的結果放到 List<T>中 * @param rs ResultSet結果集 * @param obj java類的class * @return */ public static <T> List<T> getResult(ResultSet rs, Class<T> obj) { try { List<T> list = new ArrayList<T>(); //ResultSetMetaData 有關 ResultSet 中列的名稱和類型的信息。 ResultSetMetaData metaData = rs.getMetaData(); //獲取總的列數 int count = metaData.getColumnCount(); //遍歷ResultSet while (rs.next()) { //---建立對象實例 T instance = obj.newInstance(); for (int i = 1; i <= count; i++) { //---獲取列名 String name = metaData.getColumnName(i); // 改變列名格式成 java 命名格式 主要是針對 _ 分割的狀況 如user_id name = toJavaField(name); //---獲取類型 Class<?> type = obj.getDeclaredField(name).getType(); //---獲取setter方法 // 首字母大寫 String replace = name.substring(0, 1).toUpperCase() + name.substring(1); Method setMethod = obj.getMethod("set" + replace, type); //---判斷讀取數據的類型 if (type.isAssignableFrom(String.class)) { setMethod.invoke(instance, rs.getString(i)); } else if (type.isAssignableFrom(int.class) || type.isAssignableFrom(Integer.class)) { setMethod.invoke(instance, rs.getInt(i)); } else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) { setMethod.invoke(instance, rs.getBoolean(i)); } else if (type.isAssignableFrom(Date.class)) { setMethod.invoke(instance, rs.getDate(i)); } } list.add(instance); } return list; } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO: handle exception } return null; }
先不說看不看得懂了,以上代碼設計反射、泛型、異常~也就是try..catch~ 、String類、jdbc相關的知識,這些知識我大部分都寫過文章了,有興趣的小白同窗就直接點藍色字體進入文章。咳咳...言歸正傳,有木有發現以上代碼中使用的就是一次捕獲屢次處理方式,格式以下:
try{ 編寫可能會出現異常的代碼 }catch(異常類型A e){ 當try中出現A類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }catch(異常類型B e){ 當try中出現B類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }
注意:這種異常處理方式,要求多個
catch
中的異常不能相同,而且若catch
中的多個異常之間有子父類異常的關係,那麼子類異常要求在上面的catch
處理,父類異常在下面的catch
處理。
這已是第二次寫到了,因此各位找到不淋漓盡致地catch不痛快的feel了咩?
最後,推薦閱讀本專欄的下兩篇java文章~有興趣的同窗能夠了解一下~
【java基礎之多態】理解多態的向上向下轉型從「媽媽我想吃烤山藥」講起
歡迎各位關注個人公衆號,一塊兒探討技術,嚮往技術,追求技術...