1. 程式人生 > >java異常處理 Exception、error、執行時異常和一般異常有何異同

java異常處理 Exception、error、執行時異常和一般異常有何異同

一、開場白

對於程式執行過程中的可能出現異常情況,java語言使用一種稱為異常處理的錯誤捕捉機制進行處理。相信大家對 try { }catch( ){} finally{} 這種結構非常熟悉,使用頻率極高。既然經常使用它,而且也是面試常問知識點,我們就有必要去深入地瞭解一下。也談不上深入,只是java語言的基本功。下面,開始吧!

二、異常分類

在java中,異常物件都是派生於Throwable類的一個例項。如果java內建的異常類不能夠滿足需求,使用者還可以建立自己的異常類。

下圖是java異常類層次結構圖
這裡寫圖片描述
可以看出,所有的異常都是由Throwable類,下一層分解為兩個分支:Error

Exceprion
Error層次結構描述了java執行時系統的內部錯誤和資源耗盡錯誤。大多數錯誤與程式碼編寫者執行的操作無關,而表示程式碼執行時 JVM(Java 虛擬機器)出現的問題。應用程式不應該丟擲這種型別的物件。
Exceprion這個層次結構又分解為連個分支:一個分支派生於RuntimeException;另一個分支包含其他異常。劃分兩個分支的規則是:由程式錯誤導致的異常屬於RuntimeException;而程式本身沒有沒有問題,但由於像I/O錯誤這類異常導致的異常屬於其他異常。
常見的RuntimeException(執行時異常):
IndexOutOfBoundsException(下標越界異常)
NullPointerException(空指標異常)
NumberFormatException (String轉換為指定的數字型別異常)
ArithmeticException -(算術運算異常 如除數為0)
ArrayStoreException - (向陣列中存放與宣告型別不相容物件異常)
SecurityException -(安全異常)
IOException(其他異常)

FileNotFoundException(檔案未找到異常。)
IOException(操作輸入流和輸出流時可能出現的異常。)
EOFException (檔案已結束異常)

三、概念理解

首先明白下面的兩個概念
unchecked exception(非檢查異常):包括執行時異常(RuntimeException)和派生於Error類的異常。對於執行時異常,java編譯器不要求必須進行異常捕獲處理或者丟擲宣告,由程式設計師自行決定。
checked exception(檢查異常,編譯異常,必須要處理的異常)
也:稱非執行時異常(執行時異常以外的異常就是非執行時異常),java編譯器強制程式設計師必須進行捕獲處理,比如常見的IOExeption和SQLException。對於非執行時異常如果不進行捕獲或者丟擲宣告處理,編譯都不會通過。

四、異常的處理

(1)、丟擲異常

1、呼叫一個丟擲受查異常的方法必須用throws 子句宣告 呼叫method2()方法。
2、程式執行過程中發現錯誤,並且利用throw丟擲一個受查異常 下面method2()方法。

    @Test
    public void test() throws FileNotFoundException {

        method();
    }
    public void method() throws FileNotFoundException {
        //一個會丟擲異常的方法
        method2();
    }

    //這裡 方法後是throws 
    public void method2() throws FileNotFoundException {
        //這裡是throw 
        throw new FileNotFoundException();
    }

(2)、捕獲異常
try { }catch( ){} finally{} 語句塊這就比較常見了。不在贅述。
不過下面有一道有意思的題,實際使用中不太會遇見,面試題常見。
來,看題!

       @Test
    public void test()  {

       System.out.println(test11());
    }


    public  String test11() {
        try {
            System.out.println("try block");

            return test12();
        } finally {
            System.out.println("finally block");
        }
    }

    public static String test12() {
        System.out.println("return statement");

        return "after return";
    }

答案:
try block
return statement
finally block
after return

    @Test
    public void test()  {

       System.out.println(test2());
    }


    public  int test() {
        int b = 20;

        try {
            System.out.println("try block");

            return b += 80;
        } catch (Exception e) {

            System.out.println("catch block");
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }

            return 200;
        }
    }

答案:
try block
finally block
b>25, b = 100
200

總結:finally塊的語句在try或catch中的return語句執行之後返回之前執行且finally裡的修改語句可能影響也可能不影響try或catch中 return已經確定的返回值,若finally裡也有return語句則覆蓋try或catch中的return語句直接返回。

五、實際開發中常用的一個模式

(1)、定義業務中出現的異常

分別是郵箱未註冊異常,驗證使用者資訊異常和驗證密碼異常
這裡寫圖片描述

(2)、模擬業務點會丟擲這些異常,寫一個UserService

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    public User getUserByUserId(int userId) throws EmailNotRegisterException, InvalidPasswordException, InvalidLoginInfoException {
        if(userId==0) throw new EmailNotRegisterException("郵箱沒有註冊");
        if(userId==-1) throw new InvalidLoginInfoException("賬號不存在");
        if(userId==-2) throw new InvalidPasswordException("密碼錯誤");
        return userRepository.findUserByUserId(userId);
    }
}

(3)在Controller層捕獲處理這些異常

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping(method = RequestMethod.GET)
    public ResponseEntity getUser() {
        User user= null;
        try {
            user = userService.getUserByUserId(1);
        } catch (EmailNotRegisterException e) {
            //TODO 做郵箱未註冊的處理
            ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
        } catch (InvalidPasswordException e) {
            //TODO 做驗證密碼失敗的處理
            ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
        } catch (InvalidLoginInfoException e) {
            //TODO 做驗證賬號失敗的處理
            ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage());
        }
        return  ResponseEntity.ok().body(user);
    }
}