異常處理使得程序可以處理非預(yù)期的情景,并且能夠繼續(xù)正常的操作
在java中,運(yùn)行時錯誤會作為異常拋出。異常就是一種對象,表示阻止正常進(jìn)行程序執(zhí)行的錯誤或者情況。
異常是從方法拋出的。方法的調(diào)用者可以捕獲以及處理該異常。
我們可以先從簡單的算術(shù)異常錯誤來了解拋出異常的作用
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));
}
}
當(dāng)?shù)诙€數(shù)字輸入的是數(shù)字0的時候,就會產(chǎn)生一個運(yùn)行時錯誤,因為不能用一個整數(shù)除以一個0(但是在java中浮點型除以0將不會拋出異常)
要想解決這個錯誤,一個簡單的方法就是添加一個if語句來測試第二個數(shù)字。
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);
}
}
但是這種方法是強(qiáng)行結(jié)束掉整個程序,而且不應(yīng)該是被調(diào)用的方法來結(jié)束整個程序,而是應(yīng)該由調(diào)用者來決定是否終止程序。所以要用到異常處理。
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,方法就會通過執(zhí)行下面的語句來拋出異常
throw new ArithmeticException("Divisor cannot be zero");
在這種情況下,拋出的值為new ArithmeticException("Divisor cannot be zero"),就成為一個異常(exception)。throw語句的執(zhí)行稱為拋出一個異常(throwing an exception)。異常就是一個從異常類創(chuàng)建的對象。在這種情況下,異常類就是java.lang.ArithmeticException.構(gòu)造方法ArithmeticException(str)被調(diào)用來構(gòu)建一個異常,其中str就是用來描述異常的信息。
當(dāng)異常被拋出的時候后,正常的執(zhí)行流程會被中斷。異常會從一個地方傳遞到另一個地方。調(diào)用方法的語句包含在一個try塊和一個catch塊中。try塊包含了正常情況之下執(zhí)行的代碼。異常會被catch塊捕獲。catch塊中的語句用來處理異常,然后執(zhí)行catch塊之后的語句(相當(dāng)于是throw語句調(diào)用了catch塊,但是catch塊執(zhí)行完畢之后不會返回到throw語句之后,而是直接運(yùn)行catch塊之后的語句)。
catch塊的頭部:
catch (ArithmeticException ex)
標(biāo)識符ex的作用很像是方法中的參數(shù)。所以,這個參數(shù)稱為catch塊的參數(shù)。ex之前的類型(例如,ArithmeticException)指定了catch塊可以捕獲的異常類型。一旦捕獲該異常,就能從catch塊體中的參數(shù)方訪問這個拋出的值。
總之,一個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語句直接拋出,或者調(diào)用一個可能會拋出異常的方法二拋出。
什么是拋出異常的優(yōu)點?
異常處理可以使方法拋出一個異常給它的調(diào)用者,并由調(diào)用者處理該異常。如果沒有這個能力,那么被調(diào)用的方法就必須自己處理異常或者終止改程序。被調(diào)用的方法通常不知道在出錯的情況下該做一些什么,這是庫方法的一般情況。庫方法可以檢測出錯誤,但是只有調(diào)用者才知道出現(xiàn)錯誤的時候需要做什么。異常處理最根本的優(yōu)勢就是將檢測錯誤(由被調(diào)用的方法完成)從處理錯誤(由調(diào)用方法完成)中分離出來。
異常是對象,而對象都采用類來定義。異常的根類是java.lang.Throwable。
Throwable類是所有異常類的根。所有的java異常類都直接或者間接地繼承自Throwable類。我們可以通過繼承Exception或者Exception的子類來創(chuàng)建自己的異常類。
異常類可以分為三種類型:系統(tǒng)錯誤、 異常、 運(yùn)行時異常。
- 系統(tǒng)錯誤(system error):是由java虛擬機(jī)拋出的,用error類表示。Error類描述的是系統(tǒng)內(nèi)部錯誤。
- 異常(Exception):是用Exception類表示的,他描述的是由程序和外部環(huán)境所引起的錯誤,這些錯誤可以能被程序捕獲和處理。
- 運(yùn)行時錯誤(runtime exception)是用RuntimeException類表示的,它描述的是程序設(shè)計錯誤。
RuntimeException、Error以及它們的子類都稱為免檢異常(unchecked exception),而其他所有異常都稱為必檢異常(checked exception)因為免檢異常可能出現(xiàn)在程序的任意一個地方,為了避免過多地使用try-catch塊,所以免檢異常不作強(qiáng)制要求。
Java的異常處理模基于三種操作:聲明一個異常、拋出一個異常、捕獲一個異常。
聲明異常:在Java中,當(dāng)前執(zhí)行的語句必屬于某個方法。Java解釋器調(diào)用main方法開始執(zhí)行一個程序。每個方法都必須聲明它可能拋出的必檢異常類型。
public void myMethod() throws IOException
拋出異常:檢測到錯誤的程序可以創(chuàng)建一個合適的異常類型的實例并拋出它,這就成為拋出一個異常。
IllegalArgumentException ex=new IllegalArgumentException("Wrong Argument");
throw ex;
或者用下面這種方法
throw new IllegalArgumentException("Wrong Argument");
每個異常類至少有兩個構(gòu)造方法:一個無參構(gòu)造方法和一個帶可描述這個異常的String參數(shù)的構(gòu)造函數(shù)。這個String參數(shù)稱為異常消息,可以用getMessage();獲取。
聲明異常的關(guān)鍵字是throws,拋出異常的關(guān)鍵字是throw。
捕獲異常:當(dāng)拋出一個異常時,可以在try-catch塊中捕獲和處理它。
try{
statements;
}
catch (Exception ex){
handler for exception;
}
如果在執(zhí)行try塊中代碼的過程中沒有出現(xiàn)異常,則會跳過catch語句。如果try塊中的某條語句拋出一個異常,java會跳過try塊中剩下的語句。
從一個通用的父類可以派生出各種異常類。如果一個catch塊可以捕獲一個父類的異常對象,它就能捕獲那個父類的所有子類的異常對象。而且如果父類的catch塊出現(xiàn)在子類的catch塊漆面,會導(dǎo)致編譯錯誤。
Java要求程序員必須處理必檢異常。如果方法聲明了一個必檢異常,就必須在try-catch塊中調(diào)用它,或在調(diào)用方法中聲明要拋出異常。
要是需要一個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();三種方法來顯示棧跟蹤、異常信息、異常對象和信息。
運(yùn)行結(jié)果:
finally子句
有時候,不論異常是否會出現(xiàn)或者被捕獲,都希望執(zhí)行某一些代碼,這個時候可以使用finally子句來達(dá)到這個目的。
try{
statements;
}
catch(TheException ex){
handling ex;
}
finally{
finalStatements;
}
注意:使用finally子句時可以省略掉catch塊。