@[toc]javascript
異常 :簡單說就是不正常運行,最終致使JVM的非正常中止。java
在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使用的格式以下:
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
運行效果分析:
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基礎之多態】理解多態的向上向下轉型從「媽媽我想吃烤山藥」講起
歡迎各位關注個人公衆號,一塊兒探討技術,嚮往技術,追求技術...