Java异常相关内容

219 阅读5分钟

异常体系

这里列出了一些常见的异常,不代表所有异常的真实体系~

Throwable

Java中所有异常的的父类,都继承于Throwable ErrorException是异常的两个大类

常用方法

String getMessage();    //返回Throwable简短信息
String toString();      //返回Throwable详细信息
void printStackTrace(); //jvm打印异常对象默认调用此方法(异常信息、异常原因、异常位置)

Error

  • 系统性的错误。程序不能处理、捕获的异常。一般需要修改源代码才可能修复
  • 常见的错误:
    • JVM中堆和方法区内存溢出:OutOfMemoryError
    • JVM中虚拟机栈、本地方法栈内存溢出:StackOverflowError

Exception

  • 程序的异常,分为运行期异常和非运行期异常

运行期异常 RunTimeException

  • Exception的一个子类
  • 运行期异常不用处理默认交给JVM处理
  • 常见的运行期异常
    • 空指针异常:NullPointerException
    • 索引越位异常(字符串、数组、集合):IndexOutOfBoundsException

非运行期异常

  • 程序必须处理的异常,不处理不能通过编译
  • 做法:
    • 捕获:try...catch...finally
    • 抛出:throws...
  • 常见的非运行期异常
    • IO异常:IOException
    • 文件找不到异常:FileNotFoundException
    • 类找不到异常:ClassNotFoundException

发生异常时的大致流程

  1. JVM根据异常产生的原因创建一个异常对象,这个异常对象包含了异常产生的内容、原因、位置
  2. 若方法中没有异常处理逻辑(try...catch),则把对象抛给方法调用者来处理
  3. 若最终抛到main方法都没有异常处理逻辑,那么这个异常最终会落到JVM上,JVM会做两件事:
    1. 把异常对象(内容、原因、位置)以红色的字体打印到控制台上
    2. JVM会终止当前执行的程序,也叫中断处理

异常相关的5个关键字

  • trycatchfinally
    • 含义:捕获异常
    • 格式:
      try {
          //可能产生异常的代码
      } catch(异常变量) {
          //异常的处理逻辑
      } catch(异常变量) {
          //catch块至少要有一个
      } finally {
          //逻辑处理
      }
      
    • 如果catch代码块中的异常变量存在父子关系,则可以只捕获父类异常。如果不这么做,必须把子类异常放到上面,父类异常放到下面,这样可以在子类异常中做特殊处理,否则报错。
    • finally代码块不可以单独使用;常用于资源释放(io、数据库连接),这些资源占用cpu的资源,无论是否出现异常都需要资源释放。
  • throw
    • 含义:抛出异常
    • 必须写在方法内部
    • 抛出的必须是Exception或Exception的子类
    • 构造方法
      • new XXXException(String message) 仅抛出异常信息
      • new XXXException(String message, Throwable cause) 异常信息+异常原因
  • throws
    • 含义:声明异常
    • 必须写在方法声明处
    • 抛出的必须是Exception或Exception的子类
    • 方法内部抛出了多个异常,那么必须throws多个异常,如果这些异常(编译期异常)有父子关系,则只需声明父类异常
    • 如果父类抛出了多个异常,子类重写父类方法时,可以:
      • 抛出和父类相同的异常
      • 可以抛出父类异常的子类
      • 可以不抛出任何异常
    • 如果父类没有抛出异常,子类重写父类方法时,内部有异常,不允许声明抛出,只能内部处理

try...catch...finally代码块

  • 无论是否有异常;抑或是try、catch代码块中有return语句,finally都会执行
  • 如果try、catch代码块有return,finally代码块没有return。执行到return时(加入return中有运算,也会执行),先把结果保存起来,不管finally中怎么改变这个值,都不会影响这个结果,基本数据类型、引用类型都是如此。所以这时返回的结果会在finally前确定好。
  • 如果finally中有return语句,永远返回finally中的结果(覆盖前面的结果)

几种组合情况:

  • 情况一 try { } catch{ } finally { } return value;
    • 显然按照正常顺序执行
  • 情况二 try { return value1; } catch { } finally{ } return value2;
    • 程序执行顺序:
    1. try代码块中return前的代码
    2. finally块逻辑
    3. try代码块中return语句
    4. 注意:try中已经return了,所以后面的return是不执行的
  • 情况三 try { } catch { return value1; } finally{ } return value2;
    • 程序执行顺序
    1. try代码块
    • 有异常:
    1. catch代码块中return前的代码
    2. finally块
    3. catch块中的return语句。finally块后面的return不执行
    • 没有异常:
    1. finally块
    2. finally块后的return,没有异常catch不执行,其中的return自然不执行
  • 情况四 try { return value1; } catch { } finally { return value2; }
    • 程序执行顺序:
    1. try代码块return之前的代码
    2. finally块,因为有return,提前返回,覆盖前面的结果
  • 情况五 try { } catch { return value1; } finally { return value2; }
    • 程序执行顺序:
    1. catch块return之前的代码
    2. finally块,因为有return,提前返回,覆盖前面的结果
  • 情况六 try { return value1; } catch { return value2; } finally { return value3; }
    • 程序执行顺序:
    1. try代码块return语句之前的代码
    • 有异常:
    1. catch代码块中return前的代码
    2. finally代码块,并执行finally中的return
    • 没有异常:
    1. finally代码块,并执行finally中的return

自定义异常类

  • 一般用Exception来结尾,区分其他类说明这是一个异常类
  • 自定义异常类必须继承Exception或RunTimeException
  • 格式
public class XXXException extends Exception/RunTimeException {
  //添加一个空参构造方法
  //添加一个带异常信息的构造方法
  //查看源码发现,所有的异常类都有一个带异常信息的构造方法,都会调用父类带异常信息的构造方法,让父类来处理这个异常信息
}