Java try catch异常处理详解(附带实例)
try-catch 组合的语法如下,将可能发生异常的代码块放在 try 所对应的大括号里面,catch 指定异常类型并将发生异常时要执行的代码块放在对应的大括号里面,其中 e 是异常发生时生成的指定异常类型的对象,该对象包含了所发生异常的相关信息。
try{ 可能发生异常的代码块 }catch(异常类型 e){ 发生异常e时执行的代码块 }下图是 try-catch 组合的执行流程图:

图 1 try-catch 流程图
首先尝试执行 try 包含的代码块,如果整个过程没有发生异常则直接跳过 catch 代码块往下继续执行;如果发生异常则会看 catch 指定的异常类型是否与发生的异常相匹配,如果匹配则表示成功捕获到了异常,此时将执行 catch 包含的代码块并往下继续执行;最后,如果异常未匹配则表示捕获异常失败,此时程序终止。
编写程序时,如果在可能出现异常的地方不进行异常处理的话会怎样呢?
下面看一段代码:
public class ExceptionTest { public static void main(String[] args) { String s = null; System.out.println(s.length()); System.out.println("这里会被执行吗?"); } }字符串变量 s 为 null,此时调用字符串类的 length() 方法会抛出 NullPointerException(空指针异常),因为我们并没有创建字符串对象。最终程序会在抛出异常的地方停止运行,“这里会被执行吗?”并不会被输出,即抛出异常的代码后面的所有代码都不会被执行。
如果我们没有编写捕获异常的代码,当异常发生时,由 JVM 进行处理。JVM 将首先输出异常相关信息(包括哪个线程和哪种异常类型),然后输出堆栈信息(异常发生处往上层层调用的方法),最后使程序终止。
执行程序后输出如下异常相关信息:
Exception in thread "main" java.lang.NullPointerException at com.java.exception.ExceptionTest.main(ExceptionTest.java:7)
对于上面的示例,对其进行异常捕获并处理看看会怎样:
public class ExceptionTest { public static void main(String[] args) { String s = null; try { System.out.println(s.length()); } catch (NullPointerException e) { System.out.println(e); } System.out.println("这里会被执行吗?"); } }输出结果为:
java.lang.NullPointerException
这里会被执行吗?
当捕获到异常时,可以通过“catch(xxx)”括号中的异常对象来获取相关的异常信息。比如对于“catch(xxx Exception e)”,可以通过以下三个方法来获取异常信息:
- toString():获取异常相关信息描述;
- getMessage():获取异常简要信息;
- printStackTrace():打印异常信息及堆栈信息。
这三个方法都属于 Throwable 类的方法,该类是所有异常类的顶层父类,所以所有异常类型的对象都能调用这三个方法。
例如:
public class ExceptionTest { public static void main(String[] args) { int[] arr = new int[10]; try { arr[10] = 100; } catch (IndexOutOfBoundsException e) { System.out.println("输出e.toString():"); System.out.println(e.toString()); System.out.println("输出e.getMessage():"); System.out.println(e.getMessage()); System.out.println("直接调用e.printStackTrace():"); e.printStackTrace(); } } }输出结果为:
输出e.toString():
java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10
输出e.getMessage():
Index 10 out of bounds for length 10
直接调用e.printStackTrace():
java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10
at com.java.exception.ExceptionTest.main(ExceptionTest.java:8)
我们重新聚焦“catch(xxx)”括号中的内容,假设我们通过“catch(Exception e)”直接指定捕获的异常类型为 Exception,那么由于它是所有异常的父类,因此能捕获到所有异常。但是如果我们在下面实例中将其指定为“catch (IndexOutOfBoundsException e)”,则执行程序时会导致捕获异常失败,这是因为产生的异常类型并非 IndexOutOfBoundsException 类或其子类。
public class ExceptionTest { public static void main(String[] args) { String s = null; try { System.out.println(s.length()); } catch (IndexOutOfBoundsException e) { System.out.println(e); } System.out.println("这里会被执行吗?"); } }输出结果为:
Exception in thread "main" java.lang.NullPointerException
at com.java.exception.ExceptionTest.main(ExceptionTest.java:8)
Java try-multi-catch组合
前面介绍的 try-catch 组合只能指定捕获一种异常类型,如果想要实现对多种异常类型的捕获则可以通过多个 catch 模块来实现,每个 catch 模块指定一种异常类型和异常处理逻辑,这种方式我们称为 try-multi-catch 组合。try-multi-catch 组合的语法如下:
try{ 可能发生异常的代码块 }catch(异常类型1 e1){ 发生异常e1时执行的代码块 }catch(异常类型2 e2){ 发生异常e2时执行的代码块 }catch(异常类型3 e3){ 发生异常e3时执行的代码块 }...它是在 try-catch 组合的基础上往后面不断添加 catch 模块来实现对多种异常类型的捕获和处理。注意,异常发生时多个 catch 模块就好比多个条件分支,从第一个 catch 模块开始往下匹配,直到匹配对应的异常类型。
下图是 try-multi-catch 组合的执行流程图:

