[Java] 9.1. try catch 張貼者: 羅康鴻 日期: 6月 29, 2021 取得連結 Facebook X Pinterest 以電子郵件傳送 其他應用程式 try catch,控制Java程式中的異常,避免發生錯誤時當機。 image/svg+xml9.1. 異常處理流程 – try~catch 異常類別的名稱 ClassCastException , 說明了異常發⽣的原因 常⾒的錯誤與異常類別 class Person { String name; // 無其他內容,只是⽤來表⽰繼承⽽已 } class Employee extends Person { // 無內容,只是⽤來表⽰繼承⽽已 } class Main { public static void main( String [] args) { Person p = new Person (); Employee e = ( Employee ) p ; } } 編譯階段 c:\javac Main.jav a 執⾏階段 c:\java Mai n 執⾏結果 Exception in thread "main" java.lang.ClassCastException: Person cannot be cast to Employee at Main.main(Main.java:4) class Main { public static void main( String [] args) { Person p = new Person (); p.name = "Jack" ; try { Employee e = ( Employee ) p ; } catch ( ClassCastException e ){ System.out.println( " 抓到轉型的異常了 !! " ); } System.out.println( " 我的名字是 " + p . name ); } } 執⾏結果 抓到轉型的異常了 !! 我的名字是 Jack class Main { public static void main( String [] args) { Person p = new Person (); p.name = "Jack" ; try { Employee e = ( Employee ) p ; } catch ( ClassCastException e ){ System.out.println( " 抓到轉型的異常了 !! " ); } System.out.println( " 我的名字是 " + p . name ); } } class Main { public static void main( String [] args) { Person p = new Person (); p.name = “Jack" ; try { Employee e = ( Employee ) p ; } catch ( ClassCastException e ){ System.out.println( " 抓到轉型的異常了 !! " ); } System.out.println( " 我的名字是 " + p . name ); } } class Main { public static void main( String [] args) { Person [] ps = new Person [ 3 ]; ps[ 0 ] = new Person(); ps[ 1 ] = new Person(); ps[ 2 ] = new Person(); ps[ 3 ] = new Person(); } } 執⾏時成功 c:\javac Main.jav a 執⾏時錯誤 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3 at Main.main(Main.java:8) class Main { public static void main( String [] args) { Person [] ps = new Person [ 3 ]; ps[ 0 ] = new Person(); ps[ 1 ] = new Person(); ps[ 2 ] = new Person(); try { ps[ 3 ] = new Person(); } catch ( ArrayIndexOutOfBoundsException e ){ System.out.println( " 抓到超出陣列索引值的異常了 !!" ); } } } 以下是常⾒的錯誤( Error )與異常( Exception )類別的繼承樹狀圖,在此圖中,可以了解每個錯誤與 異常物件的繼承關係。雖然錯誤與異常是兩個不同體系的類別,但它們皆是繼承了 Throwable ,因此不 論是錯誤或異常發⽣的時候,皆是只拋出錯誤或異常的⽅式進⾏的。 class Main { public static void main( String [] args) { Employee [] es = new Employee [ 3 ]; es [ 0 ] = new Employee(); es[ 1 ] = new Employee(); es[ 2 ] = new Employee(); Person e1 = new Person (); //Employee e1 = new Employee(); try { es[ 3 ] = ( Employee ) e1 ; } catch ( ArrayIndexOutOfBoundsException e){ System.out.println( " 抓到超出陣列索引值的異常了 !!" ); } } } 執⾏時異常 Exception in thread "main" java.lang.ClassCastException: Person cannot be cast to Employee at Main.main(Main.java:12) class Main { public static void main( String [] args) { Employee [] es = new Employee [ 3 ]; es[ 0 ] = new Employee(); es[ 1 ] = new Employee(); es[ 2 ] = new Employee(); Person e1 = new Person (); //Employee e1 = new Employee(); try { e s[ 3 ] = ( Employee ) e1 ; } catch ( ClassCastException e){ System.out.println( " 抓到轉型的異常了 !!" ); } catch ( ArrayIndexOutOfBoundsException e){ System.out.println( " 抓到超出陣列索引值的異常了 !!" ); } System.out.println( "try~catch 後的程式 " ); } } 執⾏結果 抓到轉型的異常了 !! try~catch 後的程式 class Main { public static void main( String [] args) { Employee [] es = new Employee [ 3 ]; es[ 0 ] = new Employee(); es[ 1 ] = new Employee(); es[ 2 ] = new Employee(); Person e1 = new Person (); //Employee e1 = new Employee(); try { e s[ 3 ] = ( Employee ) e1 ; } catch ( Exception e){ System.out.println( " 只要是異常, Exception 都是可以補抓到的 !!" ); } System.out.println( "try~catch 後的程式 " ); } } 執⾏結果 只要是異常, Exception 都是可以補抓到的 !! try~catch 後的程式 class Main { public static void main( String [] args) { Employee [] es = new Employee [ 3 ]; es[ 0 ] = new Employee(); es[ 1 ] = new Employee(); es[ 2 ] = new Employee(); Person e1 = new Person(); //Employee e1 = new Employee(); try { es[ 3 ] = (Employee)e1; } catch ( Exception e){ System.out.println( " 只要是異常, Exception 都是可以補抓到的 !!" ); } catch ( ClassCastException e){ System.out.println( " 抓到轉型的異常了 !!" ); } catch ( ArrayIndexOutOfBoundsException e){ System.out.println( " 抓到超出陣列索引值的異常了 !!" ); } } } 執⾏結果 Main.java:15: error: exception ClassCastException has already been caught }catch(ClassCastException e){ ^ Main.java:17: error: exception ArrayIndexOutOfBoundsException has already been caught }catch(ArrayIndexOutOfBoundsException e ^ 2 errors class Main { public static void main( String [] args) { Employee [] es = new Employee [ 3 ]; es[ 0 ] = new Employee(); es[ 1 ] = new Employee(); es[ 2 ] = new Employee(); Person e1 = new Person(); //Employee e1 = new Employee(); try { es[ 3 ] = (Employee)e1; } catch ( ClassCastException e){ System.out.println( " 抓到轉型的異常了 !!" ); } catch ( ArrayIndexOutOfBoundsException e){ System.out.println( " 抓到超出陣列索引值的異常了 !!" ); } catch ( Exception e){ System.out.println( " 只要是異常, Exception 都是可以補抓到的 !!" ); } } } class Main { public static void main( String [] args) { Employee [] es = new Employee [ 3 ]; es[ 0 ] = new Employee(); es[ 1 ] = new Employee(); es[ 2 ] = new Employee(); Person e1 = new Person(); try { es[ 3 ] = ( Employee ) e1 ; } catch ( ArrayIndexOutOfBoundsException e){ System.out.println( " 抓到超出陣列索引值的異常了 !!" ); } finally { System.out.println( " finally 保證程式⼀定會執⾏,即使這可能是您程到的最後執⾏的結果 ⋯ " ); } System.out.println( " try~catch 後的程式 " ); } } 錯誤 說明 AssertionError 當陳述式的 boolean 測試回傳是 false 的時 候發⽣。 ExceptionInInitializerError 嘗試初始靜態變數或在靜態區塊內發⽣錯 誤時所拋出的。 NoClassDefFoundError 嘗試執⾏⼀個沒有 main() ⽅法的類別檔。 StackOver f owError 通常是在⼀個⽅法遞迴地太深的時候發 ⽣。 ArithmeticException 算術錯誤時發⽣。例如除以 0 。 ClassCastException 轉型失敗時拋出的異常物件。 ArrayIndexOutOfBoundsException 嘗試使⽤不存在的索引值去存取⼀個陣列 時,所拋出的異常物件。 NumberFormatException 當⽅法轉換⼀個 String 成數字時,其所接放 的 String 卻不能如預期地進⾏轉換時發⽣。 IllegalStateException 當環境的狀態和正在嘗試進⾏的運算不相 符的時候拋出的。例如,使⽤已經關閉的 Scanner 。 NullPointerException 嘗試從變數中取出物件,但讓變數中只有 null 時發⽣。 EOFException 嘗試讀取檔案,但此檔已無資料可以讀取 時發⽣。 FileNotFoundException 開啟⼀個不存在的檔時發⽣。 InterruptedException 執⾏緒( Thread )被中斷時發⽣。 J9_1_1 – Person.java 4. 轉型失敗 !! J9_1_2 – Main.java 1. 這是⼈員類別( Person )。 J9_1_1 – Employee.java J9_1_1 – Main.java 2. 這是員⼯類別( Employee ),其繼承⾃⼈員類別,這表⽰ 員⼯物件可透過多型(參考第六章)轉型為⼈員型別的物件。 3. 但 … 在此建立的是⼈員物件。 4. 把非擁有員⼯型別的物件,向下轉型(即強迫轉型)為員⼯類別。 5. 即使在⼈員類別不能轉型成員⼯類別的情況下,編譯還是可以成功的。因 為我們已經透過向下轉型(即強迫轉型),來告訴編譯器⼀定可以轉型成功。 6. 但 … 執⾏時卻發現⼈員物件並不能轉型為員⼯物件,需發⽣ 轉型失敗的情況,如此程式即會當機,若不想讓程式當機⼜要 如何控制這個錯誤呢?或許告知使⽤此程式的⼈員⼀個特定訊 息,這個錯誤是我們控制範圍內的,如此也⾏! 7. 特別要注意的是當我們處理完這個異常後,在 try~catch 之後的 程式是會繼續執⾏下去的,在此會正常轉出⼈員名稱- Jack 。 5. catch 是我們捕抓的程式區塊,當成功捕抓後,就可以進⾏⼀ 些處理。現在我們的處理很單純,就是輸出⼀段訊息⽽已,告訴執 ⾏此程式的⼈員,這裡發⽣了 “ 轉型異常 ” 。 6. catch 後⾯有 ⼩括號?!沒關 係,先照著寫,等 會就會為您解釋這 是什麼。它寫起來 就像參數⼀樣。 3. 這⼀⾏程式曾經轉型失敗,⽽讓程式當機的⼀⾏程式,現在我們把 它將在屬於 try 的⼤括號({})中。意思是嘗試著去捕抓的意思。 2. 在此使⽤ try~catch 補抓轉型異常。 1. 注意,建立的是⼈員物件( Person )。 J9_1_2 – Main.java 1. 注意,建立的是⼈員物件( Person )。 2. try 程式區塊包含著可能產⽣異常的程式。 3. 轉型失敗,產⽣⼀個異常物件。 4. catch 即是⽤來捕抓此異常物 件的,在 catch 中,以參數型別- ClassCastException 比對這個 異常物件是否為此型別的物件。 5. 若是,則將這個物件接收,並放入接收變數 e 中。 Obj Obj J9_1_2 – Main.java 1. 在此建立⼀個不能轉型成員⼯型別 ( Employee )的⼈員物件( Person )。 2 . JVM 發現這是⼀個轉型失敗的問題。 3. 那就應該拿出代表此異常的類別- ClassCastException ,並建立這個類別 的物件,將它拋出去。 4. 但因為我們使⽤了 try~catch ,因此會去比對有 沒有可以補抓此轉型異常物件的 catch 項⽬(即 catch 參數中有 符合此異常物件的類別)。 5. 若比對成功則將這個物件接收, 並放入接收變數 e 中。 JVM Obj Obj J9_1_2 – Main.java 1. 在此建立⼀個不能轉型成員⼯型別 ( Employee )的⼈員物件( Person )。 2. 這裡剛好裝下 3 位⼈員物件。 3. 但 … 若執意放入第 4 個物件於索引位置 3 中,這時會因索引位置 3 是不存在的,當使⽤超過陣列的最⼤索引值位置時,將會發⽣異常 並拋出異常物件,如此⼜讓程式當機了。聰明的您看出端倪了嗎? 在發⽣異常時若不使⽤ try~catch 捕抓,則會這個異常物件列印 並顯⽰在畫⾯上,同時也會說明這個異常物件的類別名稱 ArrayIndexOutOfBoundsException 。那我們是否要對此類型 的異常作些什麼呢?好讓程式不要⼜當機了。 J9_1_4 – Main.java 1. 這個陣列最多只能夠裝下 3 位⼈員物件( Person )。 2. 這裡剛好裝下 3 位⼈員物件。 3. 在此已知使⽤不存在的索引位置 3 將會發⽣異常並拋出 ArrayIndexOutOfBoundsExceptions 類別型別的異常物件,在此類 別的名稱即可看出問題所在, “Array Index Out Of Bounds” 即說 明了陣列索引值超出了陣列 3 格空間的邊界,因此產⽣此異常。 4. 最後在 catch 中,即可透過 ArrayIndexOutOfBoundsExceptions 類別型別捕抓到這個異常物件,以進⾏後續異常的處理。 這個程式再⼀次的展現了不同的異常情況, Java 都會為我們準到⼀些表⽰異 常情況的類別,並在發現異常狀況時,依照異常的種類找出表⽰此異常的類 別,在建立異常物件後將異常物件拋出;此時,就可以使⽤ try~catch 將異 常捕抓起來並處理掉。 提 ⽰ Throwable Error Exception RuntimeException ClassCastException LinkageError VirtualMachineError StackOver f owError OutOfMemoryError ArithmeticException IndexOutOfBoundsException EOFException FileNotFoundException IOException InterruptedException NullPointerException IllegalArgumentException IllegalStateException ExceptionInInitializerError NoClassDefFoundError AssertionError ArrayIndexOutOfBoundsException NumberFormatException J9_1_5 – Main.java 1. 這個陣列最多只能夠裝下 3 位員⼯物件( Employee )。 2. 若建立⼈員物件,則會後續程式會發⽣ 轉型異常( ClassCastException )。 3. 但若改成此⾏程式,則會後續程式會發⽣陣列索引 異常( ArrayIndexOutOfBoundsException )。 4. 第1個可能發⽣轉型異常( ClassCastException ) 發⽣的位置,若 e1 放的是存⼈員物件的話。 5. 第2個可能陣列索引異常( ArrayIndexOutOfBoundsException ) 發⽣的位置。在此是⼀定會發⽣,因為索引值以超出 0~2 的範圍。 6. 這時只有⼀個 catch 捕抓陣列索引異常類別( ArrayIndexOutOfBoundsExceptions ) 的異常,這當然只能捕抓陣列索引位置錯誤的異常。 7. 但這時若先發⽣轉型異常呢?!這個轉型異常物件就未能成功捕抓,最 後發⽣程式當機。這時怎麼辦呢?有什麼好庵法可以同時處理多個異常呢? J9_1_6 – Main.java 1. 這個陣列最多只能夠裝下 3 位員⼯物件( Employee )。 2. 若建立⼈員物件,則會後續程式會發⽣ 轉型異常( ClassCastException )。 3. 但若改成此⾏程式,則會後續程式會發⽣陣列索引 異常( ArrayIndexOutOfBoundsException )。 4. 我們知道,這⾏程式有可能 發⽣兩種不同種類的異常。 5. 這時,使⽤2個 catch ,分別補抓不同的 異常物件-轉型異常與陣列索引異常。 6. 當異常發⽣時,就會直接使⽤ catch 來補抓異常物件。雖然 這⾏程式可能會發⽣兩個不同種類的異常,但只要異常⼀發 ⽣,就會跳到對應的 catch 去處理異常(只要異常發⽣就會⾺ 上離開 try 程式區塊),如此就不會發⽣程式當機的問題了。 J9_1_7 – Main.java 1. 這個陣列最多只能夠裝下 3 位員⼯物件( Employee )。 2. 若建立⼈員物件,則會後續程式會發⽣ 轉型異常( ClassCastException )。 3. 但若改成此⾏程式,則會後續程式會發⽣陣列索引 異常( ArrayIndexOutOfBoundsException )。 4. 不論這⾏程式會發⽣轉型異常( ClassCastException )或是陣列 索引異常( ArrayIndexOutOfBoundsException )類別的異常物件 … 5. Exception 皆可透過多型的⽅式捕抓異常物件。還記 得多型嗎!就是物件可以擁有多種型別,並可⾃動轉型為⽗ 類別的型別; try~catch 也可以使⽤多型的⽅式捕抓異常 物件的,⽽ Exception 即是所有異常的⽗類別。 6. 使⽤ Exception 捕抓所有異常物件的唯⼀缺點,就是您無法明確的區分 到底是哪⼀類的異常產⽣了(因為所有的異常物件都可以轉型為 Exception 型別),但若是您不在乎的話,即可以使⽤ Exception 抓取所有的異常。 J9_1_8 – Main.java 1. 若先在第 1 個 catch 中使⽤ Exception 異常類別捕抓異常。 2. 並將轉型異常( ClassCastException )或是陣列索引異常 ( ArrayIndexOutOfBoundsException )放於之後的 catch 中。 3. 這時,編譯錯誤竟然指出轉型異常( ClassCastException ) 和陣列索引異常( ArrayIndexOutOfBoundsException )已經被 捕抓了?!因此不能再使⽤ catch 捕抓,這是怎麼回事? J9_1_9 – Main.java 1. ClassCastException 和 ArrayIndexOutOfBoundsException 並沒有繼承 關係,因此沒有哪個異常類別必須先 catch 的問題。 2. 但 Exception 異常型別必須放在其他異常型別的後⾯。避免 所有異異常物件透透過多型的功能,⾃動轉型為 Exception ,造 成其他 catch 無法捕抓異常物件的問題。記得, catch 時有繼承關 係的⼦異常類別必須在⽗異常類別之前。 J9_1_10 – Main.java 1. 這此⾏程式會發⽣轉型異常。但⽬前只有⼀個 捕抓陣列索引異常的 catch 程式區塊,並沒有可以 捕抓這個轉型錯誤的 catch ,因此最後這⾏程式會 造成整個程式當機, JVM 將會顯⽰這個異常。 2. 但在程式當機之前, finally 程式區塊還是會完 成它的任務,放在 finally 程式區塊中的程式⼀定 會被執⾏,即便異常沒有成功被 catch 所捕抓。 3. 這⾏程式是不會被執⾏到的,因為異常物件 沒有成功被捕抓,因此造成整個程式當機了。 誤把⼈員轉型為員⼯,竟造成程式當掉了? 1 1 try~catch ,捕抓⼈員的轉型異常 2 1 2 1 異常,即是⼀個物件 偷偷將第 4 位⼈員放入只能將下 3 名⼈員的陣列 啊 ~~ ⼜當機了? 再次提醒,異常類別的名稱即說明了問題所在 1 1 異常 2 個不同的異常可能同時發⽣,怎麼辦? 多重 catch ,捕抓多重異常的可能 Exception ,以多型的⽅式捕抓異常物件 先補抓 Exception 異常,再補抓 ClassCastException 與 ArrayIndexOutOfBoundsException 異常,竟然編譯失敗了? 1 1 2 2 別讓⽗類別異常, 透過多型擋住其他異常被捕抓的可能 f nally ,⼀定執⾏的程式區塊 留言
留言
張貼留言