java編程中遇到的(de)異常(chang)以(yi)及異常(chang)的(de)一(yi)些處理
時間:2018-06-22 來(lai)源:未知
n 異常(chang)的概念
程(cheng)序(xu)運行(xing)時(shi),發生的(de)不被期望的(de)事(shi)件,它阻止了程(cheng)序(xu)按照(zhao)程(cheng)序(xu)員的(de)預(yu)期正常(chang)執行(xing),這就是異(yi)常(chang)。異(yi)常(chang)發生時(shi),是任程(cheng)序(xu)自(zi)(zi)生自(zi)(zi)滅,立刻退(tui)出終止,還是輸出錯(cuo)誤(wu)給用(yong)戶?
比(bi)如除(chu)法運算、讀寫文(wen)件操作,都可能(neng)發生(sheng)異常。(當除(chu)數為0時;文(wen)件路徑(jing)不存在時)。該如何處理?
C語言的(de)風格:用函數返回值作為執行(xing)狀態。比如返回一個(ge)為0的(de)值表示文(wen)件不存在這個(ge)狀態。缺點是(shi)代(dai)碼比較散亂。
而Java語言提供了一種優秀的解決辦法(fa):異(yi)常處(chu)理(li)機制。java將處(chu)理(li)異(yi)常的代(dai)碼(ma)放到(dao)一個(ge)統一的 try-catch-finally結構中去處(chu)理(li),且代(dai)碼(ma)易讀。請看下面(mian)的例(li)子
Ø 示例一
數學(xue)運算(suan)之除0異(yi)常
import java.util.Scanner;
public class AllDemo {
public static void main(String[] args) {
System.out.println("----歡迎使用命(ming)令行除(chu)法計算器----");
Scanner scan = new Scanner(System.in);
int num1 = scan.nextInt();
int num2 = scan.nextInt();
int result = a(num1,num2);
System.out.println("result:" + result);
scan.close();
}
private static int a(int num1, int num2) {
return b(num1,num2);
}
private static int b(int num1, int num2) {
return devide(num1,num2);
}
public static int devide(int num1, int num2) {
return num1 / num2;
}
}
執(zhi)行時輸入7和(he)0,會看到控制臺結果
----歡迎使用(yong)命(ming)令行除法計(ji)算器----
7
0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at AllDemo.devide(AllDemo.java:24)
at AllDemo.b(AllDemo.java:20)
at AllDemo.a(AllDemo.java:16)
at AllDemo.main(AllDemo.java:9)
分析:當devide函(han)數發(fa)生(sheng)除0異常(chang)時,devide函(han)數將拋(pao)出ArithmeticException異常(chang),于(yu)是調(diao)(diao)用它的(de)(de)函(han)數b也發(fa)生(sheng)了異常(chang),于(yu)是調(diao)(diao)用b的(de)(de)函(han)數a也發(fa)生(sheng)異常(chang),于(yu)是調(diao)(diao)用a的(de)(de)函(han)數main也發(fa)生(sheng)了異常(chang),這樣一直向調(diao)(diao)用棧(zhan)的(de)(de)棧(zhan)底回溯,這叫做異常(chang)的(de)(de)冒泡。這個例(li)子沒有使用異常(chang)處理機制,異常(chang)最終由main函(han)數拋(pao)給java虛擬(ni)機,導致程(cheng)序終止。
Ø 示例二
讀寫文件異常
import java.io.FileInputStream;
import java.io.IOException;
class ReadFile {
public static void testException() throws IOException
{
//FileInputStream的(de)構造函數會拋出FileNotFoundException
FileInputStream fileIn = new FileInputStream("E:\\a.txt");
int word;
while((word = fileIn.read())!=-1) //read方法會拋出IOException
{
System.out.print((char)word);
}
fileIn.close(); //close方法會拋出IOException
}
public static void main(String arg[]) throws IOException{
ReadFile.testException();
}
}
說明:
1、如果E盤(pan)下沒有文(wen)件a.txt,發生FileNotFoundException。
2、如(ru)果(guo)a.txt存(cun)在,但(dan)是被其它進(jin)程鎖住(zhu),有可(ke)能發生(sheng)IOException
3、鑒于1、2,此(ci)處為了編譯(yi)正(zheng)確,所以只(zhi)得在(zai)testException()加上“throws IOException”,調用它(ta)的(de)main()也得加上“throws IOException”。(注:FileNotFoundException是的(de)IOException的(de)子類)
n java異常處(chu)理的try-catch-finally結構
我們可以(yi)使(shi)用(yong)異常處理(li)結構改進(jin)上面讀文件的(de)例子
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
class ReadFile {
public static void testException() {
FileInputStream fileIn = null;
try {
// FileInputStream的構造(zao)函數會拋(pao)出FileNotFoundException
fileIn = new FileInputStream("E:\\a.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
int word;
try {
while ((word = fileIn.read()) != -1) // read方法可拋出IOException
{
System.out.print((char) word);
}
fileIn.close(); // close方法可拋出IOException
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("finally塊無(wu)論(lun)如何都要執行");
}
}
public static void main(String arg[]) {
ReadFile.testException();
}
}
說明
1、如(ru)果E盤(pan)下沒有文件a.txt,發生FileNotFoundException。進入catch結構(gou),打印出一(yi)些信(xin)息。
2、程序仍然往下能繼(ji)續運行,在fileIn.read()時發生(sheng)異常NullPointerException,這(zhe)是(shi)因為第1步進了catch塊,這(zhe)樣fileIn變(bian)量仍是(shi)最(zui)初的null值,一旦(dan)調函數便發生(sheng)NullPointerException。
3、問:我們針(zhen)對NullPointerException為(wei)什么(me)不需要寫(xie)try-catch-finally結(jie)構?具體原(yuan)因(yin)請(qing)閱(yue)讀后面的RuntimeException內(nei)容
4、這個(ge)例(li)子中故意為(wei)了演示出NullPointerException,而(er)把try-catch塊寫成(cheng)了兩個(ge),實際上可以合(he)并。
n try-catch-finally結(jie)構說(shuo)明
1、一(yi)個(ge)try可以對應多(duo)個(ge)catch塊。
2、如果發生異(yi)常,異(yi)常被(bei)(bei)拋給第一(yi)個(ge)catch 塊(kuai),如果異(yi)常的(de)類型與(yu) catch匹配,它在(zai)這里就會被(bei)(bei)捕獲。如果不(bu)匹配,它會被(bei)(bei)傳遞給第二個(ge) catch 塊(kuai)。如此,直到異(yi)常被(bei)(bei)捕獲或者通過所有的(de) catch 塊(kuai)。
3、finally塊始(shi)終會被(bei)執行。
4、如果try或catch塊(kuai)中存在return語句,那(nei)么catch、finally塊(kuai)中的語句也會被執行完了后,才(cai)真正return。除非(fei)遇到下面的幾種情況(1)System.exit(n)可導致立即(ji)終(zhong)止(2)finally塊(kuai)中發(fa)生異常(3)程序所在線程死(si)亡(4)關閉CPU。
練習
class Snippet {
public static String t() {
String s = "1";
try {
s = "2";
return s;
//throw new Exception("some");
} catch (Exception e) {
s = "3";
System.out.println("in catch block");
return s;
}finally {
s = "4";
System.out.println("in finally block");
return s;
}
}
public static void main(String[] args) {
String s= t();
System.out.println(s);
}
}
//無論如何(he),都要進到(dao)finally塊執行,如果finally{}里有return 那么返(fan)回的(de)肯定是finally里的(de)
Java異常類圖

從上(shang)面(mian)的(de)示(shi)例代碼可以看出(chu),java把異(yi)常也看成是(shi)對象。進而設計了(le)下面(mian)所示(shi)的(de)異(yi)常方面(mian)的(de)類(lei)結構體系。最上(shang)層類(lei)叫做Throwable。可以把這(zhe)些分成兩(liang)大種類(lei)
Ø 非檢查異常(chang)(chang)(unckecked exception):Error 和 RuntimeException 以(yi)及他(ta)們的(de)子類(lei)。javac在編(bian)譯時,不會(hui)提示和發(fa)(fa)(fa)現這樣的(de)異常(chang)(chang),不要(yao)(yao)求程序員必須處(chu)理這些(xie)異常(chang)(chang)。在運行階(jie)段,倘(tang)若發(fa)(fa)(fa)生(sheng)Error則(ze)虛擬機(ji)幾乎崩潰,倘(tang)若發(fa)(fa)(fa)生(sheng)RuntimeException若程序員沒處(chu)理它則(ze)一直回(hui)溯向上(shang)拋給java虛擬機(ji)處(chu)理。當然,如果程序員愿意的(de)話,也可以(yi)編(bian)寫代碼(ma)處(chu)理(使用try…catch…finally)這樣的(de)異常(chang)(chang)(但是(shi)(shi)通常(chang)(chang)情況下不會(hui)這樣做。需(xu)要(yao)(yao)這樣做的(de)情況是(shi)(shi)比如搞數(shu)學運算(suan)的(de)這個專業領(ling)域(yu)要(yao)(yao)處(chu)理ArithmeticException)。對于這些(xie)異常(chang)(chang),我們應該修正代碼(ma),而不是(shi)(shi)去通過異常(chang)(chang)處(chu)理器(qi)處(chu)理。這種(zhong)異常(chang)(chang)發(fa)(fa)(fa)生(sheng)的(de)原因多半(ban)是(shi)(shi)代碼(ma)寫的(de)有問題(ti)。如除0錯誤ArithmeticException,錯誤的(de)強制(zhi)類(lei)型(xing)轉換錯誤ClassCastException,數(shu)組索引越(yue)界ArrayIndexOutOfBoundsException,使用了空對象NullPointerException等等。
Ø 檢查異(yi)常(checked exception):除了Error 和 RuntimeException的(de)(de)其它異(yi)常。javac強制(zhi)要求程(cheng)(cheng)序員(yuan)為這樣(yang)的(de)(de)異(yi)常做預(yu)備(bei)處理工作(使用(yong)try…catch…finally或者throws)。在方法中要么用(yong)try-catch語(yu)句捕(bu)獲它并處理,要么用(yong)throws子句聲明拋出它,否則編(bian)譯不會通(tong)過。這樣(yang)的(de)(de)異(yi)常一般是由程(cheng)(cheng)序的(de)(de)運行(xing)環境(jing)導致的(de)(de)。因為程(cheng)(cheng)序可能被運行(xing)在各種未知的(de)(de)環境(jing)下(xia),而程(cheng)(cheng)序員(yuan)無法干預(yu)用(yong)戶(hu)如(ru)何使用(yong)他編(bian)寫的(de)(de)程(cheng)(cheng)序,于是程(cheng)(cheng)序員(yuan)就應該為這樣(yang)的(de)(de)異(yi)常時刻準備(bei)著。如(ru)SQLException , IOException,ClassNotFoundException 等。
n 常見異(yi)常類型說明
異常類型說明
Exception 異(yi)常(chang)層(ceng)次結構的父類(lei)
ArithmeticException算術錯誤(wu)情形,如以零作(zuo)除數(shu)
ArrayIndexOutOfBoundsException數組(zu)下(xia)標越界
NullPointerException嘗試(shi)訪問 null 對象成(cheng)員
ClassNotFoundException不能加(jia)載所需的類
IllegalArgumentException方(fang)法(fa)接收到非法(fa)參數
ClassCastException對象強(qiang)制類(lei)型轉換出錯
NumberFormatException數(shu)字格式轉換異常,如把"abc"轉換成數(shu)字
n throws關鍵字(zi)
聲明本方法不(bu)處理異常,讓調(diao)用(yong)者處理。
在(zai)函(han)數(shu)簽名中使用(yong)throws 聲(sheng)明交給函(han)數(shu)調(diao)用(yong)者caller去解決。
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
try {
divide();//調用(yong)的(de)此(ci)方法拋出了異常(chang)
}
catch (Exception e) {
System.out.println("錯誤:被除(chu)(chu)數(shu)和除(chu)(chu)數(shu)必(bi)須(xu)是整數(shu),且除(chu)(chu)數(shu)不能為零。");
e.printStackTrace();
e.getMessage();//暫時(shi)打印(yin)不(bu)出任何(he)內容
}
finally{
System.out.println("感謝使用(yong)本(ben)程序(xu)");
}
}
public static void divide() throws Exception{//聲明(ming)異常
Scanner scanner = new Scanner(System.in);
System.out.println("請(qing)輸入被除數");
int num1 = scanner.nextInt();//有可能異常InputMismatchException
System.out.println("請(qing)輸入除(chu)數(shu)");
int num2 = scanner.nextInt();//有可能異常InputMismatchException
System.out.println(num1 / num2);//有可能異常除數等于(yu)0:ArithmeticException
}
}
n throw關鍵字(zi)
主動拋出異常
如果對于具體(ti)的一些處(chu)理邏(luo)輯,程序員也可以主動(dong)的拋出異(yi)常讓 外層處(chu)理。(此異(yi)常可能是程序員自己定義的)。
class Person {
private String name;
private String sex = "男(nan)";
public void setSex(String sex) throws Exception{
if("男".equals(sex) || "女".equals(sex)){
this.sex = sex;
}
else{
//主動(dong)拋(pao)出異(yi)常(chang)。也(ye)可(ke)做成(cheng)自定義異(yi)常(chang)并拋(pao)出
throw new Exception("性別輸入錯(cuo)誤,必須是男或女");
}
}
public void print() {
System.out.println(this.name + this.sex);
}
}
class Test {
public static void main(String[] args) {
Person p = new Person();
try {
p.setSex("male");
p.print();
} catch (Exception e) {
e.printStackTrace();
}
}
}
n 異(yi)常(chang)(chang)類(lei)的(de)(de)兩個打印(yin)異(yi)常(chang)(chang)信息的(de)(de)好辦法(fa)
一般,我們需要打印出(chu)異常(chang)的相關信息(xi)。在(zai)Exception類中,定義了下面兩個(ge)方法,
e.printStackTrace();//打印調用堆棧信息(xi) 并把e.getMessage()的信息(xi)也打出(chu)來(lai)了
e.getMessage();//打印異常(chang)的相關(guan)信(xin)息
與IOException類(lei)相似,我(wo)們自己(ji)定(ding)義的(de)(de)異常(chang)類(lei)往往也(ye)是Exception的(de)(de)子類(lei),我(wo)們可(ke)以(1)覆蓋(gai)e.getMessage()方法; 也(ye)可(ke)以(2)構造的(de)(de)時候傳入具體的(de)(de)字符串信息,因為e.getMessage()就是獲取(qu)這(zhe)個信息
示例
public class Test {
public static void main(String[] args) {
try {
Exception e = new Exception("哈哈,我是異常");
throw e;
} catch (Exception e) {
e.printStackTrace();
System.out.println("message="+ e.getMessage());
}
}
}
n 建議
(1)多重catch塊(kuai):Catch塊(kuai)的排列順序必須是從子類到父類。最后一個一般是Exception。
(2)不要(yao)在fianlly中使用(yong)return。
不(bu)要(yao)在finally中(zhong)拋出異常。
減輕finally的任務,不要(yao)在finally中做一些其(qi)它的事情,finally塊僅僅用來釋放資源是最合適(shi)的。
將盡量將所有的(de)return寫(xie)在函(han)數的(de)最后(hou)面,而不是(shi)try … catch … finally中。
n 練習
import java.util.InputMismatchException;
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num1=1 , num2=1;
try {
System.out.println("請輸入被除數(shu)");
num1 = scanner.nextInt();//有可能(neng)異常InputMismatchException
System.out.println("請輸入除(chu)數");
num2 = scanner.nextInt();//有可(ke)能異常InputMismatchException
System.out.println(String.format("%d / %d = %d", num1, num2, num1 / num2));//有可(ke)能異(yi)常除數等于0:ArithmeticException
}
catch(InputMismatchException e)
{
System.err.println("被除數和除數必須是整數");
return;
}
catch(ArithmeticException e)
{
System.err.println("除數不能為零(ling)");
//return;
System.exit(0);//這個是立(li)即終止
}
catch (Exception e) {
System.out.println("錯誤:被除(chu)(chu)數(shu)和除(chu)(chu)數(shu)必須是整數(shu),且除(chu)(chu)數(shu)不能為零。");
e.printStackTrace();
e.getMessage();//暫時打印(yin)不出任何內容
}
finally{
System.out.println("感謝使用本程序");
}
}
}