Java Basics Part 19/20 - Exceptions
目錄
異常(Exception) 是運行期出現錯誤帶來的問題。當程序發生異常的時候,會不正常終止,而這不是程序員想看到,所以就引入了 異常機制,來處理運行期拋出的異常。
異常可以由很多因素導致,下面是一些常見的異常場景:
- 用戶輸入非法數據
- 需要打開的文件不存在
- 在通信過程中網絡斷開或者是 JVM 內存溢出了
一些異常由用戶錯誤導致,還有一些有程序員導致,還有一些是物理資源導致。
基於上述原因種類不同,我們可以給異常分類:
Checked exceptions:checked exception 是在編譯期間出現的異常,這些異常也叫作編譯期異常。它們在編譯期不能忽略,程序員應該把這些異常處理掉。
比如,如果使用 FileReader 類讀取一個文件,如果文件不存在,那麼就會拋出 FileNotFoundException,編譯期會提示程序員解決。import java.io.File; import java.io.FileReader; public class FilenotFound_Demo { public static void main(String args[]){ File file=new File("E://file.txt"); FileReader fr = new FileReader(file); } } // output FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown FileReader fr = new FileReader(file);
Unchecked exceptions:Unchecked exceptions 是運行期會發生的異常,又叫做運行期異常,這些異常包括程序 bug,比如邏輯錯誤,API 的錯誤使用等等這些在編譯器會忽略的異常。
public class Unchecked_Demo { public static void main(String args[]){ int num[]={1,2,3,4}; System.out.println(num[5]); } } // output Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5 at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
Errors: 這些不是異常,而是一些程序員無法控制的問題。比如,棧溢出。
Exception Hierarchy
所有的 Exception 都是 java.lang.Exception 的子類。Exception 同時也是 Throwable 的子類。除了 Exception 之外,還有 Error 類也是 Throwable 的子類。
Exception 類有兩個子類:IOException 和 RuntimeException。
這裏有常見的 java 內置 Exception.
Exceptions Methods
下面列出了 Throwable 類的一些很重要的方法。
SN | Methods with Description |
---|---|
1 | public String getMessage():異常發生時返回的錯誤信息,這個信息在異常類構造器中初始化的。 |
2 | public Throwable getCause():返回異常的原因 |
3 | public String toString():返回類名+getMessage() |
4 | public void printStackTrace():打印 toString() 的結果,輸出到 System.err |
5 | public StackTraceElement [] getStackTrace():返回調用棧元素的集合 |
6 | public Throwable fillInStackTrace() |
Catching Exceptions
可以使用 try 和 catch 來捕獲異常。語法如下:
try {
//Protected code
}catch(ExceptionName e1) {
//Catch block
}
舉例:
// File Name : ExcepTest.java
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");
}
}
// output
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
Multiple catch Blocks
一個 try,後面可以接多個 catch:
try {
//Protected code
}catch(ExceptionType1 e1) {
//Catch block
}catch(ExceptionType2 e2) {
//Catch block
}catch(ExceptionType3 e3) {
//Catch block
}
舉例:
try
{
file = new FileInputStream(fileName);
x = (byte) file.read();
}catch(IOException i)
{
i.printStackTrace();
return -1;
}catch(FileNotFoundException f) //Not valid!
{
f.printStackTrace();
return -1;
}
Catching multiple type of exceptions
從 Java 7 開始,一個 catch 語句塊可以捕獲多個異常。比如:
catch (IOException|FileNotFoundException ex) {
logger.log(ex);
throw ex;
The throws/throw Keywords
如果方法本身不會處理一個 checked exception,那麼方法必須使用 throws 拋出這個異常。throws 關鍵字總是出現在方法簽名的末尾。
方法中也可以使用 throw 關鍵字拋出一個異常。
throws 和 throw 的區別:
throws 用來推遲 checked exception 的處理,throw 用來顯示的觸發一個異常。
下面這個例子,拋出了一個 RemoteException:
import java.io.*;
public class className
{
public void deposit(double amount) throws RemoteException
{
// Method implementation
throw new RemoteException();
}
//Remainder of class definition
}
方法也可以一次拋出多個異常。
import java.io.*;
public class className
{
public void withdraw(double amount) throws RemoteException,
InsufficientFundsException
{
// Method implementation
}
//Remainder of class definition
}
The finally block
finally 塊緊跟在 try…catch 後面。finally 塊中的語句總是會執行,即使有異常發生。
通常使用 finally 語句塊來做一些資源清理的工作。
語法如下:
try
{
//Protected code
}catch(ExceptionType1 e1)
{
//Catch block
}catch(ExceptionType2 e2)
{
//Catch block
}catch(ExceptionType3 e3)
{
//Catch block
}finally
{
//The finally block always executes.
}
舉例:
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");
}
}
}
// output
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed
注意:
- 無 try 不 catch
- finally 不必在
- 無 catch 或 finally 不 try
- try catch finally 之間無代碼
The try-with-resources
通常我們會使用一些資源(流,連接等等),使用完成後要關掉這些資源,如下所示,使用 FileReader,然後關閉它:
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class ReadData_Demo {
public static void main(String args[]){
FileReader fr=null;
try{
File file=new File("file.txt");
fr = new FileReader(file); char [] a = new char[50];
fr.read(a); // reads the content to the array
for(char c : a)
System.out.print(c); //prints the characters one by one
}catch(IOException e){
e.printStackTrace();
}
finally{
try{
fr.close();
}catch(IOException ex){
ex.printStackTrace();
}
}
}
}
代碼比較多,使用 try-with-resources 句法後(Java 7 引進),try 中打開的資源會自動釋放。
語法:
try(FileReader fr=new FileReader("file path")) {
//use the resource
}catch(){
//body of catch
}
}
舉例:
import java.io.FileReader;
import java.io.IOException;
public class Try_withDemo {
public static void main(String args[]){
try(FileReader fr=new FileReader("E://file.txt")){
char [] a = new char[50];
fr.read(a); // reads the contentto the array
for(char c : a)
System.out.print(c); //prints the characters one by one
}catch(IOException e){
e.printStackTrace();
}
}
}
下面這些要點要謹記:
- 使用 try-with-resources 打開的類,必須實現 AutoCloseable接口,運行期會自動調用close()。
- 在 try-with-resources 中可用聲明多個類
- try 中聲明的多個類會以相反的順序關閉
- 除了資源的聲明之外,其他與 try/catch 無異
- try 中聲明的資源在 try 開始之前被初始化。
- try 中聲明的資源隱式的被修飾爲 final 的
User-Define Exceptions
用戶可以創建自己的異常類。自定義的時候注意以下幾點:
- 所有的異常都是 Throwable 的子類
- 如果想寫一個 checked exception,需要繼承 Exception
- 如果想寫一個 runtime exception,需要繼承 RuntimeException
舉例:
// File Name InsufficientFundsException.java
import java.io.*;
public class InsufficientFundsException extends Exception
{
private double amount;
public InsufficientFundsException(double amount)
{
this.amount = amount;
}
public double getAmount()
{
return amount;
}
}
使用上述定義的異常:
// File Name CheckingAccount.java
import java.io.*;
public class CheckingAccount
{
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;
}
}
使用上述定義的類:
// File Name 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();
}
}
}
// output
Depositing $500...
Withdrawing $100...
Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
at CheckingAccount.withdraw(CheckingAccount.java:25)
at BankDemo.main(BankDemo.java:13)
Common Exceptions
Java 中,可以定義兩種 Exceptions 和 Errors:
- JVM Exceptions: 由 JVM 拋出的異常,例如:NullPointerException,ArrayIndexOutOfBoundsException,ClassCastException
- Programmatic exceptions: 應用或者是API設計者拋出的異常,例如:IllegalArgumentException,IllegalStateException