一、異常的產生
先看下面的這個demo:
/*
* 異常的產生
*/
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println(0/0);
System.out.println("hello world");
}
}
此時程序就產生 的異常如下:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at exceptions.ExceptionDemo.main(ExceptionDemo.java:11)
分析:
- JVM檢測到了問題(0是除數的問題),于是程序發生了異常
- JVM就把這個異常進行了對象的封裝
new ArithmeticException()
- 異常對象被拋給調用者main中,main方法接收到這個異常后,由于程序中沒有對異常進行處理的方法,因此mian方法也不能處理此異常,于是異常又被拋出到JVM中
- JVM接受到異常后,采取了默認的處理措施,停止運行程序,于是后面的這個syso語句沒有執行。
異常也是對象,上述例子中異常對象的描述類是ArithmeticException,它是用來描述數學中算數問題的 異常類。比如還有常見的空指針異常描述類NullPointerException。
二、異常的繼承體系
Throwable類是所有異常和錯誤的父類
Error 錯誤
-
Exception 異常
錯誤:程序出現了嚴重的問題,不修改代碼,根本不能運行,人得了非典,艾滋,癌
異常:程序出現了比較輕的問題,處理掉之后,繼續運行,人得了闌尾炎,感冒
Exception類是所有異常的父類
- 非RuntimeException
- RuntimeException
Throwable中的方法:
- String toString()重寫Object類的方法,異常信息的詳細描述?
- String getMessage() 返回異常信息的簡短描述
- void printStackTrace() 異常信息輸出到控制臺
三、異常的兩種處理方式
-
直接處理掉異常
demo:
try{ 嘗試捕獲異常的代碼 }catch(異常類 異常變量){ 異常處理代碼 }
-
第二種處理方式就是拋出異常
- throw 手動拋出異常,后面寫的是new異常的對象,寫在方法中
- throws 方法聲明拋出異常,后面寫的是異常類,寫在方法的聲明上
demo:
/* 異常第二種處理方式,拋出異常 throw throws的用法 ExceptionDemo2.java:21: 錯誤: 未報告的異常錯誤Exception; 必須對其進行捕獲或聲明 以便拋出 throw new Exception(); 方法中,有異常拋出,但是沒有處理過,因此編譯失敗 異常的編譯提示,是Java編譯時的最后提示 */ class ExceptionDemo2 { public static void main(String[] args) throws Exception { //System.out.println("Hello World!"); //main中調用了method方法,方法拋出了異常 //main有2個選擇方法,一個是try...catch //另外一個是,異常我也不處理,交給我的調用者處理 method(-5); } /* 如果方法的參數小于0 程序出現異常,如果參數大于0 ,程序是正常的 方法自己,不想處理這個異常,把異常交給調用者處理 在方法聲明上,拋出異常,聲明出來有異常,交給調用者 throws 異常類 */ public static void method(int x)throws Exception{ if(x < 0) //程序出現了問題 //手動拋出異常 throw new Exception("程序出現了異常了"); else System.out.println("程序正常"); } }
四、多層異常的處理
demo
/*
多層的異常處理方法調用
*/
class ExceptionDemo3
{
public static void main(String[] args)
{
//System.out.println("Hello World!");
try{
methodA();
}catch(Exception e){
}
}
public static void methodA()throws Exception{
methodB();
}
public static void methodB()throws Exception{
methodC();
}
public static void methodC()throws Exception{
throw new Exception();
}
}
五、finally代碼塊
- finally可以跟隨try出現,也可以跟隨try...catch出現
- finally代碼塊中的程序,必須要運行
- finally實際的開發意義,釋放資源
demo
/*
finally代碼塊
一定要執行
*/
class ExceptionDemo4
{
public static void main(String[] args)
{
try{
method(1);
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("這里的程序必須執行");
}
}
public static void method(int x)throws Exception{
if(x == 1)
throw new Exception("異常了!!");
else
System.out.println("程序正常了");
}
}
一個finally的特例:
/*
demo:true or false
*/
class ExceptionDemo5{
public static void main(String[] args){
System.out.println(method());
}
public static boolean method(){
try{
return false;
}catch(Exception e){
}finally{
return true;
}
}
}
//結果:true
/*
demo2:2 or 10
開發中,不要再try catch中寫return
*/
class ExceptionDemo6{
public static void main(String[] args){
System.out.println(method());
}
public static int method(){
int i = 1;
try{
return ++i;
}catch(Exception e){
return 100;
}finally{
i=10;
}
}
}
//結果:2
六、編譯時期的異常
調用一個方法,這個方法拋出一個異常,此時調用者必須處理異常,否則編譯失敗。
Demo
/*
編譯時期的異常
*/
class ExceptionDemo2{
public static void main(String[] args){
System.out.printf("hello wordl");
method();
}
public static void method() throws Exception{}
}
結果如下:
ExceptionDemo2.java:10: 錯誤: 未報告的異常錯誤Exception; 必須對其進行捕獲或聲明
以便拋出
method();
^
1 個錯誤
改進,加上try catch對異常進行處理,錯誤提示消失。
/*
編譯時期的異常
*/
class ExceptionDemo2{
public static void main(String[] args){
System.out.printf("hello wordl");
try{
method();
}catch(Exception e){}
}
public static void method() throws Exception{}
}
七、運行時期的異常
運行時期的異常一旦發生了,后面的所有程序都不會接著往下執行,所以設計運行時期的異常的初衷就很明顯了,這個異常就是讓開發人員看的,發生運行異常,就必須去修改原代碼,而不是去處理異常。
Demo
/*
運行時期的異常的特點
*/
class ExceptionDemo3{
public static void main(String[] args){
//調用者不知道方法會出現異常,所以就不用處理
//這種時候,需要修改代碼,而不需要處理
method();
}
public static void method(){
//手動拋出一個異常
throw new RuntimeException();
}
}
Demo2
/*
要求:計算正方形面積,邊長的平方
定義方法:求面積,返回結果
*/
class ExceptionDemo4{
public static void main(String[] args){
System.out.println(method(-8));
}
public static int method(int num){
if(num <= 0){
//邊長不合法,沒有必要計算
throw new RuntimeException("<= 0");
}
return num * num;
}
}
常見的運行時期的異常:
異常對象 | 實際含義 |
---|---|
IndexOutOfBoundsException | 越界(字符串,和數組) |
NullPointerException | 空指針 |
ClassCastException | 類型轉換異常 |
NoSuchElementException | 沒有元素被取出 |
IllegalArgumentException | 無效參數異常 |
八、自定義異常
Java中異常體系,將很多的情況都做了異常封裝,但是實際的開發中,不可能把所有的異常都描述完畢,需要自定義的異常,用來描述自己程序中可能發生的異常。
自定義異常步驟:
- 定義類,后綴名Exception繼承Exception類,或者繼承RuntimeException
- 異常信息,自定義的異常類的構造方法,把異常信息使用super傳遞到父類
注意:只有異常類,才具備可拋性 throw new 異常體系的類
通過剛才案例:
- 如果一個方法中,拋出多個異常,必須要throws聲明多個異常(運行時起除外)
- 調用者,使用多個catch進行異常的捕獲
- 多個catch中,最大(繼承關系)的父類,寫在最后面,否則編譯失敗
Demo
package exceptions;
/*
* 自定義異常
*/
//負數異常
class FuShuException extends Exception{
FuShuException(){}
FuShuException(String info){
super(info);
}
}
//0異常
class ZeroException extends Exception{
ZeroException(){}
ZeroException(String message){
super(message);
}
}
public class ExceptionDemo5 {
public static void main(String[] args) {
try{
getArea(0);
}catch(FuShuException e){ //多異常,就多catch,范圍越大的往后寫
e.printStackTrace();
}catch(ZeroException e){
e.printStackTrace();
}
}
public static int getArea(int num) throws FuShuException,ZeroException{
if(num < 0){
throw new FuShuException("邊長是負數");
}
else if(num == 0){
throw new ZeroException("邊長為0");
}
return (int)Math.pow(num,2);
}
}
九、繼承異常
前提:子類重寫父類的方法
- 父類的方法拋出了異常,子類重寫后,異常:父類拋異常,子類可拋可不拋,但是,如果子類拋,不能拋出比父類還大的異常
- 父類的方法不拋異常,子類重寫后,子類不能拋出異常。如果子類重寫的方法中,調用了一個拋異常的方法,子類別無選擇,只能
try...catch
處理異常
demo
package exceptions;
/*
* 自定義異常
* 1.父類方法拋異常,子類重寫后可拋可不拋,若是拋,則要小于父類(前提是子類重寫了父類方法)
* 2.父類方法不拋異常,子類也不能拋,若是子類調用的方法拋了異常,子類只能try catch
*/
//A是父類異常類
class AException extends Exception{
}
//B繼承A
class BException extends AException{
}
//C和A是兄弟類
class CException extends Exception{
}
class Zi extends Fu{
public void show() throws BException{}
}
class Fu{
public void show() throws AException{}
}
public class ExceptionDemo6 {
public static void main(String[] args) throws AException{
Fu f = new Zi();
f.show();
}
}