當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > java編程中遇到的異常以及異常的一些處理
n 異常的概念
程序運(yùn)行時,發(fā)生的不被期望的事件,它阻止了程序按照程序員的預(yù)期正常執(zhí)行,這就是異常。異常發(fā)生時,是任程序自生自滅,立刻退出終止,還是輸出錯誤給用戶?
比如除法運(yùn)算、讀寫文件操作,都可能發(fā)生異常。(當(dāng)除數(shù)為0時;文件路徑不存在時)。該如何處理?
C語言的風(fēng)格:用函數(shù)返回值作為執(zhí)行狀態(tài)。比如返回一個為0的值表示文件不存在這個狀態(tài)。缺點是代碼比較散亂。
而Java語言提供了一種優(yōu)秀的解決辦法:異常處理機(jī)制。java將處理異常的代碼放到一個統(tǒng)一的 try-catch-finally結(jié)構(gòu)中去處理,且代碼易讀。請看下面的例子
Ø 示例一
數(shù)學(xué)運(yùn)算之除0異常
import java.util.Scanner;
public class AllDemo {
public static void main(String[] args) {
System.out.println("----歡迎使用命令行除法計算器----");
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;
}
}
執(zhí)行時輸入7和0,會看到控制臺結(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)
分析:當(dāng)devide函數(shù)發(fā)生除0異常時,devide函數(shù)將拋出ArithmeticException異常,于是調(diào)用它的函數(shù)b也發(fā)生了異常,于是調(diào)用b的函數(shù)a也發(fā)生異常,于是調(diào)用a的函數(shù)main也發(fā)生了異常,這樣一直向調(diào)用棧的棧底回溯,這叫做異常的冒泡。這個例子沒有使用異常處理機(jī)制,異常最終由main函數(shù)拋給java虛擬機(jī),導(dǎo)致程序終止。
Ø 示例二
讀寫文件異常
import java.io.FileInputStream;
import java.io.IOException;
class ReadFile {
public static void testException() throws IOException
{
//FileInputStream的構(gòu)造函數(shù)會拋出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盤下沒有文件a.txt,發(fā)生FileNotFoundException。
2、如果a.txt存在,但是被其它進(jìn)程鎖住,有可能發(fā)生IOException
3、鑒于1、2,此處為了編譯正確,所以只得在testException()加上“throws IOException”,調(diào)用它的main()也得加上“throws IOException”。(注:FileNotFoundException是的IOException的子類)
n java異常處理的try-catch-finally結(jié)構(gòu)
我們可以使用異常處理結(jié)構(gòu)改進(jìn)上面讀文件的例子
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
class ReadFile {
public static void testException() {
FileInputStream fileIn = null;
try {
// FileInputStream的構(gòu)造函數(shù)會拋出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塊無論如何都要執(zhí)行");
}
}
public static void main(String arg[]) {
ReadFile.testException();
}
}
說明
1、如果E盤下沒有文件a.txt,發(fā)生FileNotFoundException。進(jìn)入catch結(jié)構(gòu),打印出一些信息。
2、程序仍然往下能繼續(xù)運(yùn)行,在fileIn.read()時發(fā)生異常NullPointerException,這是因為第1步進(jìn)了catch塊,這樣fileIn變量仍是最初的null值,一旦調(diào)函數(shù)便發(fā)生NullPointerException。
3、問:我們針對NullPointerException為什么不需要寫try-catch-finally結(jié)構(gòu)?具體原因請閱讀后面的RuntimeException內(nèi)容
4、這個例子中故意為了演示出NullPointerException,而把try-catch塊寫成了兩個,實際上可以合并。
n try-catch-finally結(jié)構(gòu)說明
1、一個try可以對應(yīng)多個catch塊。
2、如果發(fā)生異常,異常被拋給第一個catch 塊,如果異常的類型與 catch匹配,它在這里就會被捕獲。如果不匹配,它會被傳遞給第二個 catch 塊。如此,直到異常被捕獲或者通過所有的 catch 塊。
3、finally塊始終會被執(zhí)行。
4、如果try或catch塊中存在return語句,那么catch、finally塊中的語句也會被執(zhí)行完了后,才真正return。除非遇到下面的幾種情況(1)System.exit(n)可導(dǎo)致立即終止(2)finally塊中發(fā)生異常(3)程序所在線程死亡(4)關(guān)閉CPU。
練習(xí)
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);
}
}
//無論如何,都要進(jìn)到finally塊執(zhí)行,如果finally{}里有return 那么返回的肯定是finally里的
Java異常類圖
從上面的示例代碼可以看出,java把異常也看成是對象。進(jìn)而設(shè)計了下面所示的異常方面的類結(jié)構(gòu)體系。最上層類叫做Throwable?梢园堰@些分成兩大種類
Ø 非檢查異常(unckecked exception):Error 和 RuntimeException 以及他們的子類。javac在編譯時,不會提示和發(fā)現(xiàn)這樣的異常,不要求程序員必須處理這些異常。在運(yùn)行階段,倘若發(fā)生Error則虛擬機(jī)幾乎崩潰,倘若發(fā)生RuntimeException若程序員沒處理它則一直回溯向上拋給java虛擬機(jī)處理。當(dāng)然,如果程序員愿意的話,也可以編寫代碼處理(使用try…catch…finally)這樣的異常(但是通常情況下不會這樣做。需要這樣做的情況是比如搞數(shù)學(xué)運(yùn)算的這個專業(yè)領(lǐng)域要處理ArithmeticException)。對于這些異常,我們應(yīng)該修正代碼,而不是去通過異常處理器處理。這種異常發(fā)生的原因多半是代碼寫的有問題。如除0錯誤ArithmeticException,錯誤的強(qiáng)制類型轉(zhuǎn)換錯誤ClassCastException,數(shù)組索引越界ArrayIndexOutOfBoundsException,使用了空對象NullPointerException等等。
Ø 檢查異常(checked exception):除了Error 和 RuntimeException的其它異常。javac強(qiáng)制要求程序員為這樣的異常做預(yù)備處理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch語句捕獲它并處理,要么用throws子句聲明拋出它,否則編譯不會通過。這樣的異常一般是由程序的運(yùn)行環(huán)境導(dǎo)致的。因為程序可能被運(yùn)行在各種未知的環(huán)境下,而程序員無法干預(yù)用戶如何使用他編寫的程序,于是程序員就應(yīng)該為這樣的異常時刻準(zhǔn)備著。如SQLException , IOException,ClassNotFoundException 等。
n 常見異常類型說明
異常類型說明
Exception 異常層次結(jié)構(gòu)的父類
ArithmeticException算術(shù)錯誤情形,如以零作除數(shù)
ArrayIndexOutOfBoundsException數(shù)組下標(biāo)越界
NullPointerException嘗試訪問 null 對象成員
ClassNotFoundException不能加載所需的類
IllegalArgumentException方法接收到非法參數(shù)
ClassCastException對象強(qiáng)制類型轉(zhuǎn)換出錯
NumberFormatException數(shù)字格式轉(zhuǎn)換異常,如把"abc"轉(zhuǎn)換成數(shù)字
n throws關(guān)鍵字
聲明本方法不處理異常,讓調(diào)用者處理。
在函數(shù)簽名中使用throws 聲明交給函數(shù)調(diào)用者caller去解決。
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
try {
divide();//調(diào)用的此方法拋出了異常
}
catch (Exception e) {
System.out.println("錯誤:被除數(shù)和除數(shù)必須是整數(shù),且除數(shù)不能為零。");
e.printStackTrace();
e.getMessage();//暫時打印不出任何內(nèi)容
}
finally{
System.out.println("感謝使用本程序");
}
}
public static void divide() throws Exception{//聲明異常
Scanner scanner = new Scanner(System.in);
System.out.println("請輸入被除數(shù)");
int num1 = scanner.nextInt();//有可能異常InputMismatchException
System.out.println("請輸入除數(shù)");
int num2 = scanner.nextInt();//有可能異常InputMismatchException
System.out.println(num1 / num2);//有可能異常除數(shù)等于0:ArithmeticException
}
}
n throw關(guān)鍵字
主動拋出異常
如果對于具體的一些處理邏輯,程序員也可以主動的拋出異常讓 外層處理。(此異?赡苁浅绦騿T自己定義的)。
class Person {
private String name;
private String sex = "男";
public void setSex(String sex) throws Exception{
if("男".equals(sex) || "女".equals(sex)){
this.sex = sex;
}
else{
//主動拋出異常。也可做成自定義異常并拋出
throw new Exception("性別輸入錯誤,必須是男或女");
}
}
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 異常類的兩個打印異常信息的好辦法
一般,我們需要打印出異常的相關(guān)信息。在Exception類中,定義了下面兩個方法,
e.printStackTrace();//打印調(diào)用堆棧信息 并把e.getMessage()的信息也打出來了
e.getMessage();//打印異常的相關(guān)信息
與IOException類相似,我們自己定義的異常類往往也是Exception的子類,我們可以(1)覆蓋e.getMessage()方法; 也可以(2)構(gòu)造的時候傳入具體的字符串信息,因為e.getMessage()就是獲取這個信息
示例
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塊:Catch塊的排列順序必須是從子類到父類。最后一個一般是Exception。
(2)不要在fianlly中使用return。
不要在finally中拋出異常。
減輕finally的任務(wù),不要在finally中做一些其它的事情,finally塊僅僅用來釋放資源是最合適的。
將盡量將所有的return寫在函數(shù)的最后面,而不是try … catch … finally中。
n 練習(xí)
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("請輸入被除數(shù)");
num1 = scanner.nextInt();//有可能異常InputMismatchException
System.out.println("請輸入除數(shù)");
num2 = scanner.nextInt();//有可能異常InputMismatchException
System.out.println(String.format("%d / %d = %d", num1, num2, num1 / num2));//有可能異常除數(shù)等于0:ArithmeticException
}
catch(InputMismatchException e)
{
System.err.println("被除數(shù)和除數(shù)必須是整數(shù)");
return;
}
catch(ArithmeticException e)
{
System.err.println("除數(shù)不能為零");
//return;
System.exit(0);//這個是立即終止
}
catch (Exception e) {
System.out.println("錯誤:被除數(shù)和除數(shù)必須是整數(shù),且除數(shù)不能為零。");
e.printStackTrace();
e.getMessage();//暫時打印不出任何內(nèi)容
}
finally{
System.out.println("感謝使用本程序");
}
}
}