Java - 异常

一、Java异常概述

1. 异常简介

  • Java异常时Java提供的一种识别以及响应错误的一致性机制。
  • 异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。
  • 在有效使用异常的情况下,能够解决异常的三个问题
    • where --> 异常是在哪抛出的
    • what --> 什么异常被抛出
    • why --> 异常为什么会抛出

2. Java异常架构

Java - 异常_第1张图片

(1) Throwable(父类)

  • Throwable是所有错误和异常的父类
  • Throwable包含两个子类:Error(错误)和Exception(异常)

(2) Error(错误)

  • Error及其子类;程序运行中无法处理的错误,表示出现了严重的错误
  • 这些错误是不受检异常,非代码性错误。因此当此类错误发生时,应用程序不应该去处理此类错误。

(3) Exception(异常)

  • 程序本身可以捕获并且可以处理的异常。
  • Exception这种异常又分为两类
    • 编译时异常

      1. Exception中除RuntimeException及其子类之外的异常
      2. Java编译器会检测此类异常;如果检测到,要么通过throws进行声明抛出,要么通过try/catch代码块捕获处理,否则不能通过编译。
      3. 常见:ClassNotFoundException、IOException
    • 运行时异常

      1. RuntimeException类及其子类,表示在运行期间可能出现的异常
      2. Java编译器不会检查它,可以编译通过(一般是由逻辑错误引起的);但我们也可以通过throws进行声明抛出,也可以通过try/catch块进行捕获。
      3. 常见:NullPointerException、ClassCastException、ArrayIndexOutBandException

3. 受检异常与非受检异常

是否受检是一个宏观的概念,其对象包含了Throwable下的所有,也就是还会包含Error

  • 受检异常:
    • 编译器要求必须处理的异常
    • Exception中除了RuntimeException及其子类的其他异常,都是受检异常
  • 非受检异常:
    • 编译器不会进行检查并且不要求处理的异常;
    • 该类异常包括运行时异常(RuntimeException及其子类)和错误(Error)

二、异常处理方法

1. 声明异常(throws)

  • 通常,我们对知道如何处理的异常 --> 进行捕获处理,对不知道如何处理的异常 --> 继续传递下去
  • 在方法签名处可以使用throws关键字声明可能抛出的异常,并且将该异常传递给方法调用者处理
  • 非受检异常不可使用throws关键字抛出

2. 抛出异常(throw)

  • 如果一个异常无法解决,但又不需要方法调用者处理,可以通过throw关键字直接抛出异常
  • 代码进行到throw,之后的代码不会再执行

3. 捕获异常(try/catch)

  • 可以用try/catch代码块捕获并且处理异常
    Java - 异常_第2张图片

三、常见问题

1. JVM如何处理异常

  • JVM会沿着调用栈查找是否有可以处理异常的代码,如果有则使用;如果没有,JVM将异常传递给默认的异常处理器(为JVM的一部分),默认异常处理器打印出异常信息并且中止程序。

2. NoClassDefFoundError 和 ClassNotFoundException 区别

  • NoClassDefFoundError 是一个 Error 类型的异常,是由 JVM 引起的,不应该尝试捕获这个异常。
    引起该异常的原因是 JVM 或 ClassLoader 尝试加载某类时在内存中找不到该类的定义,该动作发生在运行期间,即编译时该类存在,但是在运行时却找不到了,可能是变异后被删除了等原因导致

  • ClassNotFoundException是一个受检异常,需要显示的使用try/catch或者throws等对其进行处理。
    当使用Class.forName、ClassLoader.loadClass等动态加载类到内存时,如果在指定的路径下没有找到该类,就会抛出异常。

3. try/catch代码块中的return问题

  • 首先,finally代码块中的代码一定会执行
  • 如果catch中有return,会先执行finally代码块中的代码,最后返回catch中return
  • 如果catch和finally中都有return,在finally中就会return,覆盖掉catch中的return
  • 在catch中执行return之前,需要被返回的值已经确定,如果finally中更改了返回值,return的值不会发生改变
public static int getInt() {
    int a = 10;
    try {
        System.out.println(a / 0);
        a = 20;
    } catch (ArithmeticException e) {
        a = 30;
        return a;
        /*
         * return a 在程序执行到这一步的时候,这里不是return a 而是 return 30;这个返回路径就形成了
         * 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40
         * 再次回到以前的路径,继续走return 30,形成返回路径之后,这里的a就不是a变量了,而是常量30
         */
    } finally {
        a = 40;
    }
	return a;
}

4. try/catch/finally中哪个部分可以被省略

  • catch可以省略
  • 原因:
    • try --> 处理 运行时异常
    • try/catch --> 处理 运行时异常 + 普通异常
    • 编译器规定,普通异常如果选择捕获,必须使用catch显示声明以便进一步处理
    • 而运行时异常在编译时没有此规定,所以catch可以省略

你可能感兴趣的:(java)