第10章異常-Exception
第10章異常-Exception
10.1看個實(shí)際的問題和一段代碼
運(yùn)行下面的代碼,看看有什么問題->引出異常和異常處理機(jī)制Exception01.java
public static void main(String[] args){
int num1 = 10;
int num2 = 0;
int res = num1 / num2;
System.out.println("程序繼續(xù)運(yùn)行....");
}
10.2解決方案-異常捕獲
對異常進(jìn)行捕獲,保證程序可以繼續(xù)運(yùn)行.
package com.ming.exception;
/**
* @author 明
* @version 1.0
*/
public class Exception01 {
public static void main(String[] args) {
//1. num1 / num2 => 10 / 0
//2. 當(dāng)執(zhí)行到 num1 / num2 因?yàn)?num2 = 0, 程序就會出現(xiàn)(拋出)異常 ArithmeticException
//3. 當(dāng)拋出異常后,程序就退出,崩潰了 , 下面的代碼就不在執(zhí)行
//4. 大家想想這樣的程序好嗎? 不好,不應(yīng)該出現(xiàn)了一個不算致命的問題,就導(dǎo)致整個系統(tǒng)崩潰
//5. java 設(shè)計者,提供了一個叫 異常處理機(jī)制來解決該問題
// int res = num1 / num2;
//如果程序員,認(rèn)為一段代碼可能出現(xiàn)異常/問題,可以使用try-catch異常處理機(jī)制來解決
//從而保證程序的健壯性
//將該代碼塊->選中->快捷鍵 ctrl + alt + t -> 選中 try-catch
//6. 如果進(jìn)行異常處理,那么即使出現(xiàn)了異常,程序可以繼續(xù)執(zhí)行
int num1 = 10;
int num2 = 0;
int res = 0;
try {
res = num1 / num2;
} catch (Exception e) {
//e.printStackTrace();
System.out.println("出現(xiàn)異常的原因=" + e.getMessage());//輸出異常信息
}
System.out.println("程序繼續(xù)運(yùn)行....");
}
}
10.3 異常介紹
● 基本概念
Java語言中,將程序執(zhí)行中發(fā)生的不正常情況稱為“異常”。(開發(fā)過程中的語法錯誤和邏輯錯誤不是異常)
● 執(zhí)行過程中所發(fā)生的異常事件可分為兩大類
-
Error(錯誤): Java虛擬機(jī)無法解決的嚴(yán)重問題。如: JVM系統(tǒng)內(nèi)部錯誤、資源耗盡等嚴(yán)重情況。比如: StackOverflowError[棧溢出]和OOM(out of memory), Error 是嚴(yán)重錯誤,程序會崩潰。
-
Exception: 其它因編程錯誤或偶然的外在因素導(dǎo)致的一般性問題,可以使用針對性的代碼進(jìn)行處理。例如空指針訪問,試圖讀取不存在的文件,網(wǎng)絡(luò)連接中斷等等,Exception 分為兩大類: 運(yùn)行時異常[程序運(yùn)行時,發(fā)生的異常]和編譯時異常[編程時,編譯器檢查出的異常]。
10.4 異常體系圖一覽
10.4.1 異常體系圖

