異常
-
什麼是異常:
- 程序運行過程中出現的錯誤
-
異常總覽:
-
Throwable:
- java語言中所有異常或錯誤的超類
- 兩個子類:
- Error:錯誤類:是指在程序中了嚴重問題,不改代碼,運行不了
- Exception:異常類:程序一般問題,可以通過處理規避掉
-
常見的Exception:
- NullPointerException:空指針異常
- IndexOutOfBoundsException:越界異常
- ClassCastException:類型轉換錯誤
- RuntimeException:運行期錯誤
- ConcurrentModificationException:併發修改異常
-
常見的Error:
- OutOfMemoryError:內存超限:比如:int[] a = new int[1000000000];
Throwable詳解:
- java語言中所有異常或錯誤的超類
- 所有繼承自Throwable的子類(java自帶),都默認使用Throwable的方法(子類基本不實現)
- 其包括如下方法:
方法 | 描述 |
---|---|
public Throwable() | 默認構造函數 |
public Throwable(String message) | 指定消息構造函數,後面該message,可通過getMessage獲取 |
public Throwable(String message,Throwable cause) | 可以由message+舊的Throwable對象,構造新的Throwable |
public Throwable(Throwable cause) | 由老的構造,生成新的Throwable |
public String getMessage() | 獲取message(簡易的) |
public String getLocalizedMessage() | 創建本地化描述,如果不做任何子類實現,其等於getMessage |
public Throwable getCause() | 返回cause |
public Throwable initCause(Throwable cause) | 初始化cause,只能初始化一次 |
public String toString() | 詳細描述(jdk文檔說是簡單描述) |
public void printStackTrace() | 將throwable及其追蹤輸出至標準輸出流 |
public void printStackTrace(PrintStream s) | 將此 throwable 及其追蹤輸出到指定的輸出流。 |
public void printStackTrace(PrintWriter s) | 將此 throwable 及其追蹤輸出到指定的 PrintWriter。 |
JVM對異常的處理:
- 示例,比如除0異常:
public class Test4 {
public static void main(String[] args) {
int a = 1 / 0;
}
}
- 輸出:
// 異常的類,以及原因
Exception in thread "main" java.lang.ArithmeticException: / by zero
//出現異常的位置(行)
at Test3.testZero(Test3.java:27)
- 說明:
- 異常也是對象(萬物皆對象),JVM發現發生了“除法的除0異常”,將異常封裝爲java.lang.ArithmeticException:new ArithmeticException(“/ by zero")
- 異常向外拋出,拋到main方法的調用者,即:JVM,此時JVM停止程序,並將異常打印到控制檯
異常的詳細程度:
- e.printStackTrace() > e.toString() > e.getMessage()
public class Test4 {
public static void main(String[] args) {
try {
div(1, 0);
} catch (Exception e) {
System.out.println("==================== e.getMessage:簡要 ====================");
System.out.println(e.getMessage());
System.out.println("==================== e.toString:較詳細 ====================");
System.out.println(e.toString());
System.out.println("==================== e.getMessage:非常詳細 ====================");
e.printStackTrace();
}
}
private static int div(int a, int b){
return a / b;
}
}
- 輸出:
==================== e.getMessage:簡要 ====================
/ by zero
==================== e.toString:較詳細 ====================
java.lang.ArithmeticException: / by zero
==================== e.getMessage:非常詳細 ====================
java.lang.ArithmeticException: / by zero
at Test4.div(Test4.java:16)
at Test4.main(Test4.java:4)
異常的繼承:
- 一般繼承如下:並且,基本都使用RuntimeException
/**
* 一般繼承時,都覆寫空構造器、以及帶有message的構造器
*/
class YRuntimeException extends RuntimeException {
public YRuntimeException() {
super();
}
public YRuntimeException(String message) {
super(message);
}
}
異常的捕獲:
- 多個異常:
private void testExp() {
// 方式1:
try {
method();
} catch (NullPointerException | ArithmeticException e) {
System.out.println(e.toString());
}
// 方式2:
try {
method();
} catch (ArithmeticException e) {
System.out.println(e.toString());
} catch (NullPointerException e) {
System.out.println(e.toString());
}
}
- 如果多個異常,並且這些異常有父子關係,則父類需要寫到最後,否則編譯失敗
class ExceptionParent extends Exception {
}
class ExceptionSon extends ExceptionParent {
}
class TestException {
public static void main(String[] args) {
try {
throw new ExceptionParent();
} catch (ExceptionParent e) {
// 這樣寫,編譯器會報錯,因爲parent異常已經可以捕獲了
} catch (ExceptionSon e) {
}
}
}
Error的捕獲
- 因爲Error的Exception是兩個不同的實現類,故捕獲的方式不一樣:
class TestY {
public static void main(String[] args) {
try {
int[] a = new int[1000000000];
// 這裏會報OutOfMemoryError,只能用Error或者Throwable捕獲
} catch (Error e) {
System.out.println("捕獲到異常了");
System.out.println(e.toString());
}
}
}
異常拋出:
- throw:方法內部,手動拋出異常
- throws:方法上,聲明本方法拋出的異常類型,調用此方法的必須進行處理
- 運行時異常:RuntimeException或者其子類:
- 特點:運行時異常,方法上不需要throws聲明,調用者也不需要處理
class XException extends Exception {
}
class YRuntimeException extends RuntimeException {
}
class TestE {
// 只需要拋出XEception即可,RuntimeException及其子類不需要拋出
public void show(int a) throws XException{
if (a == 0) {
throw new XException();
} else {
throw new YRuntimeException();
}
}
}
finally
- finally中代碼快一定會執行,格式:
- try catch finally
- try finally
- 正常場景:try catch finally都有返回
class TestFinally1 {
public static void main(String[] args) {
System.out.println(show()); // 輸出爲3
}
private static int show() {
try {
return 1;
} catch (Exception e) {
return 2;
} finally {
return 3; // finally最終返回的是3
}
}
}
* 過程分析:執行到return 1;時,jvm先預留,不直接返回。等到finlly執行完成後,再返回。此時發現finally裏有返回,則按照finally返回,故輸出爲3.
- 正常場景:finally無返回:
class TestFinally1 {
public static void main(String[] args) {
System.out.println(show1()); // 返回爲1
}
private static int show1() {
try {
return 1; // 最後返回這裏
} catch (Exception e) {
return 2;
} finally {
System.out.println("no return”); // 首先,會打印這裏
}
}
}
輸出:
no return
1
- 異常場景:無finally,最終按照catch中返回
private static int showException() {
try {
int a = 1 / 0;
return 1;
} catch (Exception e) {
return 2; // 最終方法返回的是這裏
}
}
- 異常場景:有finally:則按照finally中返回
private static int showException() {
try {
int a = 1 / 0;
return 1;
} catch (Exception e) {
return 2;
} finally {
return 3; // 最終返回的是這裏:3
}
}
注意:這裏邏輯比較繞,但最好不要在finally中寫return
繼承關係中方法的異常處理:
- 父類方法沒有異常,則子類繼承的方法也不能有:
class A {
public void show() {
}
}
class B extends A {
// 正確寫法:
@Override
public void show() {
}
// 錯誤寫法:
// @Override
// public void show() throws ExceptionSon{
// }
}
- 父類方法有異常,則子類:
- 可以不寫
- 或者聲明爲該異常的子類(異常的父類會報錯)
class AException extends Exception {
}
class BException extends AException {
}
class A {
public void show() throws AException{
}
}
class B extends A {
// 正確寫法1:不拋異常
// @Override
// public void show() {
// }
// 正確寫法2:拋出爲父類異常的子類
@Override
public void show() throws BException{
}
// 錯誤寫法:拋出的異常比父類要高
// @Override
// public void show() throws Exception{
// }
}