Java 异常处理

前言

1、异常

  • 导致程序的正常流程被中断的事件,叫做异常

  • 比如要打开某个文件,这个文件是有可能不存在的。

  • Java 中通过 new FileInputStream(f) 试图打开某文件,就有可能抛出文件不存在异常 FileNotFoundException

  • 如果不处理该异常,就会有编译错误。

2、异常处理

  • 异常处理常见手段 trycatchfinallythrows

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    try {
    // 可能抛出异常的代码
    ...
    } catch(Exception e) {
    // 捕捉到异常,处理异常的代码
    ...
    } finally {
    // 无论是否出现异常,都会执行的代码
    ...
    }
  • 1
    2
    3
    4
    5
    6
    7
    try (/* 初始化代码 */ ...) {
    ...
    } catch(Exception e) {
    ...
    } finally {
    ...
    }
  • 如果 try {} 里有一个 return 语句

    • try 里的 return 和 finally 里的 return 都会执行,但是当前方法只会采纳 finally 中 return 的值。
  • final, finally, finalize 的区别

    • final 修饰类,方法,基本类型变量,引用的时候分别有不同的意思。
      • 修饰类 表示该类不能被继承。
      • 修饰方法 表示该方法不能被重写。
      • 修饰基本类型变量 表示该变量只能被赋值一次。
      • 修饰引用 表示该引用只有一次指向对象的机会。
    • finally 是用于异常处理的场面,无论是否有异常抛出,都会执行。
    • finalize 是 Object 的方法,所有类都继承了该方法。当一个对象满足垃圾回收的条件,并且被回收的时候,其 finalize() 方法就会被调用。

2.1 捕捉异常

  • 捕捉异常

    • 将可能抛出异常的代码放在 try 里。
    • 如果没有抛出异常,就会顺序往下执行,并且不执行 catch 块中的代码。如果有抛出异常,try 里的代码会立即终止,程序流程会运行到对应的 catch 块中。
    • 无论是否出现异常,finally 中的代码都会被执行。
  • 使用异常的父类也可以 catch 住异常。

  • 多异常捕捉

    • 分别进行捕捉

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      File f = new File("d:/LOL.exe");

      // 多异常分别进行 catch
      try {
      FileInputStream fis = new FileInputStream(f);

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      Date d = sdf.parse("2016-06-03");
      } catch (FileNotFoundException e) { // 捕捉 FileInputStream 的异常
      System.out.println("文件不存在");
      e.printStackTrace();
      } catch (ParseException e) { // 捕捉 SimpleDateFormat 的异常
      System.out.println("日期格式解析错误");
      e.printStackTrace();
      }
    • 统一进行捕捉

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      File f = new File("d:/LOL.exe");

      // 多异常统一进行 catch
      try {
      FileInputStream fis = new FileInputStream(f);

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      Date d = sdf.parse("2016-06-03");
      } catch (FileNotFoundException | ParseException e) { // 捕捉 FileInputStream 和 SimpleDateFormat 的异常
      if (e instanceof FileNotFoundException) {
      System.out.println("文件不存在");
      }
      if (e instanceof ParseException) {
      System.out.println("日期格式解析错误");
      }

      e.printStackTrace();
      }

2.2 抛出异常

  • 抛出异常

    1
    2
    3
    4
    5
    6
    private static void method2(file) throws FileNotFoundException {      // throws 声明方法可能抛出异常

    if (ObjectUtils.isEmpty(file)) {
    throw new FileNotFoundException(9006, "file not found!"); // throw 抛出异常
    }
    }
  • throws 与 throw 的区别

    • throws 出现在方法声明上,而 throw 通常都出现在方法体内。
    • throws 表示出现异常的一种可能性,并不一定会发生这些异常。
    • throw 则是抛出了异常,执行 throw 则一定抛出了某个异常对象。

3、异常分类

  • 异常分类为可查异常(CheckedException),运行时异常(RuntimeException)和错误(Error)3 种。

  • 其中,运行时异常和错误又叫非可查异常。

