异常处理

70 阅读5分钟

一、异常概述、体系

1、什么是异常

  • 异常是程序子啊“编译”或者“执行”的过程中可能出现的问题,注意:语法错误不算在异常体系中。
  • 比如:数组索引越界、空指针异常、日期格式化异常等等······

2、原因

  • 异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止
  • 研究异常并且避免异常,然后提前处理异常,提现的是程序的安全,健壮性。

3、异常体系

  • Error:系统级别的问题、JVM退出,代码无法控制。
  • Exception:java.long包下,称之为异常类,它表示程序本身可以处理的问题
    • RuntimeException及其子类:运行时异常,编译阶段不会报错(空指针异常,数组索引越界异常)
    • 除RuntimeException之外所有的异常:编译时异常,编译期必须处理的,否则程序不能通过编译。(日期格式化异常)

4、编译时异常和运行时异常

  • 编译时异常就是在编译时出现的异常
  • 运行时异常就是在运行时出现的异常

二、常见运行时异常

1、运行时异常

继承自RuntimeException的异常或者其子类,编译阶段是不会出错的,它是在运行时阶段可能出现的错误,运行时异常编译阶段可以处理也可以不处理,代码编译都能通过。

2、运行时异常示例:

  1. 数组索引越界异常: ArrayIndexOutOfBoundsException。
  2. 空指针异常 : NullPointerException。直接输出没有问题。但是调用空指针的变量的功能就会报错!!
  3. 类型转换异常:ClassCastException。
  4. 迭代器遍历没有此元素异常:NoSuchElementException。
  5. 数学操作异常:ArithmeticException。
  6. 数字转换异常: NumberFormatException。

三、常见编译时异常

编译时异常:继承自Exception的异常或者其子类,没有继承RuntimeException,"编译时异常是编译阶段就会报错",必须程序员编译阶段就处理的。否则代码编译就报错

编译时异常的作用是:是担心程序员的技术不行,在编译阶段就爆出一个错误, 目的在于提醒,提醒程序员这里很可能出错,请检查并注意不要出bug。

四、异常的默认处理流程

1、异常的产生默认的处理过程

  1. 默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。
  2. 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。
  3. 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
  4. 直接从当前执行的异常点干掉当前程序。
  5. 后续代码没有机会执行了,因为程序已经死亡。

异常一旦出现,会自动创建异常对象,最终抛出给虚拟机,虚拟机只要收到异常,就直接输出异常信息,干掉程序,默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!

五、异常的处理机制

1、编译时异常的处理形式有三种:

  • 出现异常直接抛出去给调用者,调用者也继续抛出去。
  • 出现异常自己捕获处理,不麻烦别人。
  • 前两者结合,出现异常直接抛出去给调用者,调用者捕获处理。

异常处理方式1--throws

  • throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理
  • 这种方式并不友好,发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡

抛出异常格式:

方法 throws 异常1, 异常2, 异常3.。 {
}

规范做法:

方法 throws Exception{
}

异常处理方式2--try...catch...

  • 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理。
  • 发生异常的方法自己独立完成异常的处理,程序可以继续往下执行

格式:

try{
    //监视可能出现的异常的代码
}catch(异常类型1 变量){
    //处理异常
}catch(异常类型2 变量){
    //处理异常
}...

建议格式:

try{
    //监视可能出现的异常的代码
}catch(Exception e){
    e.printStackTrace();//直接打印异常栈信息
}

Exception可以捕获处理一切异常类型!

异常处理方式3--前两者结合

  • 方法直接将异常通过throws抛出去给调用者。
  • 调用者收到异常后直接捕获处理。

2、运行时异常的处理格式

  • 运行时异常编译阶段不会出错,是运行时才可能出错的,所以编译阶段不处理也可以。
  • 按照规范建议还是处理:建议在最外层调用处集中捕获处理即可。

六、自定义异常

1、自定义异常的必要

  • Java无法为这个世界上全部的问题提供异常类
  • 如果企业要通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了

2、自定义异常的好处

  • 可以使用异常的机制管理业务问题,如提醒程序员注意。
  • 同时一旦出现bug,可以用异常的形式清晰的指出出错的地方

3、自定义异常的分类

  1. 自定义编译时异常
    • 定义一个异常继承类Exception
    • 重写构造器
    • 在出现异常的地方用throw new 自定义对象抛出
    • 作用:编译时异常时编译阶段就报错,提醒更加强烈,一定要处理。
public class ageException extends Exception{
    public ageException() {
    }

    public ageException(String message) {
        super(message);
    }
}
public class Test {
    public static void main(String[] args) {
        try {
            checkAge(45);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void checkAge(int age) throws ageException {
        if (age < 0 || age > 200){
            //抛出去一个异常对象给调用者
            //throw:在方法内部直接创建一个异常对象,并从此点抛出
            //throws:用在方法申明上的,抛出方法内部的异常
            throw new ageException(age + " is illegal");
        }else {
            System.out.println("合法");
        }
    }
}

正常和异常如下图:

image.png

image.png

  1. 自定义运行时异常
    • 定义一个异常类继承RuntimeException
    • 重写构造器
    • 在出现异常的地方用throw new自定义对象抛出
    • 作用:提醒不强烈,编译阶段不报错,运行时才可能出现。