java

异常概述

什么是异常?

比如:数组索引越界、空指针异常、 日期格式化异常,等…

为什么要学习异常?

异常一旦出现了,如果没有提前处理,程序就会退出 JVM 虚拟机而终止。

研究异常并且避免异常,然后提前处理异常,体现的是程序的安全, 健壮性。

异常体系

Error:系统级别问题、JVM 退出等,代码无法控制,严重问题,不需要处理。

Exception:在 java.lang 包下,称为异常类,它表示程序本身可以处理的问题

  • RuntimeException 及其子类:运行时异常,编译阶段不会报错,出现问题后,需要我们修改代码。 (空指针异常,数组索引越界异常)
  • 除 RuntimeException 之外所有的异常:编译时异常,编译期就必须处理的,否则程序不能通过编译。 (日期格式化异常)。

编译时异常和运行时异常

  • 编译时异常,是在编译成 class 文件时必须要处理的异常,也称之为受检异常
  • 运行时异常,在编译成 class 文件不需要处理,在运行字节码文件时可能出现的异常

简单来说:

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

区别:

编译时异常

  • 都是 Exception 类及其子类
  • 必须显示处理,否则程序就会发生错误,无法通过编译

运行时异常

  • 都是 RuntimeException 类及其子类
  • 无需显示处理,也可以和编译时异常一样处理

总结

1、异常是什么?

异常是代码在编译或者执行的过程中可能出现的错误。

2、异常分为几类?

编译时异常、运行时异常。

编译时异常:没有继承 RuntimeExcpetion 的异常,编译阶段就会出错。

运行时异常:继承自 RuntimeException 的异常或其子类,编译阶段不报错,运行可能报错。

3、学习异常的目的?

避免异常的出现,同时处理可能出现的异常,让代码更稳健。

常见运行时异常

运行时异常示例

  • 数组索引越界异常: ArrayIndexOutOfBoundsException
  • 空指针异常 : NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错。
  • 数学操作异常:ArithmeticException
  • 类型转换异常:ClassCastException
  • 数字转换异常: NumberFormatException

运行时异常的特点

  • 运行时异常:继承自 RuntimeException 的异常或者其子类,
  • 编译阶段不报错,运行可能报错。

运行时异常:一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误,自己的水平有问题!

常见编译时异常

  • 不是 RuntimeException 或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。

编译时异常示例

编译时异常的作用是什么:

  • 是担心程序员的技术不行,在编译阶段就爆出一个错误, 目的在于提醒不要出错!
  • 编译时异常是可遇不可求。遇到了就遇到了呗。

编译时异常的特点

  • 编译时异常:继承自 Exception 的异常或者其子类。
  • 编译阶段报错,必须处理,否则代码不通过。

异常的默认处理流程

(1)把异常的名称,错误原因及异常出现的位置等信息输出在了控制台。

(2)程序停止执行。

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

默认异常处理机制

  • 默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!

编译时异常的处理机制

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

  • 出现异常直接抛出去给调用者,调用者也继续抛出去。
  • 出现异常自己捕获处理,不麻烦别人。
  • 前两者结合,出现异常直接抛出去给调用者,调用者捕获处理。
  1. 异常处理方式 1 —— throws
    1. throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理。
    2. 这种方式并不好,发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡。

抛出异常格式:

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

public void 方法() throws 异常类名 { 
}

方法 throws Exception{
}
代表可以抛出一切异常,

注意事项

  • 这个 throws 格式是跟在方法的括号后面的
  • 编译时异常必须要进行处理,两种处理方案: try...catch …或者 throws,如果采用 throws 这种方案,将来谁调用谁处理
  • 运行时异常可以不处理,出现问题后,需要我们回来修改代码

示例代码

public class ExceptionDemo {
  public static void main(String[] args) {
    System.out.println("开始");
//   method();
    try {
      method2();
    }catch (ParseException e) {
      e.printStackTrace();
    }
    System.out.println("结束");
  }
  //编译时异常
  public static void method2() throws ParseException {
    String s = "2048-08-09";
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Date d = sdf.parse(s);
    System.out.println(d);
  }
  //运行时异常
  public static void method() throws ArrayIndexOutOfBoundsException {
    int[] arr = {1, 2, 3};
    System.out.println(arr[3]);
  }
}
  1. 异常处理方式 2 —— try…catch…
  • 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理。
  • 这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行。

抛出异常格式:

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

推荐格式:
    try {
        // 可能出现异常的代码!
    } catch (Exception e){
        e.printStackTrace(); // 直接打印异常栈信息
}

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

执行流程

  • 程序从 try 里面的代码开始执行
  • 出现异常,就会跳转到对应的 catch 里面去执行
  • 执行完毕之后,程序还可以继续往下执行

示例代码

public class ExceptionDemo01 {
  public static void main(String[] args) {
    System.out.println("开始");
    method();
    System.out.println("结束");
  }
  public static void method() {
    try {
      int[] arr = {1, 2, 3};
      System.out.println(arr[3]);
      System.out.println("这里能够访问到吗");
    } catch (ArrayIndexOutOfBoundsException e) {
//     System.out.println("你访问的数组索引不存在,请回去修改为正确的索引");
      e.printStackTrace();
    }
  }
}
  1. 异常处理方式 3 —— 前两者结合
  • 方法直接将异通过 throws 抛出去给调用者。
  • 调用者收到异常后直接捕获处理。

异常处理的总结

  • 在开发中按照规范来说第三种方式是最好的:底层的异常抛出去给最外层,最外层集中捕获处理。
  • 实际应用中,只要代码能够编译通过,并且功能能完成,那么每一种异常处理方式似乎也都是可以的。

运行时异常的处理机制

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

异常处理使代码更稳健的案例

需求:键盘录入一个合理的价格为止(必须是数值)。

分析:

  • 定义一个死循环,让用户不断的输入价格。

自定义异常

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

自定义异常的好处:

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

自定义异常的分类

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

代码演示


public class ScoreException extends Exception {
  public ScoreException() {}
  public ScoreException(String message) {
    super(message);
  }
}

/*
    老师类
*/
public class Teacher {
  public void checkScore(int score) throws ScoreException {
    if(score<0 || score>100) {
//     throw new ScoreException();
      throw new ScoreException("你给的分数有误,分数应该在0-100之间");
    } else {
      System.out.println("成绩正常");
    }
  }
}

/*
    测试类
*/
public class Demo {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("请输入分数:");
    int score = sc.nextInt();
    Teacher t = new Teacher();
    try {
      t.checkScore(score);
    } catch (ScoreException e) {
      e.printStackTrace();
    }
  }
}