10.4.2 異常體系圖的小結(jié)
- 異常分為兩大類,運(yùn)行時異常和編譯時異常.
- 運(yùn)行時異常,編譯器檢查不出來。一般是指編程時的邏輯錯誤,是程序員應(yīng)該避免其出現(xiàn)的異常。java.lang.RuntimeException類及它的子類都是運(yùn)行時異常
- 對于運(yùn)行時異常,可以不作處理,因?yàn)檫@類異常很普遍,若全處理可能會對程序的可讀性和運(yùn)行效率產(chǎn)生影響
- 編譯時異常,是編譯器要求必須處置的異常。
10.5 常見的運(yùn)行時異常
10.5.1 常見的運(yùn)行時異常包括
- NullPointerException 空指針異常
- ArithmeticException 數(shù)學(xué)運(yùn)算異常
- ArrayIndexOutOfBoundsException 數(shù)組下標(biāo)越界異常
- ClassCastException 類型轉(zhuǎn)換異常
- NumberFormatException 數(shù)字格式不正確異常[]
10.5.2 常見的運(yùn)行時異常舉例
- NullPointerException 空指針異常 NullPointerException_.java
當(dāng)應(yīng)用程序試圖在需要對象的地方使用 null 時,拋出該異常,看案例演示。
public class NullPointerException_ {
public static void main(String[] args) {
String name = null;
System.out.println(name.length());
}
}
- ArithmeticException數(shù)學(xué)運(yùn)算異常ArithmeticException_.java
當(dāng)出現(xiàn)異常的運(yùn)算條件時,拋出此異常。例如,一個整數(shù)“除以零”時,拋出此類的一個實(shí)例,案例演示
public class ArithmeticException {
public static void main(String[] args) {
int a = 10;
int b = 0;
int num =a / b; //拋出ArithmeticException
System.out.println(num);
}
}
- ArrayIndexOutOfBoundsException數(shù)組下標(biāo)越界異常
用非法索引訪問數(shù)組時拋出的異常。如果索引為負(fù)或大于等于數(shù)組大小,則該索引為非法索引
public class ArrayIndexOutOfBoundsException_ {
public static void main(String[] args) {
int[] arr = {1,2,4};
for (int i = 0; i <= arr.length; i++) {
System.out.println(arr[i]);
}
}
}
- ClassCastException類型轉(zhuǎn)換異常
當(dāng)試圖將對象強(qiáng)制轉(zhuǎn)換為不是實(shí)例的子類時,拋出該異常。例如,以下代碼將生成一個ClassCastException
public class ClassCastException_ {
public static void main(String[] args) {
A b = new B(); //向上轉(zhuǎn)型
B b2 = (B)b;//向下轉(zhuǎn)型,這里是OK
C c2 = (C)b;//這里拋出ClassCastException
}
}
class A {}
class B extends A {}
class C extends A {}
- NumberFormatException數(shù)字格式不正確異常
public class NumberFormatException_ {
public static void main(String[] args) {
String name = "sky";
//將String 轉(zhuǎn)成 int
int num = Integer.parseInt(name);//拋出NumberFormatException
System.out.println(num);//1234
}
}
10.6編譯異常
10.6.1 介紹
編譯異常是指在編譯期間,就必須處理的異常,否則代碼不能通過編譯。
10.6.2 常見的編譯異常
- SQLException //操作數(shù)據(jù)庫時,查詢表可能發(fā)生異常
- IOException //操作文件時,發(fā)生的異常
- FileNotFoundException //當(dāng)操作一個不存在的文件時,發(fā)生異常
- ClassNotFoundException //加載類,而該類不存在時,異常
- EOFException // 操作文件,到文件末尾,發(fā)生異常
- IllegalArguementException //參數(shù)異常
10.6.3 案例說明
因?yàn)槲覀冞€沒有學(xué)習(xí)SQL,文件編程等等,這里我們先舉一個(FileNotFoundException)案例來說明,其它異常使用方式類似,演示:
代碼
public class Exception02 {
public static void main(String[] args) {
try {
FileInputStream fis;
fis = new FileInputStream("d:\\aa.jpg");
int len;
while ((len = fis.read()) != -1) {
System.out.println(len);
}
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
10.7 異常課堂練習(xí)
看看下面代碼是否正確,為什么? 課堂練習(xí) 4 個題 4min
String friends[]={"tom","jack","milan"};
for(int i=0;i<4;i++) {
System.out.println(friends[i]);
}
//出現(xiàn)ArrayIndexOutOfBoundsException
Cat c=new Cat();
cat=null;
System.out.println(cat.name);
//出現(xiàn)NullPointerException
public class AAA{
int x;//默認(rèn) 0
public static void main(String[] args) {
int y;
AAA a=new AAA();
y=3/a.x; //==> 3 / 0
System.out.println(“program ends ok!”);
}
}//ArithmeticException
class Person {
public static void main(String[] args)
Object obj = new Date();
Person person;
person = (Person)obj;
System.out.println(person);
}
} //出現(xiàn)ClassCastException
10.8 異常處理
10.8.1 基本介紹
異常處理就是當(dāng)異常發(fā)生時,對異常處理的方式。
10.8.2 異常處理的方式
try-catch-finally
程序員在代碼中捕獲發(fā)生的異常,自行處理throws
將發(fā)生的異常拋出,交給調(diào)用者(方法)來處理,最頂級的處理者就是JVM
10.8.3 示意圖
try-catch-finally 處理機(jī)制示意圖
try {
// 代碼/可能有異常
} catch(Exception e) {
// 捕獲到異常
// 1.當(dāng)異常發(fā)生時
// 2.系統(tǒng)將異常封裝成Exception 對象 e,傳遞給catch
// 3.得到異常對象后,程序員,自己處理
// 4.注意,如果沒有發(fā)生異常 catch代碼塊不執(zhí)行
} finally{
// 1.不管try代碼塊是否有異常發(fā)生,始終要執(zhí)行finally
// 2.所以,通常將釋放資源的代碼,放在finally
}
throws 處理機(jī)制圖
try-catch-finally和throws二選一- 如果程序員,沒有顯示是處理異常,默認(rèn)
throws
10.9 try-catch 異常處理
10.9.1 try-catch 方式處理異常說明 TryCatch01.java
- Java提供
try和catch塊來處理異常。try塊用于包含可能出錯的代碼,catch塊用于處理try塊中發(fā)生的異常。可以根據(jù)需要在程序中有多個try...catch塊。 - 基本語法
try {
// 可疑代碼
// 將異常生成對應(yīng)的異常對象,傳遞給catch塊
} catch(異常) {
// 對異常的處理
}
// 如果沒有finally,語法是可以通過
10.9.2 try-catch 方式處理異常-快速入門
public static void main(String[] args) {
int num1=10;
int num2=0;
try{
int res = num1 / num2;
}catch(Exception e){
System.out.println(e.getMessage());
}
}
10.9.3 try-catch 方式處理異常-注意事項 TryCatchDetail.java
- 如果異常發(fā)生了,則異常發(fā)生后面的代碼不會執(zhí)行,直接進(jìn)入到
catch塊。 - 如果異常沒有發(fā)生,則順序執(zhí)行
try的代碼塊,不會進(jìn)入到catch。 - 如果希望不管是否發(fā)生異常,都執(zhí)行某段代碼(比如關(guān)閉連接,釋放資源等)則使用如下代碼-
finally {}
try{
//可疑代碼
}catch(異常){
//...
}finally{
//釋放資源等..
}
try {
int a = Integer.parseInt(str);
System.out.println("數(shù)字:" + a);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("不管是否發(fā)生異常,始終執(zhí)行的代碼~");
}
代碼
public class TryCatchDetail {
public static void main(String[] args) {
//ctrl + atl + t
//解讀
//1. 如果異常發(fā)生了,則異常發(fā)生后面的代碼不會執(zhí)行,直接進(jìn)入到catch塊
//2. 如果異常沒有發(fā)生,則順序執(zhí)行try的代碼塊,不會進(jìn)入到catch
//3. 如果希望不管是否發(fā)生異常,都執(zhí)行某段代碼(比如關(guān)閉連接,釋放資源等)則使用如下代碼- finally
try {
String str = "ming";
int a = Integer.parseInt(str);
System.out.println("數(shù)字:" + a);
} catch (NumberFormatException e) {
System.out.println("異常信息=" + e.getMessage());
} finally {
System.out.println("finally代碼塊被執(zhí)行...");
}
System.out.println("程序繼續(xù)...");
}
}
- 可以有多個
catch語句,捕獲不同的異常(進(jìn)行不同的業(yè)務(wù)處理),要求父類異常在后,子類異常在前,比如(Exception在后,NullPointerException在前),如果發(fā)生異常,只會匹配一個catch,案例演示
代碼
public class TryCatchDetail02 {
public static void main(String[] args) {
//解讀
//1.如果try代碼塊有可能有多個異常
//2.可以使用多個catch 分別捕獲不同的異常,相應(yīng)處理
//3.要求子類異常寫在前面,父類異常寫在后面
try {
Person person = new Person();
//person = null;
System.out.println(person.getName());//NullPointerException
int n1 = 10;
int n2 = 0;
int res = n1 / n2;//ArithmeticException
} catch (NullPointerException e) {
System.out.println("空指針異常=" + e.getMessage());
} catch (ArithmeticException e) {
System.out.println("算術(shù)異常=" + e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
}
}
}
class Person {
private String name = "jack";
public String getName() {
return name;
}
}
- 可以進(jìn)行
try-finally配合使用,這種用法相當(dāng)于沒有捕獲異常,因此程序會直接崩掉/退出。應(yīng)用場景,就是執(zhí)行一段代碼,不管是否發(fā)生異常,都必須執(zhí)行某個業(yè)務(wù)邏輯
public class TryCatchDetail03 {
public static void main(String[] args) {
/*
可以進(jìn)行 try-finally 配合使用, 這種用法相當(dāng)于沒有捕獲異常,
因此程序會直接崩掉/退出。應(yīng)用場景,就是執(zhí)行一段代碼,不管是否發(fā)生異常,
都必須執(zhí)行某個業(yè)務(wù)邏輯
*/
try{
int n1 = 10;
int n2 = 0;
System.out.println(n1 / n2);
}finally {
System.out.println("執(zhí)行了finally..");
}
System.out.println("程序繼續(xù)執(zhí)行..");
}
}
10.9.4 異常處理課堂練習(xí)
1) 題 1 TryCatchExercise01.java
public class Exception01 {
public static int method() {
try {
String[] names = new String[3];//String[]數(shù)組
if (names[1].equals("tom")) { //NullPointerException
System.out.println(names[1]);
} else {
names[3] = "hspedu";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) { //捕獲
return 3;
} finally { //必須執(zhí)行
return 4; //返回4
}
}
public static void main(String[] args) {
System.out.println(method()); //4
}
}
//輸出什么?
題1解析與答案
- 代碼分析:
在try塊中,names[1].equals("tom")會觸發(fā)NullPointerException(因?yàn)閿?shù)組元素未初始化,為null)。
異常被catch (NullPointerException e)捕獲,準(zhǔn)備返回3,但finally塊一定會執(zhí)行,最終返回finally中的4。 - 答案:4
2) 題 2TryCatchExercise02.java
public class Exception02 {
public static int method() {
int i = 1;
try {
i++; //i = 2
String[] names = new String[3];
if (names[1].equals("tom")) { //空指針
System.out.println(names[1]);
} else {
names[3] = "hspedu";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {
return ++i; //i = 3
} finally { //必須執(zhí)行
return ++i; //i = 4
}
}
public static void main(String[] args) {
System.out.println(method()); //輸出4
}
}
//練習(xí)2 返回
題2解析與答案
- 代碼分析:
try塊中names[1].equals("tom")觸發(fā)NullPointerException,進(jìn)入對應(yīng)catch塊,i先自增為3(準(zhǔn)備返回3)。
執(zhí)行finally塊,i再次自增為4,最終返回finally中的4。 - 答案:4
3) 題 3TryCatchExercise03.java
public class ExceptionExe01 {
public static int method() {
int i = 1;//i = 1
try {
i++;//i=2
String[] names = new String[3];
if (names[1].equals("tom")) {//空指針
System.out.println(names[1]);
} else {
names[3] = "hspedu";
}
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
return 2;
} catch (NullPointerException e) {
return ++i; //i = 3 => 保存臨時變量 temp = 3;
} finally {
++i; //i = 4
System.out.println("i=" + i);//i = 4
}
}
public static void main(String[] args) {
System.out.println(method());// 3
}
}
//練習(xí)3 i=4, 3
題3解析與答案
- 代碼分析:
try塊中觸發(fā)NullPointerException,進(jìn)入對應(yīng)catch塊,i自增為3(臨時保存返回值3)。
執(zhí)行finally塊,i自增為4并打印i=4,但finally塊沒有返回語句,最終返回catch塊中保存的臨時值3。 - 答案:先打印
i=4,再輸出3
10.9.5 try-catch-finally 執(zhí)行順序小結(jié)
- 如果沒有出現(xiàn)異常,則執(zhí)行
try塊中所有語句,不執(zhí)行catch塊中語句,如果有finally,最后還需要執(zhí)行finally里面的語句 - 如果出現(xiàn)異常,則
try塊中異常發(fā)生后,try塊剩下的語句不再執(zhí)行。將執(zhí)行catch塊中的語句,如果有finally,最后還需要執(zhí)行finally里面的語句!
10.9.6 課后練習(xí)題: TryCatchExercise04.java
如果用戶輸入的不是一個整數(shù),就提示他反復(fù)輸入,直到輸入一個整數(shù)為止
public class TryCatchExercise04 {
public static void main(String[] args) {
//如果用戶輸入的不是一個整數(shù),就提示他反復(fù)輸入,直到輸入一個整數(shù)為止
//思路
//1. 創(chuàng)建Scanner對象
//2. 使用無限循環(huán),去接收一個輸入
//3. 然后將該輸入的值,轉(zhuǎn)成一個int
//4. 如果在轉(zhuǎn)換時,拋出異常,說明輸入的內(nèi)容不是一個可以轉(zhuǎn)成int的內(nèi)容
//5. 如果沒有拋出異常,則break 該循環(huán)
Scanner scanner = new Scanner(System.in);
int num = 0;
String inputStr = "";
while (true) {
System.out.println("請輸入一個整數(shù):"); //
inputStr = scanner.next();
try {
num = Integer.parseInt(inputStr); //這里是可能拋出異常
break;
} catch (NumberFormatException e) {
System.out.println("你輸入的不是一個整數(shù):");
}
}
System.out.println("你輸入的值是=" + num);
}
}
10.10 throws 異常處理
10.10.1 基本介紹
- 如果一個方法(中的語句執(zhí)行時)可能生成某種異常,但是并不能確定如何處理這種異常,則此方法應(yīng)顯示地聲明拋出異常,表明該方法將不對這些異常進(jìn)行處理,而由該方法的調(diào)用者負(fù)責(zé)處理。
- 在方法聲明中用
throws語句可以聲明拋出異常的列表,throws后面的異常類型可以是方法中產(chǎn)生的異常類型,也可以是它的父類。
10.10.2 快速入門案例
演示,初步體會一把. Throws01.java
public static void readFile(String file) throws FileNotFoundException {
// ......
// 讀文件的操作可能產(chǎn)生FileNotFoundException類型的異常
FileInputStream fis = new FileInputStream("d:/aa.txt");
// .........
}
10.10.3 注意事項和使用細(xì)節(jié) ThrowsDetail.java
- 對于編譯異常,程序中必須處理,比如 try-catch 或者 throws
- 對于運(yùn)行時異常,程序中如果沒有處理,默認(rèn)就是 throws 的方式處理[舉例]
- 子類重寫父類的方法時,對拋出異常的規(guī)定:子類重寫的方法,所拋出的異常類型要么和父類拋出的異常一致,要么為父類拋出的異常的類型的子類型[舉例]
- 在 throws 過程中,如果有方法 try-catch,就相當(dāng)于處理異常,就可以不必 throws
public class ThrowsDetail {
public static void main(String[] args) {
f2();
}
public static void f2() /*throws ArithmeticException*/ {
//1.對于編譯異常,程序中必須處理,比如 try-catch 或者 throws
//2.對于運(yùn)行時異常,程序中如果沒有處理,默認(rèn)就是throws的方式處理
int n1 = 10;
int n2 = 0;
double res = n1 / n2;
}
public static void f1() throws FileNotFoundException {
//這里大家思考問題 調(diào)用f3() 報錯
//解讀
//1. 因?yàn)閒3() 方法拋出的是一個編譯異常
//2. 即這時,就要f1() 必須處理這個編譯異常
//3. 在f1() 中,要么 try-catch-finally ,或者繼續(xù)throws 這個編譯異常
f3(); // 拋出異常
}
public static void f3() throws FileNotFoundException {
FileInputStream fis = new FileInputStream("d://aa.txt");
}
public static void f4() {
//解讀:
//1. 在f4()中調(diào)用方法f5() 是OK
//2. 原因是f5() 拋出的是運(yùn)行異常
//3. 而java中,并不要求程序員顯示處理,因?yàn)橛心J(rèn)處理機(jī)制
f5();
}
public static void f5() throws ArithmeticException {
}
}
10.11 自定義異常
10.11.1 基本概念
當(dāng)程序中出現(xiàn)了某些“錯誤”,但該錯誤信息并沒有在Throwable子類中描述處理,這個時候可以自己設(shè)計異常類,用于描述該錯誤信息。
10.11.2 自定義異常的步驟
- 定義類:自定義異常類名(程序員自己寫),繼承
Exception或RuntimeException - 如果繼承
Exception,屬于編譯異常 - 如果繼承
RuntimeException,屬于運(yùn)行異常(一般來說,繼承RuntimeException)
10.11.3 自定義異常的應(yīng)用實(shí)例 CustomException.java
當(dāng)我們接收Person對象年齡時,要求范圍在 18 - 120 之間,否則拋出一個自定義異常(要求 繼承RuntimeException),并給出提示信息。
public class CustomException {
public static void main(String[] args) /*throws AgeException*/ {
int age = 180;
//要求范圍在 18 – 120 之間,否則拋出一個自定義異常
if(!(age >= 18 && age <= 120)) {
//這里我們可以通過構(gòu)造器,設(shè)置信息
throw new AgeException("年齡需要在 18~120之間");
}
System.out.println("你的年齡范圍正確.");
}
}
//自定義一個異常
//解讀
//1. 一般情況下,我們自定義異常是繼承 RuntimeException
//2. 即把自定義異常做成 運(yùn)行時異常,好處時,我們可以使用默認(rèn)的處理機(jī)制
//3. 即比較方便
class AgeException extends RuntimeException {
public AgeException(String message) {//構(gòu)造器
super(message);
}
}
10.12 throw 和 throws 的區(qū)別
10.12.1 一覽表
| 意義 | 位置 | 后面跟的東西 | |
|---|---|---|---|
throws |
異常處理的一種方式 | 方法聲明處 | 異常類型 |
throw |
手動生成異常對象的關(guān)鍵字 | 方法體中 | 異常對象 |
10.12.2 測試題-下面的測試輸出什么 ThrowException.java2mi
public class ThrowException {
public static void main(String[] args) {
try {
ReturnExceptionDemo.methodA();
} catch (Exception e) {
System.out.println(e.getMessage());
}
ReturnExceptionDemo.methodB();
}
}
class ReturnExceptionDemo {
static void methodA() {
try {
System.out.println("進(jìn)入方法A");
throw new RuntimeException("制造異常");
} finally {
System.out.println("用A方法的finally");
}
}
static void methodB() {
try {
System.out.println("進(jìn)入方法B");
return;
} finally {
System.out.println("調(diào)用B方法的finally");
}
}
}
/*
輸出結(jié)果
進(jìn)入方法A
用A方法的finally
制造異常
進(jìn)入方法B
調(diào)用B方法的finally
*/

浙公網(wǎng)安備 33010602011771號