例外
-
首先是上期的String物件一共生成了11個(包括底層生成的存于哈希表中的那個哈~!)
-
首先要知道例外有兩大類,一個是編譯期例外、一個是運行時例外
-
編譯器例外:如System.out.println寫成system.out.println,此時編譯時就會報錯,或者說在你的IDEA里此代碼當場報紅
-
運行時例外:當程式跑起來了,才在IDEA底下的結果視窗中彈出例外的資訊,所以說此時代碼是可以通過編譯的
- 使用例外的好處:可以讓我們的例外都統一放在一起,方便管理,看著也舒服;要不然采用我們平時寫代碼的習慣,每個都用個if陳述句去判斷,可能造成正常運行的代碼和出現例外的代碼都混在一起的現象,
例外的基本用法😋
try{
有可能出現例外的陳述句都丟在這;
}catch(例外型別 例外物件){
處理例外的陳述句;
}finally{
例外的出口;//這個finally的部分可以不寫,寫了的話,這里面的陳述句是一定會被執行的,即使try陳述句里有return,finally里的陳述句都要先執行!
}
-
代碼執行到有例外的陳述句后,如果我們自己不去處理,其結果是什么?
結果是這個例外會交給JVM去處理!而JVM處理的結果就是,代碼從這個出現例外的陳述句開始往下的代碼全部終止!或者說例外終止,或者說奔潰了,
-
那給一個我們主動去處理一個例外的情況看看:
public class Test {
public static void main(String[] args) {
int n=0;
try{
if(n==0){
throw new Exception("拋出例外咯!");//這里new物件的時候呼叫的是內置例外類,這句話一旦執行,這個例外會在結果欄里給出定位(例外代碼所在行號,這也是例外使用的好處之一)
// Exception的特定構造方法
}
}catch(Exception e){
e.printStackTrace();//列印例外e,這里就相當于處理例外了
}
System.out.println("hehe");
}
}
/*
結果:
java.lang.Exception: 拋出例外咯!
at Test.main(Test.java:13)
hehe
Process finished with exit code 0
*/
上述代碼給我們的啟示是:即使有例外出現(被拋出),我們將其處理之后,不影響后續代碼的執行,不至于整個程式都崩潰,而如果我們自己不處理,就交由JVM處理,其結果就是程式將終止與第六行代碼,
- 其次我們try{}內的陳述句從上往下依次執行時,一旦出現某個陳述句拋出例外,那這個{}內該代碼以下的代碼不會再執行!
關于例外列印的順序🍊
- 我們知道JVM處理例外后,將例外資訊堆疊的例外都將列印出來,因為這是一個堆疊,所以說先進去的例外資訊后出來,后進去的例外資訊會先出來,就比如:
public class Test {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int n=scanner.nextInt();
System.out.println(n);
}
}
當我們從鍵盤鍵入一些字母之后,查看最后的例外資訊:
sasa
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at Test.main(Test.java:13)
由上面提到的先進后出的例外資訊可知,第一個出現的例外是main類的第13行,而上面的例外資訊都是內置類的原始碼出錯!(我們不可能懷疑原始碼),所以我們就乖乖去修改第13行代碼就可以啦!改完(我們自己可以去用try-catch去包裹它,包裹:surround)即可,
處理多個例外的情況🍨
public class Test {
public static void main(String[] args) {
int[] arr={1,2,3};
try{
arr=null;
System.out.println(arr[5]);
System.out.println(arr.length);
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
System.out.println("捕捉到了陣列越界例外!");
}catch (NullPointerException e){
e.printStackTrace();
System.out.println("捕捉到了空指標例外!");
}
System.out.println("嘻嘻!");
}
}
/*
結果:
java.lang.NullPointerException
at Test.main(Test.java:15)
捕捉到了空指標例外!
嘻嘻!
*/
還是上面提到的原則:try里第一個拋出例外的陳述句執行完,try內的之后的陳述句都終止執行,
當然我們可以多捕獲多個例外的代碼換一種寫法:
public class Test {
public static void main(String[] args) {
int[] arr={1,2,3};
try{
arr=null;
System.out.println(arr[5]);
System.out.println(arr.length);
}catch (ArrayIndexOutOfBoundsException | NullPointerException e){//例外想寫幾個寫幾個,e只能寫一個哦
e.printStackTrace();//根據例外資訊判斷是哪個例外即可
}
System.out.println("嘻嘻!");
}
}
例外的體系結構🌊

