【Java】异常处理


本文将了解常见的异常类,区分运行时异常和受检查异常,学习如何捕获和抛出异常,以及异常处理时的一些小提示和建议

 

整体把握

基于面向对象的编程思想,Java语言采用不同的异常类来描述不同的异常情况,但所有的异常类都有一个祖先类java.lang.Throwable,Throwable提供了异常操作常用的一些方法,如getMessage()和printStackTrace()。

1、getMessage()返回String类型的异常信息

2、printStackTrace()返回详细异常信息

Throwable有两个直接子类,Error和Exception;

【Java】异常处理

Error (错误)

Error及其子孙类,表示JVM本身的错误,这些错误仅靠程序本身无法解决。Java 程序通常不捕获和处理Error。

Exception (异常)

Exception及其子孙类描述了在程序运行期间出现的各种异常状况,Exception可以分为运行时异常和受检查异常。

【Java】异常处理

运行时异常(RuntimeException)

RuntimeException及其子类均属于运行时异常,运行时异常在程序编译阶段不会被发现,事实上,Java编译器并不会对运行时异常进行检查,无论是否会出现运行时异常都能够编译通过。下面认识一些常见的运行时异常:

ArithmeticException

算术异常,当出现异常的运算条件时,抛出此异常。比如整数除以0;

IndexOutOfBoundsException

下标越界异常,常见的子类ArrayIndexOutOfBoundsException表示数组下标越界。

ClassCastException

类型转换异常,当试图将对象强制转换为该对象或其子类对象时,可能出现该异常。

IllegalArgumentException

非法参数异常,当传递一个不合法或不正确的参数时,可能出现该异常。其子类NumberFormatException表示数字格式异常。

NullPointerException

空指针异常

检查异常(Checked Exception)

如果程序可能出现检查异常时,将无法通过Java编译器检查,eclipse等编译器还会在代码编辑时提醒开发者在代码中处理该类异常,当程序出现检查异常时,可以使用try-catch语句捕获处理,也可以使用throws声明异常抛出。下面认识一些常见的检查异常:

ClassNotFoundException

类没找到异常,应用程序加载不到相应的类时,抛出该异常。

InstantiationException

实例化异常,当使用 Class 的 newInstance 方法创建类的实例,但指定的类对象是接口类或抽象类时,因为无法实例化而抛出该异常。

NoSuchFieldException

未找到相应属性,在类中没有找到相应名称的属性字段。

NoSuchMethodException

未找到相应方法,在类中没有找到指定的方法。

异常捕获

我们可以使用try-catch代码块捕获异常。

将可能出现异常的代码放到try-catch中,catch 语句可以允许有多个,负责声明要捕获的异常类型,JVM会把实际出现的异常对象按照声明顺序依次和catch声明的异常类型进行匹配,匹配成功则执行该catch块,其他catch块则不再执行。

try-catch还可以搭配finally使用,无论try代码块中是否出现异常都会执行finally代码块。finally代码块不被执行的情况是程序提前终止了,如:在finally代码块执行前先执行了System.exit();

异常抛出

如果一个方法可能会出现异常,但方法内无法处理时,可以使用throws声明异常抛出,throws关键字用于方法声明处,作用就是声明可能会出现的异常。

程序中也可以使用throw来抛出异常,但throw抛出的对象必须是Throwable类或其子类。

异常处理提示

1、在catch中使用了throw抛出异常时,程序不会执行再执行try-catch-finally块后面的代码。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
  try {
    String[] str = new String[3];
    str[0] = "00";
    str[1] = "11";
    str[2] = "22";
    str[3] = "33";
  } catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("— catch —");
    throw e; // 使用了throw
  } finally {
    System.out.println("— finally —");
  }
  System.out.println("— end —"); // 该行代码不会执行
}

运行结果:(可以看到try-catch-finally块后面的代码未执行)

【Java】异常处理

 

2、建议不要在finally中使用return或者throw

当在finally中使用了throw时,编译器不会再对try-catch中的检查异常进行检查,JVM也不会再去捕获try-catch中的异常,程序抛出的异常以finally中抛出的异常为准;

当在finally中使用了return时,try-catch中的return语句将会被忽略;原因是当程序在try-catch中遇到return语句时,会先执行finally中的代码,因此,如果在finally中使用了return,那么finally中的return语句会覆盖try-catch中的return语句。

合理的做法是在 finally块之后使用return语句。

3、建议不要使用catch (Exception e)子句,即用Exception声明异常类型,Exception作为异常的父类,会捕获程序出现的所有异常,包括那些本应抛出的异常,而且不利于对具体异常做具体处理。

4、把try-catch块放到循环体内时,当程序出现异常并未导致运行中止且未声明结束语句时,循环会继续执行,但如果try-catch在循环体外,如果出现异常,循环会结束。

当try-catch块在循环体内时:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
  String[] str = new String[2];
  for(int i = 0; i < 5; i++) {
    try { // try-catch块放于循环体内
      str[i] = i + "";
    } catch (ArrayIndexOutOfBoundsException e) {
      e.printStackTrace();
    }
  }
}

运行结果:

【Java】异常处理

当try-catch块在循环体外时:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
  String[] str = new String[2];
  try { // try-catch块放于循环体外
    for(int i = 0; i < 5; i++) {
      str[i] = i + "";
    }
  } catch (ArrayIndexOutOfBoundsException e) {
    e.printStackTrace();
  }
}

运行结果:

【Java】异常处理

— END —

【Java】异常处理