跳至主要內容

异常

Yang大约 6 分钟JavaJava基础

异常本质上是程序上的错误,在程序运行过程中,意外发生的情况,背离我们程序本身的意图的表现,都可以理解为异常

分类

编译时异常

  • 括号配对
  • 语句分号
  • 关键字错误
  • 等等

运行时异常

  • 使用空的对象引用调用方法
  • 数组访问时下标越界
  • 算术运算时除数为 0
  • 类型转换时无法正常转型
  • 等等

Throwable

异常根类

Error

程序无法处理的错误,表示运行应用程序中较严重的问题,他们在应用程序的控制和处理能力之外,而且大多数是程序运行是不允许出现的状况,通常不需要关心此类错误

  • VirtualMachineError:虚拟机错误
  • OutOfMemoryError:内存溢出
  • ThreadDeath:线程死锁

Exception

程序本身可以处理的异常

  • Checked Exception:检查型异常,编译器要求必须处理的异常

    • IOException:IO 异常
    • SQLException:SQL 异常
  • Unchecked Exception:非检查型异常,编译器不要求强制处理的异常

    • RuntimeException
      • NullPointerException:空指针异常(使用了未经初始化的对象或者是不存在的对象)
      • ArrayIndexOutOfBoundsException:数组下标越界异常(使用超出数组下标范围的下标)
      • ArrayStoreException:数组中包含不兼容的值的异常(数组中实际传入的数据与预定不符,例如子类数组经过向上转型后,传入父类对象)
      • ArithmeticException:数学运算异常(涉及到数学运算的地方可能出现失误,比如程序中出现了除以零这样的运算)
      • NumberFormatException:数字格式化异常(涉及到类型转换时,比如不符合转换格式的字符串被转换成数字)
      • ClassCastException:类型转换异常(如向下转型时,转换对象无法完成正常转换)
      • InputMismatchException:输入格式错误异常(接收数据与预期格式不符)
        • 全称时 java.util.InputMismatchException,由于java.util包不是默认直接加载的,所以需要导入操作才能正常应用
      • FileNotFoundException:文件未找到异常(操作文件内容时发现文件不存在 )
      • 等等

关键字

try catch finally

  • try 块后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally

    • try {}后可以书写多个 catch 块,但不能出现同类型异常
    • 安全起见,建议在多个 catch 块的最后(并且只能是最后位置)添加异常父类 catch (Exception e){}
  • try...catch 代码块中定义的是局部变量,只能在其代码块中使用

  • finally 代码块是强制执行的,不建议在其中书写 return 语句,一旦书写会屏蔽掉 trycatch 代码块中的 return 语句

  • System.exit(1):终止正在运行的 java 虚拟机,不会继续执行到 finally 代码块,参数非 0 表示异常终止

  • 子类重写父类排除的异常方法时,声明的异常必须是父类方法所声明异常的同类或子类

try {
  System.out.println(12 / 0);
} catch (Exception e) {
  System.out.println(e);
  e.printStackTrace();
} finally {
  System.out.println("程序运行结束");
}

throws

当在设计中,希望有针对性的产生、抛出异常,由上级调用者视情况处理时,就需要使用throw、throws

  • 表示通知方法调用者,使用该方法时,可能会发生哪些异常,需要进行相关处理

  • 通过 throws 声明将要抛出何种类型的异常,后面可以跟多个异常类型,用逗号隔开

    • public void method() throws Exception1,Exception2,Exception3 {}
  • 声明抛出的异常类型必须和 throw 抛出的对象相同或者是其父类

  • 当方法抛出异常列表中的异常时,方法将不对这些类型及其子类类型的异常作处理,而抛向调用该方法的方法,由他去处理

import java.util.Scanner;
import java.util.InputMismatchException;