- 頂層類Throwable派生出兩個而重要子類:Error和Exceptiom
- 其中Error代表java運行時內部錯誤或者資源耗盡錯誤,應用程式不會拋出這個例外,這種內部錯誤一旦出現,除了告知用戶并使程式終止之外,無能為力,這種情況很少出現
- Exception是我們程式員所使用的例外類的父類
- 其中Exception有一個子類叫RuntimeException,這里面又派生出許多我們常見的例外類,如:NullpointerException等
-
說上面的是因為:我們用catch去捕捉例外類時是可以用父類例外去捕捉子類例外的,從邏輯上來說,我們可以用Exception去捕捉所有的它的子類例外!但不建議這樣去做,因為Exception涵蓋的例外類太多,可能把控不好,最好是寫我們想捕捉的具體的例外類,盡量不要使用父類例外去捕捉子類例外,但沒說不可用哈,
-
若一段代碼中既有父類例外去捕獲一個例外,也有具體的例外去捕獲例外,那書寫順序是:先寫子類例外去捕獲,然后再父類,寫反了,當場報錯,
-
當我們不知道一個例外的父類到底是誰時:我們可以左鍵點擊一下例外類,然后右鍵→Diagram→show就可以看見整個繼承關系了!:(社區辦可能沒有這個功能,想不花錢用旗艦版,那只能破解版了)

