異常處理使得程序可以處理非預期的情景,并且能夠繼續正常的操作
在java中,運行時錯誤會作為異常拋出。異常就是一種對象,表示阻止正常進行程序執行的錯誤或者情況。
異常是從方法拋出的。方法的調用者可以捕獲以及處理該異常。
我們可以先從簡單的算術異常錯誤來了解拋出異常的作用
import java.util.Scanner;
public class Quotient {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("Enter two integers: ");
int number1=input.nextInt();
int number2=input.nextInt();
System.out.println(number1+" / "+number2+" is "+(number1/number2));
}
}
當第二個數字輸入的是數字0的時候,就會產生一個運行時錯誤,因為不能用一個整數除以一個0(但是在java中浮點型除以0將不會拋出異常)
要想解決這個錯誤,一個簡單的方法就是添加一個if語句來測試第二個數字。
import java.util.Scanner;
public class Quotient {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("Enter two integers: ");
int number1=input.nextInt();
int number2=input.nextInt();
if(number2!=0)
System.out.println(number1+" / "+number2+" is "+(number1/number2));
else
System.out.println("Divisor cannot be zero");
}
}
為了介紹異常處理,重新再寫一個方法來計算商。
import java.util.Scanner;
public class QuotienWithMethod {
public static int quotient(int number1,int number2){
if(number2==0){
System.out.println("Divisor cannot be zero");
System.exit(1);//表示程序非正常退出
}
return number1/number2;
}
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int number1=input.nextInt();
int number2=input.nextInt();
int result=quotient(number1, number2);
System.out.println(number1+" / "+number2+" is "+result);
}
}
但是這種方法是強行結束掉整個程序,而且不應該是被調用的方法來結束整個程序,而是應該由調用者來決定是否終止程序。所以要用到異常處理。
import java.util.Scanner;
public class QuotientWithException {
public static int quotient(int number1,int number2){
if(number2==0)
throw new ArithmeticException("Divisor cannot be zero");
return number1/number2;
}
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.println("Enter two integers: ");
int number1=input.nextInt();
int number2=input.nextInt();
try{
int result=quotient(number1,number2);
System.out.println(number1+" / "+number2+" is "+result);
}
catch(ArithmeticException ex){
System.out.println("Exception: an integer"+"cannot be divided by zero");
}
System.out.println("Execution continues...");
}
}
如果number2為0,方法就會通過執行下面的語句來拋出異常
throw new ArithmeticException("Divisor cannot be zero");
在這種情況下,拋出的值為new ArithmeticException("Divisor cannot be zero"),就成為一個異常(exception)。throw語句的執行稱為拋出一個異常(throwing an exception)。異常就是一個從異常類創建的對象。在這種情況下,異常類就是java.lang.ArithmeticException.構造方法ArithmeticException(str)被調用來構建一個異常,其中str就是用來描述異常的信息。
當異常被拋出的時候后,正常的執行流程會被中斷。異常會從一個地方傳遞到另一個地方。調用方法的語句包含在一個try塊和一個catch塊中。try塊包含了正常情況之下執行的代碼。異常會被catch塊捕獲。catch塊中的語句用來處理異常,然后執行catch塊之后的語句(相當于是throw語句調用了catch塊,但是catch塊執行完畢之后不會返回到throw語句之后,而是直接運行catch塊之后的語句)。
catch塊的頭部:
catch (ArithmeticException ex)
標識符ex的作用很像是方法中的參數。所以,這個參數稱為catch塊的參數。ex之前的類型(例如,ArithmeticException)指定了catch塊可以捕獲的異常類型。一旦捕獲該異常,就能從catch塊體中的參數方訪問這個拋出的值。
總之,一個try-throw-catch塊的模板可能會如下所示:
try{
Code to run;
A statement or a method that may throw an exception;
More code to run;
}
catch(type ex){
Code to process the exception;
}
一個異常可能是通過try塊中的throw語句直接拋出,或者調用一個可能會拋出異常的方法二拋出。
什么是拋出異常的優點?
異常處理可以使方法拋出一個異常給它的調用者,并由調用者處理該異常。如果沒有這個能力,那么被調用的方法就必須自己處理異常或者終止改程序。被調用的方法通常不知道在出錯的情況下該做一些什么,這是庫方法的一般情況。庫方法可以檢測出錯誤,但是只有調用者才知道出現錯誤的時候需要做什么。異常處理最根本的優勢就是將檢測錯誤(由被調用的方法完成)從處理錯誤(由調用方法完成)中分離出來。
異常是對象,而對象都采用類來定義。異常的根類是java.lang.Throwable。
Throwable類是所有異常類的根。所有的java異常類都直接或者間接地繼承自Throwable類。我們可以通過繼承Exception或者Exception的子類來創建自己的異常類。
異常類可以分為三種類型:系統錯誤、 異常、 運行時異常。
- 系統錯誤(system error):是由java虛擬機拋出的,用error類表示。Error類描述的是系統內部錯誤。
- 異常(Exception):是用Exception類表示的,他描述的是由程序和外部環境所引起的錯誤,這些錯誤可以能被程序捕獲和處理。
- 運行時錯誤(runtime exception)是用RuntimeException類表示的,它描述的是程序設計錯誤。
RuntimeException、Error以及它們的子類都稱為免檢異常(unchecked exception),而其他所有異常都稱為必檢異常(checked exception)因為免檢異常可能出現在程序的任意一個地方,為了避免過多地使用try-catch塊,所以免檢異常不作強制要求。
Java的異常處理模基于三種操作:聲明一個異常、拋出一個異常、捕獲一個異常。
聲明異常:在Java中,當前執行的語句必屬于某個方法。Java解釋器調用main方法開始執行一個程序。每個方法都必須聲明它可能拋出的必檢異常類型。
public void myMethod() throws IOException
拋出異常:檢測到錯誤的程序可以創建一個合適的異常類型的實例并拋出它,這就成為拋出一個異常。
IllegalArgumentException ex=new IllegalArgumentException("Wrong Argument");
throw ex;
或者用下面這種方法
throw new IllegalArgumentException("Wrong Argument");
每個異常類至少有兩個構造方法:一個無參構造方法和一個帶可描述這個異常的String參數的構造函數。這個String參數稱為異常消息,可以用getMessage();獲取。
聲明異常的關鍵字是throws,拋出異常的關鍵字是throw。
捕獲異常:當拋出一個異常時,可以在try-catch塊中捕獲和處理它。
try{
statements;
}
catch (Exception ex){
handler for exception;
}
如果在執行try塊中代碼的過程中沒有出現異常,則會跳過catch語句。如果try塊中的某條語句拋出一個異常,java會跳過try塊中剩下的語句。
從一個通用的父類可以派生出各種異常類。如果一個catch塊可以捕獲一個父類的異常對象,它就能捕獲那個父類的所有子類的異常對象。而且如果父類的catch塊出現在子類的catch塊漆面,會導致編譯錯誤。
Java要求程序員必須處理必檢異常。如果方法聲明了一個必檢異常,就必須在try-catch塊中調用它,或在調用方法中聲明要拋出異常。
要是需要一個catch塊捕獲多個異常可以使用"|"來將每個異常類型隔開。
從異常中獲取信息:
public class TestException {
public static void main(String[] args) {
try{
System.out.println(sum(new int[]{1,2,3,4,5}));
}
catch (Exception ex){
ex.printStackTrace();
System.out.println("\n"+ex.getMessage());
System.out.println("\n"+ex.toString());
System.out.println("\nTrace Info Obtained from getStackTrace");
StackTraceElement[] traceElements=ex.getStackTrace();
for(int i=0;i<traceElements.length;i++){
System.out.println("method"+traceElements[i].getMethodName());
System.out.println("("+traceElements[i].getClassName()+":");
System.out.println(traceElements[i].getLineNumber()+")");
}
}
}
private static int sum(int [] list){
int result=0;
for(int i=0;i<=list.length;i++)
result+=list[i];
return result;
}
}
在代碼中使用了printStackTrace();、getMessage();、toString();三種方法來顯示棧跟蹤、異常信息、異常對象和信息。
運行結果:
finally子句
有時候,不論異常是否會出現或者被捕獲,都希望執行某一些代碼,這個時候可以使用finally子句來達到這個目的。
try{
statements;
}
catch(TheException ex){
handling ex;
}
finally{
finalStatements;
}
注意:使用finally子句時可以省略掉catch塊。