图 2 try-multi-catch流程图
最开始尝试执行 try 包含的代码块,如果没有发生异常则直接跳过所有 catch 代码块往下继续执行。反之,如果发生了异常则会判断是否是异常类型 1,如果是则进入第一个 catch 代码块。要是异常类型 1 未匹配则尝试匹配异常类型 2,如果匹配则进入第二个 catch 代码块。以此类推,往下匹配每个 catch 指定的异常类型并处理。假如最终没有任何异常类型能匹配,那么表示捕获异常失败,程序将终止。
下面来看看如何使用 try-multi-catch 组合,即在 try-catch 组合的基础上将需要捕获的异常通过多个 catch 连起来:
public class ExceptionTest { public static void main(String[] args) { int[] arr = new int[10]; try { arr[10] = 100; } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }程序中对 IndexOutOfBoundsException、NullPointerException 和 Exception 三个异常进行捕获,最终匹配了 IndexOutOfBoundsException 异常。
运行程序,结果为:
java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 10
at com.java.exception.ExceptionTest.main(ExceptionTest.java:8)
多个 catch 模块要按匹配范围从小到大顺序排列,如果不按这个顺序排列会怎样呢?下面实例中,我们把匹配范围最大的 Exception 类放在第一个 catch 模块中,然后放其他异常类,结果发现编译时直接报错了。
public class ExceptionTest { public static void main(String[] args) { int[] arr = new int[10]; try { arr[10] = 100; } catch (Exception e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } } }报错信息如下,意思是后面的异常捕获都没必要了,因为 Exception 类对应的 catch 模块会优先捕获到这些异常:
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
Unreachable catch block for NullPointerException. It is already handled by the catch block for Exception
Unreachable catch block for IndexOutOfBoundsException. It is already handled by the catch block for Exception
at com.java.exception.ExceptionTest.main(ExceptionTest.java:11)
总结一下,使用 try-multi-catch 组合时需要注意如下 4 点:
- try-multi-catch 组合中一次只会发生一个异常,所以异常最多只能被一个 catch 模块捕获并处理。
- 如果 try 代码块中包含发生多个异常的代码,那么在第一个异常发生后其余代码就不会被执行了,所以 try 代码块中只可能发生最早出现的那个异常。
- 当异常发生时,多个 catch 模块将按顺序依次被匹配,直到匹配第一个 catch 模块。
- 多个 catch 模块要按匹配范围从小到大顺序排列,比如有三个异常类的继承关系 Exception- RuntimeException-NullPointerException,那么范围从小到大依次是 NullPointerException 类、RuntimeException 类和 Exception 类。
Java try-catch-finally组合
在使用 try-catch 组合捕获异常时,有些代码不管是放在 try 代码块还是 catch 代码块中都无法保证它一定会被执行,然而有些代码是必须执行的,比如打开网络连接和磁盘文件后必须要对其关闭从而回收资源,这时就需要把资源关闭和回收的代码放到 finally 模块中,这种方式我们称为 try-catch-finally 组合。try-catch-finally 组合的语法如下,它是在 try-catch 组合(当然也可以是 try-multi-catch 组合)的基础上在后面添加一个 finally 模块来实现。finally 后的大括号里指定必须执行的代码块,这里的代码块不管异常有没有被成功处理都会被执行。
try{ 可能发生异常的代码块 }catch(异常类型 e){ 发生异常e时执行的代码块 }finally{ 必执行的代码块 }下图是 try-catch-finally 组合的执行流程图,其中大部分流程与 try-catch 组合一样,唯一不同的地方在于不管有没有发生异常或者有没有成功捕获异常都将执行 finally 代码块。

图 3 try-catch-finally流程图
对于 finally 代码块,我们主要关注三种情况。
第一种是当 try 代码块没有发生异常时,将在执行完 try 代码块后执行 finally 代码块。例如:
public class ExceptionTest { public static void main(String[] args) { try { int a = 20 / 1; System.out.println("a = " + a); } catch (ArithmeticException e) { System.out.println("捕获到异常并进行处理"); } finally { System.out.println("必执行代码"); } } }输出结果为:
a = 20
必执行代码
第二种是当成功捕获到异常时,将在执行完 catch 代码块后执行 finally 代码块:
public class ExceptionTest { public static void main(String[] args) { try { int a = 20 / 0; System.out.println("a = " + a); } catch (ArithmeticException e) { System.out.println("捕获到异常并进行处理"); } finally { System.out.println("必执行代码"); } } }输出结果为:
捕获到异常并进行处理
必执行代码
第三种是当未能成功捕获到异常时,由于 catch 模块未捕获到异常,因此将由 JVM 输出异常信息,在此之前会先执行 finally 代码块。例如:
public class ExceptionTest { public static void main(String[] args) { try { int a = 20 / 0; System.out.println("a = " + a); } catch (IndexOutOfBoundsException e) { System.out.println("捕获到异常并进行处理"); } finally { System.out.println("必执行代码"); } } }输出结果为:
必执行代码
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.java.exception.ExceptionTest.main(ExceptionTest.java:7)