-
其次很重要的一點:函式處理例外的時候,本函式拋出例外如果么有當場解決,這個例外就會自動拋給呼叫這個函式的函式,呼叫者(主函式)如果還不處理這個例外,那就只能交由JVM去處理這個例外了!換句話說就是:例外會沿著例外的資訊呼叫堆疊進行傳遞!!!!
finally的使用🌴
- finally表示最后的善后作業,例如釋放資源
public class Test {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);//Scanner相當于一個資源!~
try{
int n=scanner.nextInt();
System.out.println(10/n);
}catch(InputMismatchException e){
e.printStackTrace();
System.out.println("捕捉到了輸入不匹配例外!");
}catch(ArithmeticException e){
e.printStackTrace();
System.out.println("捕捉到了算術例外,可能除數為0");
}finally {
scanner.close();
}
}
}
針對上述代碼,這里有一個小技巧一步到位:
public class Test {
public static void main(String[] args) {
try(Scanner scanner=new Scanner(System.in)){//這里可以做到try里面的陳述句執行完畢,自動呼叫scanner.close()
int n=scanner.nextInt();
System.out.println(10/n);
}catch(InputMismatchException e){
e.printStackTrace();
System.out.println("捕捉到了輸入不匹配例外!");
}catch(ArithmeticException e){
e.printStackTrace();
System.out.println("捕捉到了算術例外,可能除數為0");
}
}
}
- 對上述代碼:一個函式拋出例外(本函式內部不作處理!)我想把例外傳給主函式,要對被呼叫的函式做例外申明:
public class Test {
public static void func()throws Exception{//throws就用于宣告例外啦,所以說throws用在宣告,而throw用在拋出例外,給例外定位!
//哪里寫了throw,只要這個陳述句被執行,最后這行代碼會被標出來,到時候我們直接過去改就可以了!
throw new Exception();
}
public static void main(String[] args) {
try{
func();
}catch (Exception e){
e.printStackTrace();
System.out.println("捕捉到了被呼叫函式的例外啦!");
}
System.out.println("haha");
}
}
- 針對finally里面的陳述句,我們要知道,即使try{}內有return 的陳述句,我們finally里面的陳述句也會在此之前執行(所以說我們盡量不要在finally里面寫return 的陳述句!),如:
public class Test {
public static int func(){
int a=10;
try{
return a;
}catch(ArithmeticException e){
e.printStackTrace();
}finally{
return 20;
}
}
public static void main(String[] args) {
int ret=func();
System.out.println(ret);
}
}//列印結果是什么
//20,因為finally里面的陳述句先執行(因為要在func方法結束之前執行),回傳20后,try里面的陳述句沒法回傳了!
例外的處理流程?
- 程式先執行try中的代碼
- 如果try中的代碼出現例外,就會結束try中的代碼,看catch 中的例外是否匹配
- 如果找到匹配的例外,就會執行對應catch中的代碼
- 如果沒有找到匹配的catch的例外,就會傳遞給呼叫者
- 無論是否找到匹配的例外型別,finally里面的代碼都會被執行,finally所在方法結束之前執行!
- 如果上層呼叫者沒有處理例外,繼續往上面傳
- 一直穿到了main函式也沒有人去處理這個例外,那這個例外將會交由JVM處理,此時程式就會出現例外終止,結果代碼是code 1
手動拋出例外(throw)🍉
- 除了java中內置的一些例外類之外,程式員也可以手動拋出某個例外,使用throw關鍵字來完成這個操作(前述已提及throws做例外宣告),
public class Test{
public static void func(int x){//最好能在這做一個申明
if(x==0){
throw new ArithmeticException("拋出一個例外,你可以在這里描述這個例外")
}
}
public static void main(String[] args){
func(0);
}
}//這里只是拋出例外,我并沒有去處理它
//結果
Exception in thread "main" java.lang.ArithmeticException: 拋出一個例外,你可以在這里描述這個例外
at Test.func(Test.java:14)
at Test.main(Test.java:18)
Process finished with exit code 1 //這個1就是因為是JVM處理了這個例外,如果是我們自己處理的就是數字0
受查例外必須顯式去處理是什么意思?🙋?♂
- 受查例外前面也有所提及,它是指Exception子類中除了RuntimeException之外的類,這些類一旦出現,我們在程式運行之前就可以看到代碼報紅了,這里意思就是說,你不處理這個例外,你連編譯都別想過去,
- 而顯式處理這種例外的方法主要有兩種,一個是用try-catch進行包裹;一個就是“往上拋”,所謂“往上拋”就是說,當前方法只管用這個類,出不出錯我都不管,我就把這個可能的例外拋給呼叫者,呼叫者如果想處理,就去catch它,或者呼叫者也不想處理,呼叫者也將這個可能的例外往上拋,就連我們的main函式都可以這樣做,當main函式也將這個可能的例外往上拋時,那就相當于交給JVM處理了,因為中間沒有一個人想處理這個“爛攤子”,
舉個例子:比如我們常用的介面Clonable(注意事項已寫在代碼里):
class Person implements Cloneable{
public int age=10;
@Override
protected Object clone() throws CloneNotSupportedException {//下面的clone()屬于受查例外,super可能是不允許克隆的
//這里就相當于這事我先干了,例外交給上頭去處理,
return super.clone();
}
}
public class Test1 {
public static void main(String[] args) throws CloneNotSupportedException {//這里也是把clone()的可能例外往上拋,如果出現例外
//這里就相當于交給JVM去處理了,
Person person=new Person();
Person person1=(Person)person.clone();
System.out.println(person1.age);
}
}
用try-catch進行包裹,不再贅述,就是前面的例子,就是我們當場就把那個可能的例外給處理了的意思,
什么是自定義例外?怎么使用???
- java中雖然已經內置了豐富的例外類,但是實際場景中我們可能還需要對例外類進行拓展,創建符合我們自己的例外類,
- 那使用自定義例外類是必須繼承一個內置的例外類的,內置的例外類無非就是兩種:一種是受查例外(Exception)、一種就是非受查例外(RuntimeException),
舉例(注意事項已寫在代碼內部)
class MyException1 extends Exception{
public MyException1(String message) {
super(message);
}
}//自定義受查例外1
class MyException2 extends RuntimeException{
public MyException2(String message) {
super(message);
}
}//自定義受查例外2
public class Test2 {
public static void func1(int x){
try{//使用try-catch包裹顯式處理這個受查例外
if(x==0){
throw new MyException1("hehe");//此處子類構造要先幫父類進行構造
}
}catch (MyException1 e){
e.printStackTrace();
}
}
public static void func2(int x)throws MyException2{//“往上拋”
if(x==0){
throw new MyException2("HAHA");//這個也是子類構造前要先幫父類進行構造
}
}
public static void main(String[] args) {
func1(0);
func2(0);
}
}
//結果:
MyException1: hehe
at Test2.func1(Test2.java:22)
at Test2.main(Test2.java:34)
Exception in thread "main" MyException2: HAHA
at Test2.func2(Test2.java:30)
at Test2.main(Test2.java:35)
Process finished with exit code 1//這里是1是因為,func2()往上拋例外,但我main并沒有去catch這個可能的例外,所以交由JVM去處理,那就會出現這樣的例外終止代碼1
- 自定義例外的使用:
class NameException1 extends Exception{
public NameException1(String message) {
super(message);
}
}
class PasswordException2 extends Exception{
public PasswordException2(String message) {
super(message);
}
}
public class Test3 {
private static final String name="hehe";//static final修飾的成員變數必須初始化
private static final String psaaword="123";//同理
public static void login(String name,String password)throws NameException1,PasswordException2{
if(!Test3.name.equals(name)){
throw new NameException1("用戶名錯誤!");//僅拋出例外不行,要顯式處理
}
if(!Test3.psaaword.equals(password)){
throw new PasswordException2("密碼錯誤!");
}
}
public static void main(String[] args) {
try{//try-catch包裹進行處理顯式例外
login("hehe","1234");
}catch (NameException1 e){
e.printStackTrace();//定位例外
System.out.println("用戶名錯誤!");
}catch(PasswordException2 e){
e.printStackTrace();//定位例外
System.out.println("密碼錯誤!");
}
}
}
//結果:
PasswordException2: 密碼錯誤!
at Test3.login(Test3.java:27)
at Test3.main(Test3.java:33)
密碼錯誤!
Process finished with exit code 0//因為例外是我們自己處理的,所以code 0
一個練習題:使用while回圈建立類似“恢復模型”的例外處理行為,他將不斷重復,直到例外不再拋出!下期揭曉答案(答案不唯一,起到類似的效果即可)🍂
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/395528.html
標籤:java
上一篇:如何為容器安裝其他依賴項?
