异常处理

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2022-06-25

异常体系结构

java.lang.Throwable

  • java.lang.Error: 一般不编写针对性代码进行处理
  • java.lang.Exception: 可以进行异常的处理
    • 编译时异常(checked): IOException, ClassNotFoundException, …
    • 运行时异常(unchecked): NullPointException, ArrayIndexOutOfBoundsException, …

代码示例:

import org.junit.Test;
import java.util.Date;
import java.util.Scanner;

public class ExceptionTest {
  // 运行时异常
  // ArithmeticException
  @Test
  public void test6() {
    int a = 10;
    int b = 0;
    System.out.println(a / b);
  }

  // InputMismatchException
  @Test
  public void test5() {
    Scanner scanner = new Scanner(System.in);
    int score = scanner.nextInt(); // 如果输入abc,则报错
    System.out.println(score);
  }


  // NumberFormatException
  @Test
  public void test4() {
    String str = "abc";
    int num = Integer.parseInt(str);
  }


  // 类型转换类型 ClassCastException
  @Test
  public void test3() {
    Object obj = new Date();
    String str = (String)obj;
  }


  // 数组角标越界 ArrayIndexOutOfBoundsException
  @Test
  public void test2() {
    int[] arr = new int[10];
    System.out.println(arr[10]);

    // StringIndexOutOfBoundsException
    String str = "abc";
    System.out.println(str.charAt(3));
  }

  // 空指针异常 NullPointException
  @Test
  public void test1() {
    int[] arr = null;
    System.out.println(arr[3]);

    String str = "abc";
    str = null;
    System.out.println(str.charAt(0));
  }
}

异常处理机制

异常的处理:抓抛模型

抛:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生产一个对应异常类的对象,并将此对象抛出。一旦抛出对象以后,其之后的代码就不再执行

抓:异常的处理方式

try-catch

import org.junit.Test;

public class ExceptionTest2 {

  @Test
  public void test1() {
    try {
      // 在try结构中声明的变量,出了try结构就不能访问了
      String str = "abc";
      int num = Integer.parseInt(str);
      System.out.println(num);
    } catch (NumberFormatException e) {
      System.out.println("出现了数值转换异常");

      // getMessage
      System.out.println(e.getMessage()); // 异常信息: For input string: "abc"


      // printStackTrace 打印堆栈错误信息:错误出现在哪一行、及相关程序代码的调用栈、...
      e.printStackTrace();

      // try...catch结构可以嵌套
      try {

      } catch (Exception err) {
        System.out.println(err);
      }

    } catch (NullPointerException e) {
      System.out.println("空指针异常");
      // 可以一直catch下去,会根据类型进入相应的结构
      // 如果catch中的异常类型没有子父类关系,则谁在上谁在下没有关系
      // 如果有,要求子类异常一定声明在父类异常的上面
    } finally {
      // finally子句是可选的
      // 是一定会被执行的代码,即使catch代码块又出现了异常,或者try/catch中有return语句,finally子句也一定会执行
      System.out.println("不管有没有异常,一定会执行的代码");
    }
  }
}

throws

向上抛出异常但是没有处理掉,try-catch是捕捉到了错误并进行了处理。

1、throws + 异常类型 写在方法的声明处,指明此方法执行时,可能会排除异常类型,一旦方法体执行,出现异常,仍会在代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class ExceptionTest3 {
  public static void main(String[] args) throws IOException {
    // 在main方法一般不会抛出异常,但是可以抛出,只是异常一旦抛出就是抛给了jvm,等于程序员没有自行处理
    method2();
  }


  public static void method2() throws IOException {
    // 调用了method1,method1会抛出错误,如果method2不用try-catch来处理也需要继续往上抛 直到main方法位置
    method1();
  }

  public static void method1() throws IOException {
    File file = new File("hello.txt");
    FileInputStream fis = new FileInputStream(file);

    int data = fis.read();

    while (data != -1) {
      System.out.println((char)data);
      data = fis.read();
    }
    fis.close();
  }
}

2、关于throws及方法重写的规则

子类重写的方法抛出的异常类型“不大于”父类被重写的方法抛出的异常类型。父类的方法如果没有抛出异常,子类重写的方法也不能抛出异常。

import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionTest4 {
  public static void main(String[] args) {
    ExceptionTest4 t = new ExceptionTest4();
    t.display(new SubClass());
  }

  // 多态,函数接收一个父类,使用的时候传入一个子类
  // 但是函数定义的时候,是根据父类抛出错误来进行catch处理的
  // 如果传入子类,而子类重写的方法,大于父类抛出的错误类型
  // 则针对父类抛出错误的catch处理(多态代码设计)就catch不到了
  // 同样,父类的方法如果没有抛出异常,子类重写的方法也不能抛出异常
  public void display(SuperClass s) {
    try {
      s.method();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

class SuperClass {
  public void method() throws IOException {

  }
}

class SubClass extends SuperClass {
  // 也可以是IOException,但是不能比IOException大(如:Exception)
  public void method() throws FileNotFoundException {

  }
}

如何选择处理方式

1、如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,此时如果子类重写的方法中有异常,就必须使用try-catch方式处理

2、执行的方法A中,先后又调用了另外的几个方法,这几个方法是递进关系执行的,则建议这几个方法使用throws的方式进行处理,而执行的方法A考虑使用try-catch的方式处理。因为在递进方法中的某一个中出现了异常并进行了try-catch,调用依然会继续进行,下一个方法所处就不是正常的上下文环境,所以干脆throws,在上层统一try-catch。

手动抛出异常

public class ExceptionTest5 {
  public static void main(String[] args) {
    Student s = new Student();
    s.register(-1);
  }
}

class Student {
  private int id;

  public void register(int id) {
    if (id > 0) {
        this.id = id;
    } else {
      throw new RuntimeException("输入的数据非法");
    }
  }
}

自定义异常

/**
 * 如何定义异常类
 * 1、继承于现有的异常结构:RuntimeException、Exception
 * 2、提供全局常量 serialVersionUID
 * 3、提供重载构造器
 */

public class ExceptionTest6 extends RuntimeException {
  static final long serialVersionUID = -703489719075766939L;

  public ExceptionTest6() {
  }

  public ExceptionTest6(String message) {
    super(message);
  }
}