异常类型 是否可查 说明
可查异常 即必须进行处理的异常,要么 try catch 住,要么往外抛,谁调用,谁处理,如果不处理,编译器,就不让你通过
运行时异常 否,非可查异常 不是必须进行 try catch 的异常,在编写代码的时候,依然可以使用 try catch throws 进行处理,
与可查异常不同之处在于,即便不进行 try catch,也不会有编译错误
错误 否,非可查异常 指的是系统级别的异常,通常是内存用光了,在默认设置下,一般 Java 程序启动的时候,最大可以使用 16m 的内存,
与运行时异常一样,错误也是不要求强制捕捉的
  • 运行时异常与一般异常有何异同

    • 运行时异常 又叫做非可查异常,在编译过程中,不要求必须进行显示捕捉。
    • 一般异常又叫做可查异常,在编译过程中,必须进行处理,要么捕捉,要么通过 throws 抛出去。
  • Error 和 Exception 的区别

    • Error 和 Exception 都实现了 Throwable 接口。
    • Error 指的是 JVM 层面的错误,比如内存不足 OutOfMemoryError。
    • Exception 指的是代码逻辑的异常,比如下标越界 OutOfIndexException。
  • Throwable 类ExceptionError 都继承了该类,所以在捕捉的时候,也可以使用 Throwable 进行捕捉。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package java.lang;

    /**
    * The {@code Throwable} class is the superclass of all errors and
    * exceptions in the Java language. Only objects that are instances of this
    * class (or one of its subclasses) are thrown by the Java Virtual Machine or
    * can be thrown by the Java {@code throw} statement. Similarly, only
    * this class or one of its subclasses can be the argument type in a
    * {@code catch} clause.
    *
    * @author unascribed, Josh Bloch
    * @jls 11.2 Compile-Time Checking of Exceptions
    * @since JDK1.0
    */

    public class Throwable implements Serializable {

    }
  • Exception 类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package java.lang;

    /**
    * The class {@code Exception} and its subclasses are a form of
    * {@code Throwable} that indicates conditions that a reasonable
    * application might want to catch.
    *
    * @author Frank Yellin
    * @see java.lang.Error
    * @jls 11.2 Compile-Time Checking of Exceptions
    * @since JDK1.0
    */

    public class Exception extends Throwable {

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package java.lang;

    /**
    * {@code RuntimeException} is the superclass of those
    * exceptions that can be thrown during the normal operation of the
    * Java Virtual Machine.
    *
    * @author Frank Yellin
    * @jls 11.2 Compile-Time Checking of Exceptions
    * @since JDK1.0
    */

    public class RuntimeException extends Exception {

    }
    1
    2
    3
    4
    5
    package com.sun.xml.internal.ws.api.model;

    public interface CheckedException {

    }
  • Error 类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package java.lang;

    /**
    * An {@code Error} is a subclass of {@code Throwable}
    * that indicates serious problems that a reasonable application
    * should not try to catch. Most such errors are abnormal conditions.
    * The {@code ThreadDeath} error, though a "normal" condition,
    * is also a subclass of {@code Error} because most applications
    * should not try to catch it.
    *
    * @author Frank Yellin
    * @see java.lang.ThreadDeath
    * @jls 11.2 Compile-Time Checking of Exceptions
    * @since JDK1.0
    */

    public class Error extends Throwable {

    }
  • 常见异常

异常 介绍
RuntimeException 运行时异常
ArithmeticException 算术异常,由于除数为 0 引起的异常
ArrayStoreException 由于数组存储空间不够引起的异常
ClassCastException 类型转换异常,当把一个对象归为某个类,但实际上此对象并不是由这个类 创建的,也不是其子类创建的,则会引起异常
ConcurrentModificationException 同步修改异常,遍历一个集合的时候,删除集合的元素,就会抛出该异常
NegativeArraySizeException 数组长度是负数,则产生异常
NullPointerException 空指针异常,程序试图访问一个空的数组中的元素或访问空的对象中的 方法或变量时产生异常
NumberFormatException 字符的 UTF 代码数据格式有错引起异常
IndexOutOfBoundsException 数组下标越界异常
IllegalMonitorStateException 监控器状态出错引起的异常
IllegalThreadException 线程调用某个方法而所处状态不适当,引起异常
SecurityException 由于访问了不应访问的指针,使安全性出问题而引起异常
StringIndexOutOfBoundsException 访问字符串序号越界,引起异常
Exception 可查异常
IOException 由于文件未找到、未打开或者 I/O 操作不能进行而引起异常
ClassNotFoundException 未找到指定名字的类或接口引起异常
CloneNotSupportedException 程序中的一个对象引用 Object 类的 clone 方法,但 此对象并没有连接 Cloneable 接口,从而引起异常
InterruptedException 当一个线程处于等待状态时,另一个线程中断此线程,从 而引起异常,有关线程的内容,将在下一章讲述
NoSuchMethodException 所调用的方法未找到,引起异常
FileNotFoundException 未找到指定文件引起异常
EOFException 未完成输入操作即遇文件结束引起异常
Error 错误
OutofMemoryException 用 new 语句创建对象时,如系统无法为其分配内存空 间则产生异常
IndexOutOfBoundsExcention 由于数组下标越界或字符串访问越界引起异常
Illega1AccessExcePtion 试图访问一个非 public 方法
ArrayIdexOutOfBoundsException 访问数组元素下标越界,引起异常

4、自定义异常

  • 创建 自定义异常

    • 创建一个自定义异常类,并继承 Exception,提供两个构造方法。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      /* AdviceNotFoundException.java */

      package com.qianchia.support.exception;

      import java.io.Serializable;

      // 自定义异常类
      public class AdviceNotFoundException extends NotFoundException implements Serializable {

      public AdviceNotFoundException() { // 无参的构造方法

      }

      public AdviceNotFoundException(int code, String message) { // 带参的构造方法,并调用父类的对应的构造方法
      super(code, message);
      }

      public AdviceNotFoundException(String message) { // 带参的构造方法,并调用父类的对应的构造方法
      super(message);
      }
      }
  • 抛出 自定义异常

    • 创建一个自定义异常类实例。
    • 通过 throw 抛出该异常。
    • 当前方法通过 throws 抛出该异常。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      /* AdviceService.java */

      package com.qianchia.service;

      import com.qianchia.support.exception.AdviceNotFoundException;

      public interface AdviceService {

      void deleteAdvice(int id) throws AdviceNotFoundException; // 声明 可能抛出异常的方法
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      /* AdviceServiceImpl.java */

      package com.qianchia.service.impl;

      import com.qianchia.dao.AdviceDao;
      import com.qianchia.po.Advice;

      import com.qianchia.service.AdviceService;
      import com.qianchia.support.exception.AdviceNotFoundException;

      @Service
      public class AdviceServiceImpl implements AdviceService {

      @Override
      public void deleteAdvice(int id) throws AdviceNotFoundException { // 实现 可能抛出异常的方法

      Advice advice = adviceDao.getById(id);
      if (ObjectUtils.isEmpty(advice)) {
      throw new AdviceNotFoundException(9006, "Advice not found!"); // 抛出异常
      }
      adviceDao.delete(id);
      }
      }
  • 捕捉 自定义异常

    • 使用 catch 捕捉异常。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      /* AdviceController.java */

      package com.qianchia.controller.manage;

      import com.qianchia.po.Advice;

      import com.qianchia.service.AdviceService;
      import com.qianchia.support.exception.AdviceNotFoundException;

      @RestController
      @RequestMapping("/api/v1")
      public class AdviceController {

      @Autowired
      private AdviceService adviceService;

      @PostMapping("/advice/{id:\\d+}/delete")
      public Response deleteAdvice(@PathVariable("id") int id) {

      try { // 处理 可能抛出异常的方法
      adviceService.deleteAdvice(id);
      return new Response<>();
      } catch (AdviceNotFoundException e) { // 捕捉异常
      return new Response(e.getCode(), e.getMessage());
      }
      }
      }
文章目录
  1. 1. 前言
  2. 2. 1、异常
  3. 3. 2、异常处理
    1. 3.1. 2.1 捕捉异常
    2. 3.2. 2.2 抛出异常
  4. 4. 3、异常分类
  5. 5. 4、自定义异常
隐藏目录