引言

本篇博文为 Java 异常处理的常见概念及相关细节梳理,意在重学 Java 查漏补缺。
博文随时会进行更新,补充新的内容并修正错漏,该系列博文旨在帮助自己巩固扎实 Java 基础。
毕竟万丈高楼,基础为重,借此督促自己时常温习回顾。

异常概述

异常:在 Java 语言中,将程序执行中发生的不正常情况称为 “异常”。(开发过程中的语法错误和逻辑错误不是异常)

Java 程序在执行过程中所发生的异常事件可分为两类:

  • Error:Java 虚拟机无法解决的严重问题。
    • 如:JVM 系统内部错误、资源耗尽等严重情况。
    • 比如:StackOverflowError(栈溢出) 和 OOM(OutOfMemoryError 堆溢出)
    • 一般不编写针对性的代码进行处理
  • Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理,例如:
    • 空指针访问
    • 试图读取不存在的文件
    • 网络连接中断
    • 数组下标(索引)越界

对于这些错误,一般有两种解决方法:

  • 遇到错误就终止程序的运行
  • 在编写程序时,考虑到错误的检测、错误消息的提示以及错误的处理

异常分类

捕获错误最理想的是在编译期间,但有些错误只有在运行时才会发生,比如:

  • 除数为零(一个数除以 0)
  • 数组下标越界

分类为:编译时异常、运行时异常

编译时异常(受检异常 checked)

  • Throwable
    • Exception
      • IOException
        • EOFException
        • FileNotFoundException
        • MalformedURLException
        • UnknownHostException
      • ClassNotFoundException
      • CloneNotSupportedException

运行时异常(非受检异常 unchecked)

Error

  • RuntiomException
    • ArithmeticException
    • ClassCastException
    • IllegalArgumentException
    • IllegalStateException
    • IndexOutOfBoundsException
    • NoSuchElementException
    • NullPointerException

常见异常

java.lang.Throwable
|----java.lang.Error: 一般不编写针对性的代码进行处理
|----java.lang.Exception: 可以进行异常的处理
|----编译时异常(checked)
|----IOException
|----FileNotFoundException
|----ClassNotFoundException
|----运行时异常(unchecked)
|----NullPointerException(空指针异常:引用变量指向 null 使用引用变量时触发)
|----ArrayIndexOutOfBoundsException(数组下标越界: array[array.length])
|----StringIndexOutOfBoundsException(String 下标越界)
|----ClassCastException(类型转换异常: Object obj = new Date();String str = (String)obj;)
|----NumberFormatException(数值格式化异常: String str = "a";int num = Integer.parseInt(str);)
|----InputMismatchException(输入不匹配异常:Scanner input = Scanner(System.in);input.nextInt();此时键盘键入 a 触发)
|----ArithmeticException(算数异常: int result = 1/0;)

异常处理

异常的处理:“抓抛模型”

  • “抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出。
    一旦抛出对象后,其后的代码就不再继续执行
  • “抓”:可以理解为异常的处理方式
    • try-catch-finally
    • throws

Java 程序的执行过程中如出现异常,会生成一个异常类对象,该异常类对象将被交给 Java 运行时系统,这个过程称为抛出(throw)异常

异常对象的生成:

  • 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出 —— 自动抛出
  • 由开发人员手动创建:Exception exception = new ClassCastException(); —— 创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样

try-catch-finally

格式:

try {
// 可能出现异常的代码
} catch(异常类型1 变量名1) {
// 处理异常的方式1
} catch(异常类型2 变量名2) {
// 处理异常的方式2
}
...
finally {
// 一定会执行的代码
}

说明:

  • finally 是可选的
  • 使用 try 将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个异常类的对象,根据此对象的类型,去 catch 中进行匹配
  • 一旦 try 中的异常对象匹配到某一个 catch 时,就进入 catch 中进行异常的处理。一旦处理完成就跳出当前的 try-catch 结构(在没有 finally 的情况)。继续执行其后的代码
  • catch 中的异常类型如果没有子、父类关系,则声明顺序随意
    catch 中的异常类型如果满足子、父类关系,则要求子类一定声明在父类之前,否则编译报错
  • 常用的异常对象处理的方式:
    • String getMessage():打印异常信息
    • printStackTrace():打印堆栈
  • 在 try 结构中声明的变量,在出了 try 结构后不能再被调用
  • try-catch-finally 结构可以嵌套

注意:使用 try-catch-finally 处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错。相当于使用 try-catch-finally 将一个编译时可能出现的异常延迟到运行时出现

throws + 异常类型

  • “throws + 异常类型” 写在方法的声明处。指明此方法执行时,可能会抛出的异常类型
    一旦方法执行时出现异常,仍会在异常代码处生成一个异常类对象,此对象满足 throws 后的异常类型时,就会被抛出。异常代码后续的代码不再继续执行
  • try-catch-finally 方式真正的将异常进行了处理
    throws 方式只是将异常抛给了方法的调用者,并没有真正将异常进行处理

try-catch-finally 与 throws 的使用场景

开发中根据不同场景选用不同的处理方式

  • 如果父类中被重写的方法没有 throws 方式处理异常,则子类重写的方法也不能使用 throws,意味着如果子类重写的方法中若涉及异常,必须使用 try-catch-finally 方式处理
  • 要执行的方法中,先后又调用了另外的几个方法,这几个方法是通过递进关系执行的,这时建议这几个方法使用 throws 方式进行处理;而这个要执行的方法可以考虑使用 try-catch-finally 方式进行处理

手动抛出异常

格式:throw new 异常类构造器(参数);

throw 和 throws 的区别:

  • throw 表示抛出一个异常类的对象,生成异常类的过程。声明在方法体内
  • throws 属于异常处理的一种方式,声明在方法的声明处

自定义异常类

自定义异常的方式:

  • 继承于现有的异常类:RuntimeException、Exception
  • 提供全局常量:serialVersionUID:(唯一标识)
  • 提供重载的构造器