1. 異常的產(chǎn)生
異常是導(dǎo)致程序中斷執(zhí)行的一種指令流笙隙,異常一旦出現(xiàn)且沒有對其進(jìn)行合理的處理,程序就中斷執(zhí)行坎缭。
范例:不產(chǎn)生異常的代碼
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("1.開始除法計(jì)算");
System.out.println("2.計(jì)算 " + (10/2));
System.out.println("3.結(jié)束除法計(jì)算");
}
}
輸出顯示:
1.開始除法計(jì)算
2.計(jì)算 5
3.結(jié)束除法計(jì)算
范例:產(chǎn)生異常
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("1.開始除法計(jì)算");
//此行代碼產(chǎn)生異常
System.out.println("2.計(jì)算 " + (10 / 0));
System.out.println("3.結(jié)束除法計(jì)算");
}
}
輸出顯示:
1.開始除法計(jì)算
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ExceptionDemo.main(ExceptionDemo.java:5)
異常一旦發(fā)生竟痰,產(chǎn)生異常的語句及之后的語句都不再執(zhí)行,默認(rèn)輸出異常信息后掏呼,程序自動(dòng)結(jié)束執(zhí)行凯亮。
我們要做的就是:即使出現(xiàn)了異常,也應(yīng)該讓程序正確的執(zhí)行完畢哄尔。
2. 處理異常
如果想要進(jìn)行異常的處理,在Java之中提供了三個(gè)關(guān)鍵字:try柠并、catch岭接、finally,而這三個(gè)關(guān)鍵字的使用語法如下:
//“[]” 表示有可能使用到
try {
//有可能出現(xiàn)異常的語句
} [ catch(異常類型 對象){
//異常處理
}catch(異常類型 對象){
//異常處理
}...] [finally {
//不管是否出現(xiàn)異常臼予,都執(zhí)行的代碼
}]
范例:應(yīng)用異常處理try...catch
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("1.開始除法計(jì)算");
try {
System.out.println("2.計(jì)算 " + (10 / 0));
} catch (ArithmeticException e){
e.printStackTrace();
}
System.out.println("3.結(jié)束除法計(jì)算");
}
}
輸出顯示:
1.開始除法計(jì)算
java.lang.ArithmeticException: / by zero
at ExceptionDemo.main(ExceptionDemo.java:6)
3.結(jié)束除法計(jì)算
由于使用了異常處理鸣戴,即使程序出現(xiàn)了異常,也可以正常的執(zhí)行完畢粘拾。而且使用異常類中提供的printStackTrace()方法窄锅,可以完整的輸出異常信息。
范例:使用try...catch...finally
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("1.開始除法計(jì)算");
try {
System.out.println("2.計(jì)算 " + (10 / 0));
} catch (ArithmeticException e){
e.printStackTrace();
} finally {
System.out.println("不管是否有異常缰雇,都執(zhí)行此語句入偷!");
}
System.out.println("3.結(jié)束除法計(jì)算");
}
}
輸出顯示:
1.開始除法計(jì)算
java.lang.ArithmeticException: / by zero
at ExceptionDemo.main(ExceptionDemo.java:5)
不管是否有異常,都執(zhí)行此語句械哟!
3.結(jié)束除法計(jì)算
3. 異常的處理流程(核心)
首先觀察兩個(gè)異常類的繼承結(jié)構(gòu):
觀察可以發(fā)現(xiàn)所有的異常類都是Throwable的子類疏之,而在Throwable下有兩個(gè)子類:
Error 和 Exception
- Error :指的是JVM錯(cuò)誤,即:此時(shí)的程序還沒有執(zhí)行暇咆,用戶還無法處理锋爪;
- Exception :指的是程序運(yùn)行中產(chǎn)生的異常丙曙,用戶可以處理。
也就是所謂的異常處理指的就是所有的Exception以及它的子類異常其骄。
面試題:Java中的異常處理流程
范例:使用Exception處理異常
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("1.開始除法計(jì)算");
try {
int x = Integer.parseInt(args[0]);
int y = Integer.parseInt(args[1]);
System.out.println("2.計(jì)算 " + (x / y));
System.out.println("**************");
} catch (Exception e){
e.printStackTrace();
} finally {
System.out.println("不管是否有異常亏镰,都執(zhí)行此語句!");
}
System.out.println("3.結(jié)束除法計(jì)算");
}
}
所有的異常都使用了Exception進(jìn)行處理拯爽,所以在程序之中不用再去關(guān)心到底使用哪一個(gè)異常索抓。
注意:
- 在編寫多個(gè)catch捕獲異常的時(shí)候,捕獲范圍大的異常一定要放在捕獲異常范圍小的異常之后某抓,否則編譯錯(cuò)誤纸兔。
- 雖然直接捕獲Exception比較方便,但是這樣也不好否副,因?yàn)檫@樣所有的異常都會(huì)安裝同一方式處理汉矿。
4. throws關(guān)鍵字
throws關(guān)鍵字主要用于方法聲明上,指的是當(dāng)方法之中出現(xiàn)異常后交由被調(diào)用處來進(jìn)行處理备禀。
范例:使用throws
class MyMath {
//存在throws關(guān)鍵字洲拇,表示此方法里面產(chǎn)生的異常交給調(diào)用處處理
public static int div(int x , int y) throws Exception {
return x / y ;
}
}
public class ThrowsDemo {
public static void main(String[] args) {
try {
System.out.println(MyMath.div(10 , 2));
} catch (Exception e) {
e.printStackTrace();
}
}
}
調(diào)用了具有throws聲明的方法之后,不管操作是否出現(xiàn)異常曲尸,都必須使用try...catch來進(jìn)行異常的處理赋续。
5. throw關(guān)鍵字
在程序之中直接使用throw手工拋出一個(gè)異常類的實(shí)例化對象。
范例:手工拋出異常
public class ThrowDemo {
public static void main(String[] args) {
try {
throw new Exception("自己定義的異常");
} catch(Exception e) {
e.printStackTrace();
}
}
}
輸出顯示:
java.lang.Exception: 自己定義的異常
at ThrowDemo.main(ThrowDemo.java:4)
面試題:throws 和 throw的區(qū)別
- throws:在方法的聲明上使用另患,表示此方法在調(diào)用時(shí)必須處理異常纽乱;
- throw:指的是在方法中人為拋出一個(gè)異常類對象(這個(gè)異常類對象可能是自己實(shí)例化或是拋出已知存在)。
6. 重要的代碼模型:異常的使用格式
需求:要求定義一個(gè)div()方法昆箕,在進(jìn)行計(jì)算之前打印提示信息鸦列,計(jì)算結(jié)束也打印提示信息,如果在計(jì)算之中產(chǎn)生了異常鹏倘,則交給被調(diào)用處進(jìn)行處理薯嗤。
范例:不出錯(cuò)的代碼
class MyMath {
public static int div(int x , int y) {
int result = 0;
System.out.println("*** 1. 計(jì)算開始 ***");
result = x / y;
System.out.println("*** 1. 計(jì)算結(jié)束 ***");
return result;
}
}
public class ThrowsDemo {
public static void main(String[] args) {
System.out.println(MyMath.div(10 , 2));
}
}
但是以上代碼中的除法操作不一定永遠(yuǎn)都正常完成,所以應(yīng)該進(jìn)行合理的處理纤泵。首先骆姐,如果方法出現(xiàn)異常,必須交由調(diào)用處處理捏题,所以應(yīng)該在方法上使用throws拋出
class MyMath {
//存在throws關(guān)鍵字玻褪,表示此方法里面產(chǎn)生的異常交給調(diào)用處處理
public static int div(int x , int y) throws Exception {
int result = 0;
System.out.println("*** 1. 計(jì)算開始 ***");
result = x / y;
System.out.println("*** 1. 計(jì)算結(jié)束 ***");
return result;
}
}
public class ThrowsDemo {
public static void main(String[] args) {
try {
System.out.println(MyMath.div(10 , 2));
} catch (Exception e) {
e.printStackTrace();
}
}
}
如果以上代碼真的出錯(cuò),程序的有些內(nèi)容就不執(zhí)行了涉馅,所以修改如下:
class MyMath {
//存在throws關(guān)鍵字敞斋,表示此方法里面產(chǎn)生的異常交給調(diào)用處處理
public static int div(int x , int y) throws Exception {
int result = 0;
System.out.println("*** 1. 計(jì)算開始 ***");
try {
result = x / y;
} catch (Exception e) {
throw e; //繼續(xù)拋異常
} finally {
System.out.println("*** 1. 計(jì)算結(jié)束 ***");
}
return result;
}
}
public class ThrowsDemo {
public static void main(String[] args) {
try {
System.out.println(MyMath.div(10 , 0));
} catch (Exception e) {
e.printStackTrace();
}
}
}
輸出顯示:
*** 1. 計(jì)算開始 ***
*** 1. 計(jì)算結(jié)束 ***
java.lang.ArithmeticException: / by zero
at MyMath.div(ThrowsDemo.java:7)
at ThrowsDemo.main(ThrowsDemo.java:19)
7. RuntimeException 類
先觀察一段代碼:
public class RuntimeExceptionDemo {
public static void main(String[] args) {
int temp = Integer.parseInt("100");
System.out.println(temp); //輸出100
}
}
在看一下parseInt()方法的定義:
public static int parseInt(String s) throws NumberFormatException
此時(shí)parseInt()方法上拋出了NumberFormatException肄方,按理說袭艺,應(yīng)該進(jìn)行強(qiáng)制性的異常捕獲,但是現(xiàn)在并沒有強(qiáng)制性操作捻浦,所以來看下NumberFormatException的繼承結(jié)構(gòu):
java.lang.Object
java.lang.Throwable
java.lang.Exception
java.lang.RuntimeException → 運(yùn)行時(shí)異常
java.lang.IllegalArgumentException
java.lang.NumberFormatException
在Java里面為了方便用戶代碼的編寫,專門提供了RuntimeException類桥爽,其特征為:程序在編譯的時(shí)候不會(huì)強(qiáng)制性要求用戶處理異常朱灿,用戶可以根據(jù)需求選擇性的進(jìn)行處理,如果沒有處理又發(fā)生了異常钠四,則交給JVM默認(rèn)處理盗扒。
面試題:Exception 和 RuntimeException的區(qū)別?列舉幾個(gè)常見的RuntimeException子類:
- Exception是RuntimeException的父類缀去;
- 使用Exception定義的異常必須要被處理侣灶,而RuntimeException的異常可以選擇性處理:
- 常見的RuntimeException:ArithmeticException缕碎、NullPointerException褥影、ClassCastException。
8. assert關(guān)鍵字—斷言(了解)
assert關(guān)鍵字是在JDK1.4的時(shí)候引入的咏雌,其主要的功能就是進(jìn)行斷言凡怎。斷言:指的是程序執(zhí)行到某行代碼處判斷是否是預(yù)期的結(jié)果。
范例:觀察斷言
public class AssertDemo {
public static void main(String[] args) {
int num = 10;
//中間可能經(jīng)過了多行代碼操作num的內(nèi)容
//期望中的內(nèi)容是20
assert num == 20 : "num的內(nèi)容不是20";
System.out.println("num = " + num);
}
}
正常執(zhí)行輸出顯示:
num = 10
啟用斷言執(zhí)行:java -ea AssertDemo
Exception in thread "main" java.lang.AssertionError: num的內(nèi)容不是20
at AssertDemo.main(AssertDemo.java:6)
9. 自定義異常
Java本身提供了大量的異常赊抖,但是這些異常實(shí)際工作中并不夠去使用统倒,所以就需要用戶自定義異常類。如果開發(fā)自定義的異常類可以選擇繼承Exception或者RuntimeException類氛雪。
范例:定義AddException
class AddException extends Exception{
public AddException(String msg) {
super(msg);
}
}
public class AddExceptionDemo{
public static void main(String[] args) {
int num = 20;
try {
if (num > 0) { //出現(xiàn)錯(cuò)誤房匆,應(yīng)該產(chǎn)生異常
throw new AddException("數(shù)值傳遞過大");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
以上代碼知識(shí)介紹自定義異常的形式,但是并不能說明自定義異常的實(shí)際使用报亩。
總結(jié)
- Exception的父類是Throwable坛缕,但是在編寫代碼的時(shí)候盡量不要使用Throwable,因?yàn)門hrowable類有子類Error捆昏,用戶能處理的只有Exception類
- 異常處理的標(biāo)準(zhǔn)格式:try、catch毙沾、finally骗卜、throw、throws左胞;
- RuntimeException與Exception的區(qū)別寇仓。