Java基础学习 — 异常

134 阅读6分钟

学习B站动力节点视频:www.bilibili.com/video/BV1a5…

1、异常继承结构

image-20240702010017034.png

  • 所有的 异常(Exception)和 错误(Error)都是可抛出的。都继承了 Throwable 类。
  • Error是无法处理的,出现后只有一个结果:JVM终止
  • Exception是可以处理的。
  • Exception的分类:
    • 所有的RuntimeException的子类:运行时异常/未检查异常(UncheckedException)/非受控异常
    • Exception的子类(除RuntimeException之外):编译时异常/检查异常(CheckedException)/受控异常
  • 编译时异常和运行时异常区别
    • 编译时异常特点:在编译阶段必须提前处理,如果不处理编译器报错。
    • 运行时异常特点:在编译阶段可以选择处理,也可以不处理,没有硬性要求。
    • 编译时异常一般是由外部环境或外在条件引起的,如网络故障、磁盘空间不足、文件找不到等。作用在于提醒程序员检查本地信息。
    • 运行时异常一般是由程序员的错误引起的,并且不需要强制进行异常处理
  • 注意:编译时异常并不是在编译阶段发生的异常,所有的异常发生都是在运行阶段的,因为每个异常发生都是会new异常对象的,new异常对象只能在运行阶段完成。那为什么叫做编译时异常呢?这是因为这种异常必须在编译阶段提前预处理,如果不处理编译器报错,因此而得名编译时异常。

2、自定义异常

  • 编写异常类继承 Exception(编译时异常)/ RuntimeException(运行时异常)
  • 提供一个无参数构造方法,再提供一个带 String msg 参数的构造方法,在构造方法中调用父类的构造方法。
// 自定义异常
public class IllegalAgeException extends Exception{

    public IllegalAgeException() {
    }

    public IllegalAgeException(String message) {
        super(message);
    }
}

// 使用:  throw 抛出异常
class Test {
    public static void main(String[] args) {
        int age = 10;
        if (age <= 18) {
            throw new IllegalArgumentException("不合法的年龄!");
        }
    }
}

3、异常处理的两种方式

  • 声明异常:类似于推卸责任的处理方式

    在方法定义时使用 throws 关键字声明异常,告知调用者,调用这个方法可能会出现异常。这种处理方式的态度是:如果出现了异常则会 抛给调用者 来处理。

  • 捕捉异常:真正处理捕捉异常

    在可能出现异常的代码上使用 try..catch 进行捕捉处理。

    这种处理方式的态度是:把异常抓住。其它方法如果调用这个方法,对于调用者来说是不知道这个异常发生的。因为这个异常被抓住并处理掉了。

  • 异常在处理的整个过程中应该是:声明和捕捉联合使用。

    如果异常发生后需要调用者来处理的,需要调用者知道的,则采用声明方式。否则采用捕捉。

3.1、声明异常

  • 使用 throws 关键字 (s表示复数,即后面可以抛出多种异常,抛给调用者)

    eg: public void m() throws AException, BException... {}

  • 如果 AException 和 BException 都继承了 XException,那么也可以这样写:

    eg: public void m() throws XException {}

  • 调用者在调用m()方法时,编译器会检测到该方法上用 throws 声明了异常,表示可能会抛出异常,编译器会继续检测该异常是否为编译时异常,如果为编译时异常则必须在编译阶段进行处理,如果不处理编译器就会报错。

  • 如果所有位置都采用 throws ,包括main方法的处理态度也是throws,如果运行时出现了异常,最终异常是抛给了main方法的调用者(JVM)。JVM的处理方式只是在控制台打印异常信息,然后 JVM则会终止程序的执行

  • 发生异常后,在发生异常的位置上,往下的代码是不会执行的,除非进行了异常的捕捉。

3.2、捕捉异常

  • 如果一个异常发生后,不需要调用者知道,也不需要调用者来处理,选择使用捕捉方式处理。
  try {
      // 尝试执行可能会出现异常的代码
      // try块中的代码如果执行出现异常,出现异常的位置往下的代码是不会执行的,直接进入catch块执行
      // 如果出现了A类型的异常,底层会 new AException() 赋值给 e
  } catch (AException e) {
      // 如果捕捉到 AException类型的异常,在这里处理
  } catch (BException e) {
      // 如果捕捉到 BException类型的异常,在这里处理
  } catch (XException e) {
      // 如果捕捉到 XException类型的异常,在这里处理
  }
  • catch可以写多个。并且遵循自上而下,从小到大

  • Java7新特性:catch后面小括号中可以编写多个异常,使用运算符“|”隔开。

 try {
 
 } catch (AException | BException e) {
    
 }

4、异常的常用方法

Throwable 的方法:

方法名说明
public String getMessage()返回此 throwable 的详细信息
public String toString()返回抛出的简短描述
public void printStackTrace()异常错误信息输出在控制台

5、finally 语句块

  • finally语句块中的代码是一定会执行的。
  • finally语句块不能单独使用,至少需要配合try语句块一起使用:
    • try ... finally
    • try ... catch ... finally
  • 通常在finally语句块中完成资源的释放
    • 资源释放的工作比较重要,如果资源没有释放会一直占用内存。
    • 为了保证资源的关闭,也就是说:不管程序是否出现异常,关闭资源的代码一定要保证执行。
  • final、finally、finalize分别是什么?
    • final是一个关键字,修饰的类无法继承,修饰的方法无法覆盖,修饰的变量不能修改。
    • finally是一个关键字,和try一起使用,finally语句块中的代码一定会执行。
    • finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,Java 中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

6、异常面试题

  • 第一题
public class Demo {
    public static void main(String[] args) {
        System.out.println(m1());// 2
    }

    public static int m1() {
        String str = null;
        try {
            int l = str.length(); // NullPointerException
            System.out.println(l);
        } catch (NullPointerException e) {
            return 1;
        } finally { // 一定会执行
            return 2;
        }
    }
}
  • 第二题
public class Demo {
    public static void main(String[] args) {
        System.out.println(m1());// 3
    }

    public static int m1() {
        String str = null;
        int i = 0;
        try {
            i++; // i=1
            int l = str.length(); // NullPointerException
            System.out.println(l);
        } catch (NullPointerException e) {
            return ++i; // i=2
        } finally { // 一定会执行
            return ++i; // i=3
        }
    }
}
  • 第三题
public class Demo {
    public static void main(String[] args) {
        System.out.println(m1());// 2
    }

    public static int m1() {
        String str = null;
        int i = 0;
        try {
            i++; // i=1
            int l = str.length(); // NullPointerException
            System.out.println(l);
            return 1;
        } catch (NullPointerException e) {
            return ++i; // i=2 临时保存返回值temp = 2
        } finally { // 一定会执行
            ++i; // i=3
            System.out.println("i=" + i); // i=3
        }
    }
}