public class Test {
    public static int test() throws ArithmeticException, InputMismatchException {
        Scanner input = new Scanner(System.in);
        System.out.println("=====运算开始=====");
        System.out.print("请输入第一个整数:");
        int one = input.nextInt();
        System.out.print("请输入第二个整数:");
        int two = input.nextInt();
        System.out.println("=====运算结束=====");
        return one / two;
    }

    public static void main(String[] args) {
        try {
            int result = test();
            System.out.println("one 和 two 的商是:" + result);
        } catch (ArithmeticException e) {
            System.out.println("除数不允许为零!");
            e.printStackTrace();
        } catch (InputMismatchException e) {
            System.out.println("请输入整数!");
            e.printStackTrace();
        }
    }
}

throw

通过 throw 将产生的异常抛出,用来规避可能出现的风险,完成一些程序的逻辑

  • 抛出的异常只能是 Throwable 或者其子类的实例对象
// 写法1:自己抛出的异常,自己处理
public void method(){
  try {
    ...
    throw new 异常类型();
  } catch (异常类型 e){
    ...
  }
}

// 写法2:谁调用谁处理
public void method() throws 异常类型 {
  ...
  throw new 异常类型();
}

方法

toString()

获取异常类型和描述信息,当直接输出对象 e 时,调用的就是该方法

try {
  
} catch (Exception e) {
  System.out.println(e.toString());
}

getMessage()

获取异常描述信息

try {
  
} catch (Exception e) {
  System.out.println(e.getMessage());
}

printStackTrace()

打印异常的堆栈信息,包括种类、描述信息、出错位置等

try {
  
} catch (Exception e) {
  e.printStackTrace();
}

自定义异常

  • 描述特定业务产生的异常类型
  • 定义一个类,继承 Throwable 类或它的子类
  • 判断自定义的异常类型属于检查异常还是非检查异常需要看在定义自定义异常类时所继承的父类,如果父类属于检查异常,则自定义异常也就是检查异常,反之亦然
  • 自定义异常需先经过 throw 抛出,才能被 catch 捕获,无法自动被程序捕获并处理
// GameAgeException.java
public class GameAgeException extends Exception {
    public GameAgeException() {
        super("未满18岁,不能玩此类型游戏!");
    }
}

// Test.java
import java.util.Scanner;

public class Test {
    // 描述酒店的入住规则:限定年龄,18岁以下,80岁以上的住客必须由亲友陪同
    public static void ageCheck() throws GameAgeException {
        System.out.print("请输入年龄:");
        Scanner input = new Scanner(System.in);
        int age = input.nextInt();
        if (age < 18 || age > 80) {
            throw new GameAgeException();
        } else {
            System.out.println("欢迎进入游戏");
        }
    }

    public static void main(String[] args) {
        try {
            ageCheck();
        } catch (GameAgeException e) {
            System.out.println(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

异常链

  • 捕获一个异常后再抛出另一个异常
  • 将异常发生的原因一个传一个串起来,即把底层的异常信息传递给上层,逐层抛出
// HotelAgeException.java
public class HotelAgeException extends Exception {
    public HotelAgeException() {
        super("18岁以下,80岁以上的住客必须由亲友陪同!");
    }
}

// Test.java
public class Test {
    public static void main(String[] args) {
        try {
            testThree();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 描述酒店的入住规则:限定年龄,18岁以下,80岁以上的住客必须由亲友陪同
    public static void testOne() throws HotelAgeException {
        throw new HotelAgeException();
    }

    public static void testTwo() throws Exception {
        try {
            testOne();
        } catch (HotelAgeException e) {
            throw new Exception("我是新产生的异常1", e);
        }
    }

    public static void testThree() throws Exception {
        try {
            testTwo();
        } catch (Exception e) {
            // 使用第二个参数
            throw new Exception("我是新产生的异常2", e);
            /*
             或者使用 initCause
             Exception e1 = new Exception("我是新产生的异常2");
             e1.initCause(e);
             throw e1;
            */
        }
    }
}
上次编辑于:
贡献者: sunzhenyang