Java異常總結
一、異常的概述
異常定義:在程式中,發生“不正常”的事件,導致程式無法正常執行,並使JVM中斷,稱為異常
生活中的異常:早上起床上課,平時騎車20分鐘可以到達教室,由於天氣原因或鬧鐘響了自動關閉,不能按時到達教室上課,遲到了,此時就屬於異常現象。
捕獲異常:當程式在執行時,發生了異常,為了讓程式正常執行,需要對異常捕獲(catch),稱之為捕獲異常
Java是面向物件的語言,異常本身就是一個類(Exception),當發生異常時會建立異常物件,捕獲的就是該物件。
System.out.println("請輸入一個數字"); Scanner sc = new Scanner(System.in); // 對可能發生的異常 進行處理 int num = sc.nextInt(); if(num%2==0){ System.out.println("這個數是偶數"); }
以上程式碼可能發生異常,當用戶輸入非數字時,導致程式丟擲一個異常物件:
Exception in thread "main" java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
二、異常捕獲(異常關鍵字以及層次關係)
a、try:試一試,將可能發生的程式碼使用try包裹,try不能單獨出現
b、catch:捕獲異常,當發生指定的異常物件時,執行catch程式碼
System.out.println("請輸入一個數字"); Scanner sc = new Scanner(System.in); // 對可能發生的異常 進行處理 try { int num = sc.nextInt(); // 發生異常後,try裡面的程式碼不再執行 if (num % 2 == 0) { System.out.println("這個數是偶數"); } System.out.println("結束"); }catch(Exception ee){// 對應的異常類 來捕獲對應的異常物件 ,不能確定異常類,可以使用父類Exception System.out.println("你的輸入不正確"); } System.out.println("程式繼續執行直到結束。。。。");
一個try+多個catch
// 丟擲的異常 不能被catch捕獲,會發生什麼? try { int[] num = {1, 2, 3}; System.out.println(num[1]); // 沒有捕獲該異常物件,JVM依然終止執行 System.out.println(10/0); }catch(NullPointerException ee){ System.out.println("這是空指標異常"); }catch(ArrayIndexOutOfBoundsException ee){ System.out.println("陣列下標越界異常"); }catch(Exception ee){ // 輸出異常 堆疊訊息 方便程式設計師排錯(儘可能避免使用者看見) ee.printStackTrace(); System.out.println("系統繁忙!"+ee.getMessage()); } System.out.println("程式結束");
c、finally:異常之後的最終處理(無論是否發生異常,程式都執行)
try......finally結構
try{
System.out.println("請輸入兩個數 ,計算兩個數相除");
Scanner sc = new Scanner(System.in);
int num1 = sc.nextInt();
int num2 = sc.nextInt();
double s = num1/num2; // 可能出錯
System.out.println(" try裡面結束,結果:"+s);
}finally{
System.out.println("無論是否發生異常,都會執行這個語句塊,一般用於資源回收");
}
try...catch...finaally結構
try {
System.out.println("請輸入兩個數 ,計算兩個數相除");
Scanner sc = new Scanner(System.in);
int num1 = sc.nextInt();
int num2 = sc.nextInt();
double s = num1 / num2; // 可能出錯
System.out.println(" try裡面結束,結果:" + s);
}catch(ArithmeticException ee){
ee.printStackTrace();
System.out.println("除數不能為0 !!");
}catch(Exception ee){
ee.printStackTrace();
System.out.println("系統繁忙!!!");
}finally {
System.out.println("用於資源回收。");
}
三、丟擲異常
/**
* 根據下標訪問陣列元素
* @param array
* @param index
* @return
*/
public static int getEleByIndex(int [] array , int index){
// 丟擲異常: 可以在異常發生時 或發生之前 建立一個異常物件並丟擲
// 手動丟擲一個異常 throw new 異常類([異常訊息]);
if(index <0 || index > array.length-1){
//丟擲異常
throw new ArrayIndexOutOfBoundsException("你的下標越界了");
}
int n = array[index];
return n;
}
public static void main(String[] args) {
//陣列
int [] array = {2,1,4,5};
int index=4;
// 定義方法訪問下標的元素 此時會產生異常 並丟擲給方法的呼叫者
try {
int num = getEleByIndex(array, index);
System.out.println("訪問的元素:" + num);
}catch(ArrayIndexOutOfBoundsException ee){
System.out.println(ee.getMessage());
}
System.out.println("結束。。。");
}
四、異常分類
由於有些異常時不能直接丟擲的,需要先宣告才可以丟擲,異常可以分為兩大類:
1、編譯期異常(check異常或者檢查異常):在編譯期間檢查異常,如果沒有處理異常,則編譯出錯。
//建立一個檔案類的物件
File file = new File("d:/aaa.txt");
// 在寫程式碼(編譯之前)時 一定要處理的異常(try..catch 或者 throws),就是編譯時異常
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
這裡的IOException就是編譯期異常,需要手動處理的
2、執行期異常(runtime異常或者執行異常):在執行期間檢查異常,編譯期可以不處理異常。
// 在執行期間丟擲異常 不需要事先處理的 NullPointException是執行異常
String str=null;
System.out.println(str.length());
Exception中常用的異常類
- RuntimeException
- ArrayIndexOutOfBoundsException:陣列下標越界異常
- NullPointerException:空指標異常
- ArithmeticException:算數異常
- NumberFormatException:數字格式化異常
- ClassNotFoundException:類沒有找到異常
- ClassCastException:類轉換異常
- 檢查異常(check Exception)
- IOException:IO操作異常
- FileNotFoundException:檔案未找到異常
- SQLException:資料庫異常
- EOFEXception:讀寫檔案尾異常
- ParseException:日期格式化異常
- SocketException:資料傳輸異常
注意:對於丟擲檢查異常,需要使用throws宣告,對於丟擲執行時異常,必須要使用throws宣告
宣告丟擲異常語法:
宣告丟擲異常語法:
public ... 方法名([引數]) throws 異常類1,異常類2{
//通過throws丟擲 或 處理 檢查異常
}
/**
* 宣告丟擲異常語法:
* public ... 方法名([引數]) throws 異常類1,異常類2{
*
* }
*/
//建立檔案
public static void createFile() throws FileNotFoundException ,IOException {
File file = new File("d:/hello.txt");
if(file.exists()){
// 不能建立 ,需要提示使用者 該檔案存在
throw new FileNotFoundException("這個檔案已存在,不能建立");
}else{
//建立
file.createNewFile();
}
}
throws用於進行異常類的宣告,若該方法可能有多種異常情況產生,那麼在throws後面可以寫多個異常類,用逗號隔開。
面試題:關於finally和return的執行順序問題?
回答:當方法有返回值時,先執行finally,再return,但是finally的程式碼不會改變return結果
/**
* 方法有返回值 有 finally
* @param n
* @return
*/
public static int getNum(int n){
try{
if(n%2==0){
n++;
}else{
n--;
}
return n;
}catch(Exception ee){
System.out.println("catch--"+n);
return 0;
}finally {
// return 如果放在 try或catch中,不會受finally的改變
// 如果放在最下面,會受finally的改變
n++; // 5
System.out.println("fially----n:" + n); // 5
}
}
public static void main(String[] args) {
int m=getNum(5);
System.out.println(m);
}
結果返回
finally----n:5
4
當只有在try或者catch中呼叫退出JVM的相關方法,此時finally才不會執行,否則finally永遠會執行。
異常注意事項
a、執行時異常被丟擲可以不處理。即不捕獲也不宣告丟擲。
b、如果finally有return語句,永遠返回finally中的結果,避免該情況.
c、如果父類丟擲了多個異常,子類重寫父類方法時,丟擲和父類相同的異常或者是父類異常的子類或者不丟擲異
常。
d、父類方法沒有丟擲異常,子類重寫父類該方法時也不可丟擲異常。此時子類產生該異常,只能捕獲處理,不
能宣告丟擲
五、自定義異常
5.1、為什麼需要使用自定義異常
在Java中每一個異常類都表示特定的異常型別,例如NullPointerException表示空指標,ArithmeticException表示算數異常,但是sun公司提供的API中不可能將實際專案中的業務問題全部定義為已知的異常類,這時需要程式設計師根據業務需求來定製異常類,例如使用者註冊,可以定義使用者註冊異常(RegisterException),分數不可能為負數也可以定製異常(ScoreException)。
5.2、什麼是自定義異常
在開發中根據自己的業務情況來定製異常類,靈活性較高,且方便易用。
5.3、如何實現自定義異常
a、定義編譯期異常類,建立一個類繼承java.lang.Exception;
b、定義執行期異常類,建立一個類繼承java.lang.RuntimeException;
5.4、案例分析:自定義異常應用
要求:模擬使用者註冊操作,使用者輸入使用者名稱,驗證使用者名稱是否存在,如果存在,則丟擲一個異常訊息“親,該使用者已存在,不能註冊”,通過自定義異常提示訊息
public class RegisterException extends Exception {
public RegisterException(){
}
public RegisterException(String message){
// 將message 賦值給父類的構造
super(message); // 將message賦值給父類的 屬性,可通過getMessage()方法
}
}
public class TestRegister {
// 模擬已存在的使用者
String [] users = {"袁魏巍","王麻子","王小花"};
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("請輸入你要註冊的使用者:");
String uname = sc.next();
TestRegister obj = new TestRegister();
try {
// 呼叫方法
obj.checkUserName(uname);
System.out.println("註冊成功");
} catch (RegisterException e) {
System.out.println("註冊失敗");
System.out.println(e.getMessage());
}
}
/**
* 檢查使用者是否存在
* @param username
* @return true 表示通過
* 異常表示不通過
*/
public boolean checkUserName(String username) throws RegisterException{
// 使用foreach遍歷
/**
* for(資料型別 變數名 : 陣列名/集合名 ){
* 迴圈中的 變數名代表的就是陣列的元素
* }
*/
for(String u : users){
// 判斷u是否與 username相等 ,相等說明使用者存在,需要丟擲異常
if(u.equals(username)){
throw new RegisterException("親,"+username+" 已存在,不能註冊");
}
}
return true;
}
}