Java 異常處理
異常是程序中的一些錯誤眉反,但并不是所有的錯誤都是異常,并且錯誤有時候是可以避免的
比如說穆役,代碼少了一個分號寸五,那么運行出來結果是提示是錯誤 java.lang.Error
再比如,如果用 System.out.println(11/0)耿币,因為用 0 做了除數梳杏,會拋出 java.lang.ArithmeticException 的異常
異常發(fā)生的原因有很多,通常包含以下幾大類
- 用戶輸入了非法數據
- 要打開的文件不存在
- 網絡通信時連接中斷淹接,或者 JVM 內存溢出
這些異常有的是因為用戶錯誤引起十性,有的是程序錯誤引起的,還有其它一些是因為物理錯誤引起的
要理解 Java 異常處理是如何工作的塑悼,需要掌握以下三種類型的異常
- 檢查性異常: 最具代表的檢查性異常是用戶錯誤或問題引起的異常劲适,這是程序員無法預見的。例如要打開一個不存在文件時厢蒜,一個異常就發(fā)生了霞势,這些異常在編譯時不能被簡單地忽略
- 運行時異常: 運行時異常是可能被程序員避免的異常。與檢查性異常相反斑鸦,運行時異炽倒保可以在編譯時被忽略
- 錯誤: 錯誤不是異常,而是脫離程序員控制的問題巷屿。錯誤在代碼中通常被忽略固以。例如,當棧溢出時嘱巾,一個錯誤就發(fā)生了憨琳,它們在編譯也檢查不到的
Exception 類的層次
所有的異常類是從 java.lang.Exception 類繼承的子類
Exception 類是 Throwable 類的子類
除了 Exception 類外,Throwable 還有一個子類 Error
Error 用來指示運行時環(huán)境發(fā)生的錯誤
Java 程序通常不捕獲錯誤浓冒,錯誤一般發(fā)生在嚴重故障時栽渴,它們在 Java 程序處理的范疇之外
例如,JVM 內存溢出稳懒。一般地闲擦,程序不會從錯誤中恢復
異常類有兩個主要的子類:IOException 類和 RuntimeException 類
Java 內置異常類
Java 在 java.lang 包中定義了一些異常類
標準運行時異常類的子類是最常見的異常類
由于 java.lang 包是默認加載到所有的 Java 程序的,所以大部分從運行時異常類繼承而來的異常都可以直接使用
Java 根據各個類庫也定義了一些其他的異常
下表中列出了 Java 的非檢查性異常
異常 | 描述 |
---|---|
ArithmeticException | 當出現異常的運算條件時场梆,拋出此異常墅冷。例如,一個整數"除以零"時或油,拋出此類的一個實例 |
ArrayIndexOutOfBoundsException | 用非法索引訪問數組時拋出的異常寞忿。如果索引為負或大于等于數組大小,則該索引為非法索引 |
ArrayStoreException | 試圖將錯誤類型的對象存儲到一個對象數組時拋出的異常 |
ClassCastException | 當試圖將對象強制轉換為不是實例的子類時顶岸,拋出該異常 |
IllegalArgumentException | 拋出的異常表明向方法傳遞了一個不合法或不正確的參數 |
IllegalMonitorStateException | 拋出的異常表明某一線程已經試圖等待對象的監(jiān)視器腔彰,或者試圖通知其他正在等待對象的監(jiān)視器而本身沒有指定監(jiān)視器的線程 |
IllegalStateException | 在非法或不適當的時間調用方法時產生的信號叫编。換句話說,即 Java 環(huán)境或 Java 應用程序沒有處于請求操作所要求的適當狀態(tài)下 |
IllegalThreadStateException | 線程沒有處于請求操作所要求的適當狀態(tài)時拋出的異常 |
IndexOutOfBoundsException | 指示某排序索引(例如對數組霹抛、字符串或向量的排序)超出范圍時拋出 |
NegativeArraySizeException | 如果應用程序試圖創(chuàng)建大小為負的數組搓逾,則拋出該異常 |
NullPointerException | 當應用程序試圖在需要對象的地方使用null時,拋出該異常 |
NumberFormatException | 當應用程序試圖將字符串轉換成一種數值類型杯拐,但該字符串不能轉換為適當格式時霞篡,拋出該異常 |
SecurityException | 由安全管理器拋出的異常,指示存在安全侵犯 |
StringIndexOutOfBoundsException | 此異常由String方法拋出端逼,指示索引或者為負朗兵,或者超出字符串的大小 |
UnsupportedOperationException | 當不支持請求的操作時,拋出該異常 |
下表中列出了定義在 java.lang 包中的檢查性異常類
異常 | 描述 |
---|---|
ClassNotFoundException | 應用程序試圖加載類時顶滩,找不到相應的類余掖,拋出該異常 |
CloneNotSupportedException | 當調用Object類中的clone方法克隆對象,但該對象的類無法實現Cloneable接口時诲祸,拋出該異常 |
IllegalAccessException | 拒絕訪問一個類的時候浊吏,拋出該異常 |
InstantiationException | 當試圖使用Class類中的newInstance方法創(chuàng)建一個類的實例,而指定的類對象因為是一個接口或是一個抽象類而無法實例化時救氯,拋出該異常 |
InterruptedException | 一個線程被另一個線程中斷找田,拋出該異常 |
NoSuchFieldException | 請求的變量不存在 |
NoSuchMethodException | 請求的方法不存在 |
Throwable 類提供的方法
下表列出了 Throwable 類的主要方法
方法及說明 |
---|
public String getMessage() |
返回關于發(fā)生的異常的詳細信息。這個消息在Throwable 類的構造函數中初始化了 |
public Throwable getCause() |
返回一個 Throwable 對象代表異常原因 |
public String toString() |
使用getMessage()的結果返回類的串級名字 |
public void printStackTrace() |
打印toString()結果和棧層次到System.err着憨,即錯誤輸出流 |
public StackTraceElement [] getStackTrace() |
返回一個包含堆棧層次的數組墩衙。下標為0的元素代表棧頂,最后一個元素代表方法調用堆棧的棧底 |
public Throwable fillInStackTrace() |
用當前的調用棧層次填充 Throwable 對象棧層次甲抖,添加到棧層次任何先前信息中 |
捕獲異常
可以使用 try 和 catch 關鍵字可以捕獲異常
try/catch 語句塊可以放在異称岣模可能發(fā)生的地方
try {
// 程序代碼
}catch(ExceptionName e1) {
// Catch 塊
}
try 語句塊中的的代碼稱為保護代碼
catch 語句包含要捕獲異常類型的聲明,當保護代碼塊中發(fā)生一個異常時准谚,try 后面的 catch 塊就會被檢查
如果發(fā)生的異常包含在 catch 塊中挫剑,異常會被傳遞到該 catch 塊,這和傳遞一個參數到方法是一樣
范例
下面的范例聲明有兩個元素的一個數組柱衔,當代碼試圖訪問數組的第三個元素的時候就會拋出一個異常
import java.io.*;
public class ExcepTest {
public static void main(String args[]) {
try{
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Exception thrown :" + e);
}
System.out.println("Out of the block");
}
}
編譯運行以上 Java 語句樊破,輸出結果如下
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
多重捕獲塊
try 語句后面可以添加任意數量的 catch 語句塊
一個 try 語句塊后面可以跟多個 catch 代碼塊的情況就叫多重捕獲
try{
// 程序代碼
}catch(異常類型1 異常的變量名1){
// 程序代碼
}catch(異常類型2 異常的變量名2){
// 程序代碼
}catch(異常類型2 異常的變量名2){
// 程序代碼
}
上面的代碼段包含了 3 個 catch 塊
這些 catch 語句塊的運行邏輯如下
如果保護代碼中發(fā)生異常,異常被拋給第一個 catch 塊
如果拋出異常的數據類型與 ExceptionType1 匹配唆铐,它在這里就會被捕獲
如果不匹配哲戚,它會被傳遞給第二個 catch 塊
如此,直到異常被捕獲或者通過所有的 catch 塊
下面的代碼演示了如何使用多重 try/catch
try
{
file = new FileInputStream(fileName);
x = (byte) file.read();
}catch(IOException i) {
i.printStackTrace();
return -1;
}catch(FileNotFoundException f) {
f.printStackTrace();
return -1;
}
throws / throw 關鍵字
如果一個方法沒有捕獲一個檢查性異常艾岂,那么該方法必須使用 throws 關鍵字來聲明
throws 關鍵字放在方法簽名的尾部
throw 關鍵字則用于拋出一個異常顺少,無論它是新實例化的還是剛捕獲到的
下面方法的聲明拋出一個 RemoteException 異常
import java.io.*;
public class className
{
public void deposit(double amount) throws RemoteException
{
// 方法實現
throw new RemoteException();
}
// 其它類的屬性和方法定義
}
一個方法可以聲明拋出多個異常,多個異常之間用逗號隔開
下面的方法聲明拋出 RemoteException 和 InsufficientFundsException
import java.io.*;
public class className
{
public void withdraw(double amount) throws RemoteException,
InsufficientFundsException
{
// 方法語句塊
}
// ... 類的其它方法定義
}
finally 語句
finally 語句用來創(chuàng)建在 try 代碼塊后面執(zhí)行的代碼塊
無論是否發(fā)生異常,finally 代碼塊中的代碼總會被執(zhí)行
因此可以在 finally 代碼塊中運行清理類型等收尾善后性質的語句
finally 代碼塊出現在 catch 代碼塊最后
try{
// 程序代碼
}catch(異常類型1 異常的變量名1){
// 程序代碼
}catch(異常類型2 異常的變量名2){
// 程序代碼
}finally{
// 程序代碼
}
下面的范例演示了如何使用 finally 語句
public class ExcepTest {
public static void main(String args[]) {
int a[] = new int[2];
try {
System.out.println("Access element three :" + a[3]);
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("Exception thrown :" + e);
} finally {
a[0] = 6;
System.out.println("First element value: " +a[0]);
System.out.println("The finally statement is executed");
}
}
}
編譯運行以上 Java 代碼脆炎,輸出結果如下
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed
try...catch...finally 注意點
try...catch...finally 語句在使用過程中梅猿,有幾點需要注意
- catch 不能獨立于 try 存在
- 在 try/catch 后面添加 finally 塊并非強制性要求的
- try 代碼后不能既沒 catch 塊也沒 finally 塊
- try, catch, finally 塊之間不能添加任何代碼
自定義異常
Java 允許開發(fā)者自定義異常
class MyException extends Exception{}
但有幾點規(guī)則需要遵守
- 所有異常都必須是 Throwable 的子類
- 如果希望寫一個檢查性異常類,則需要繼承 Exception 類
- 如果你想寫一個運行時異常類腕窥,那么需要繼承 RuntimeException 類
只繼承 Exception 類來創(chuàng)建的異常類是檢查性異常類
一個異常類和其它任何類一樣粒没,包含有變量和方法。
下面的 InsufficientFundsException 類是用戶定義的異常類簇爆,它繼承自 Exception
范例
下面的范例是一個銀行賬戶的模擬,通過銀行卡的號碼完成識別爽撒,可以進行存錢和取錢的操作
import java.io.*;
//自定義異常類入蛆,繼承 Exception 類
public class InsufficientFundsException extends Exception
{
//此處的 amount 用來儲存當出現異常 ( 取出錢多于余額時 ) 所缺乏的錢
private double amount;
public InsufficientFundsException(double amount)
{
this.amount = amount;
}
public double getAmount()
{
return amount;
}
}
定好了一個異常后,我們就能在接下來的代碼中使用它了
下面的 CheckingAccount 類中包含一個 withdraw() 方法拋出一個 InsufficientFundsException 異常
import java.io.*;
//此類模擬銀行賬戶
public class CheckingAccount
{
//balance為余額硕勿,number為卡號
private double balance;
private int number;
public CheckingAccount(int number)
{
this.number = number;
}
//方法:存錢
public void deposit(double amount)
{
balance += amount;
}
//方法:取錢
public void withdraw(double amount) throws
InsufficientFundsException
{
if(amount <= balance)
{
balance -= amount;
}
else
{
double needs = amount - balance;
throw new InsufficientFundsException(needs);
}
}
//方法:返回余額
public double getBalance()
{
return balance;
}
//方法:返回卡號
public int getNumber()
{
return number;
}
}
然后我們就可以寫一個測試類調用 CheckingAccount 類的 deposit() 和 withdraw() 方法
BankDemo.java
public class BankDemo
{
public static void main(String [] args)
{
CheckingAccount c = new CheckingAccount(101);
System.out.println("Depositing $500...");
c.deposit(500.00);
try {
System.out.println("\nWithdrawing $100...");
c.withdraw(100.00);
System.out.println("\nWithdrawing $600...");
c.withdraw(600.00);
}catch(InsufficientFundsException e) {
System.out.println("Sorry, but you are short $"
+ e.getAmount());
e.printStackTrace();
}
}
}
編譯運行上面的 3 個文件哨毁,輸出結果如下
Depositing
100...
Withdrawing200.0
InsufficientFundsException
at CheckingAccount.withdraw(CheckingAccount.java:25)
at BankDemo.main(BankDemo.java:13)
異常級別
Java 程序中有兩種級別的異常和錯誤
-
JVM( Java 虛擬機 ) 異常: 由 JVM 拋出的異常或錯誤
例如:NullPointerException 類源武,ArrayIndexOutOfBoundsException 類扼褪,ClassCastException 類
-
程序級異常: 由程序或者 API 程序拋出的異常
例如 IllegalArgumentException 類,IllegalStateException 類