首页 > Java入门教程(旧版) > Java异常处理
Java try catch finally(捕获和处理异常)
Java 异常处理机制通过五个关键字来实现,分别是 try、catch、throw、throws 和 finally:
本节我们我们只讲解 try catch finally 三个关键字,让读者掌握异常处理技能;我们将在《Java声明和抛出异常》一节中继续讲解 throw 和 throws 关键字,让读者再掌握异常抛出技能。
如果 try 语句块中发生异常,那么一个相应的异常对象就会被拋出,然后 catch 语句就会依据所拋出异常对象的类型进行捕获,并处理。处理之后,程序会跳过 try 语句块中剩余的语句,转到 catch 语句块后面的第一条语句开始执行。
如果 try 语句块中没有异常发生,那么 try 块正常结束,后面的 catch 语句块被跳过,程序将从 catch 语句块后的第一条语句开始执行。
两点注意:
在 catch 语句块中,我们可以使用以下 3 个方法输出相应的异常信息:
【示例1】编写一个录入学生姓名、年龄和性别的程序,要求能捕捉年龄不为数字时的异常。在这里使用 try catch 语句来实现,具体代码如下:
运行结果如下所示:
注意:当捕获的多个异常类之间存在父子关系时,捕获异常时一般先捕获子类,再捕获父类。所以子类异常必须在父类异常的前面,否则子类捕获不到。
【示例2】多重 catch 的使用:
在 try 代码块中第 16 行代码调用 FileInputStream 构造方法可能会发生 FileNotFoundException 异常。第 20 行代码调用 BufferedReader 输入流的 readLine() 方法可能会发生 IOException 异常。FileNotFoundException 异常是 IOException 异常的子类,应该先捕获 FileNotFoundException 异常,见代码第 27 行;后捕获 IOException 异常,见代码第 30 行。
如果将 FileNotFoundException 和 IOException 捕获顺序调换,那么捕获 FileNotFoundException 异常代码块将永远不会进入,FileNotFoundException 异常处理永远不会执行。 上述代码第 33 行 ParseException 异常与 IOException 和 FileNotFoundException 异常没有父子关系,所以捕获 ParseException 异常位置可以随意放置。
finally 语句可以与上面介绍的 try catch 语句块匹配使用,语法格式如下:
一般情况下,无论是否有异常拋出,都会执行 finally 语句块中的语句,执行流程如图 1 所示。

图 1 try catch finally 语句执行流程图
try catch finally 语句块的执行情况可以细分为以下 3 种情况:
除非在 try 块、catch 块中调用了退出虚拟机的方法
【示例3】当 Windows 系统启动之后,即使不作任何操作,在关机时都会显示“谢谢使用”。下面编写 Java 程序使用 try catch finally 语句这个过程,具体代码如下:
声明:《Java系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。
- try 用来捕获异常;
- catch 用来匹配异常类型;
- finally 用来“善后”,它就像 default 语句,在任何情况下都会执行;
- throw 用来抛出异常;
- throws 用来声明可能会出现的异常。
本节我们我们只讲解 try catch finally 三个关键字,让读者掌握异常处理技能;我们将在《Java声明和抛出异常》一节中继续讲解 throw 和 throws 关键字,让读者再掌握异常抛出技能。
Java try catch 语句
Java try catch 语句的一般格式如下:try { // 可能发生异常的语句 } catch(ExceptionType e) { // 处理异常语句 }在以上语法中,把可能引发异常的语句封装在 try 语句块中,用以捕获可能发生的异常。catch 后面的
( )
中放的是匹配的异常类,用来指明 catch 语句可以处理的异常类型,也即发生异常时产生异常类的实例化对象。如果 try 语句块中发生异常,那么一个相应的异常对象就会被拋出,然后 catch 语句就会依据所拋出异常对象的类型进行捕获,并处理。处理之后,程序会跳过 try 语句块中剩余的语句,转到 catch 语句块后面的第一条语句开始执行。
如果 try 语句块中没有异常发生,那么 try 块正常结束,后面的 catch 语句块被跳过,程序将从 catch 语句块后的第一条语句开始执行。
两点注意:
-
try 和 catch 后面的花括号
{ }
不可以省略,即使 try 块里只有一行代码,也不可省略这个花括号。 - try 块中声明的变量只是代码块内的局部变量,它只在 try 块内有效,其它地方不能访问该变量。
在 catch 语句块中,我们可以使用以下 3 个方法输出相应的异常信息:
- printStackTrace() 方法:指出异常的类型、性质、栈层次及出现在程序中的位置(关于 printStackTrace 方法的使用可参考《Java的异常跟踪栈》一节)。
- getMessage() 方法:输出错误的性质。
- toString() 方法:给出异常的类型与性质。
【示例1】编写一个录入学生姓名、年龄和性别的程序,要求能捕捉年龄不为数字时的异常。在这里使用 try catch 语句来实现,具体代码如下:
import java.util.Scanner; public class Demo{ public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("----学生信息录入(www.weixueyuan.net)----"); String name = ""; // 获取学生姓名 int age = 0; // 获取学生年龄 String sex = ""; // 获取学生性别 try { System.out.println("请输入学生姓名:"); name = scanner.next(); System.out.println("请输入学生年龄:"); age = scanner.nextInt(); System.out.println("请输入学生性别:"); sex = scanner.next(); } catch (Exception e) { e.printStackTrace(); System.out.println("输入有误!"); } System.out.println("姓名:" + name); System.out.println("年龄:" + age); } }上述代码在 main() 方法中使用 try catch 语句来捕获异常,将可能发生异常的
age = scanner.nextlnt();
代码放在了 try 块中,在 catch 语句中指定捕获的异常类型为 Exception,并调用异常对象的 printStackTrace() 方法输出异常信息。运行结果如下所示:
----学生信息录入(www.weixueyuan.net)----
请输入学生姓名:
魏雪原
请输入学生年龄:
110a
java.util.InputMismatchException
输入有误!
at java.base/java.util.Scanner.throwFor(Scanner.java:939)
at java.base/java.util.Scanner.next(Scanner.java:1594)
at java.base/java.util.Scanner.nextInt(Scanner.java:2258)
at java.base/java.util.Scanner.nextInt(Scanner.java:2212)
at Demo.main(Demo.java:14)
姓名:魏雪原
年龄:0
多重 catch 语句
如果 try 代码块中有很多语句会发生异常,而且发生的异常种类又很多。那么可以在 try 后面跟有多个 catch 代码块。多 catch 代码块语法如下:try { // 可能会发生异常的语句 } catch(ExceptionType e) { // 处理异常语句 } catch(ExceptionType e) { // 处理异常语句 } catch(ExceptionType e) { // 处理异常语句 ... }在多个 catch 代码块的情况下,当一个 catch 代码块捕获到一个异常时,其它的 catch 代码块就不再进行匹配。
注意:当捕获的多个异常类之间存在父子关系时,捕获异常时一般先捕获子类,再捕获父类。所以子类异常必须在父类异常的前面,否则子类捕获不到。
【示例2】多重 catch 的使用:
import java.io.*; import java.text.*; import java.util.Date; public class Demo{ public static void main(String[] args) { Date date = readDate(); System.out.println("读取的日期 = " + date); } public static Date readDate() { FileInputStream readfile = null; InputStreamReader ir = null; BufferedReader in = null; try { readfile = new FileInputStream("www.weixueyuan.net.txt"); ir = new InputStreamReader(readfile); in = new BufferedReader(ir); // 读取文件中的一行数据 String str = in.readLine(); if (str == null) { return null; } DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); Date date = df.parse(str); return date; } catch (FileNotFoundException e) { System.out.println("处理FileNotFoundException..."); e.printStackTrace(); } catch (IOException e) { System.out.println("处理IOException..."); e.printStackTrace(); } catch (ParseException e) { System.out.println("处理ParseException..."); e.printStackTrace(); } return null; } }上述代码通过 Java I/O(输入输出)流技术从文件 readme.txt 中读取字符串,然后解析成为日期。由于 Java I/O 技术还没有介绍,大家先不要关注 I/O 技术细节,只看调用它们时方法会发生的异常就可以了。
在 try 代码块中第 16 行代码调用 FileInputStream 构造方法可能会发生 FileNotFoundException 异常。第 20 行代码调用 BufferedReader 输入流的 readLine() 方法可能会发生 IOException 异常。FileNotFoundException 异常是 IOException 异常的子类,应该先捕获 FileNotFoundException 异常,见代码第 27 行;后捕获 IOException 异常,见代码第 30 行。
如果将 FileNotFoundException 和 IOException 捕获顺序调换,那么捕获 FileNotFoundException 异常代码块将永远不会进入,FileNotFoundException 异常处理永远不会执行。 上述代码第 33 行 ParseException 异常与 IOException 和 FileNotFoundException 异常没有父子关系,所以捕获 ParseException 异常位置可以随意放置。
Java try catch finally 语句
在实际开发中,根据 try catch 语句的执行过程,try 语句块和 catch 语句块有可能不被完全执行,而有些处理代码则要求必须执行。例如,程序在 try 块里打开了一些物理资源(如数据库连接、网络连接和磁盘文件等),这些物理资源都必须显式回收。Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只回收堆内存中对象所占用的内存。所以为了确保一定能回收 try 块中打开的物理资源,异常处理机制提供了 finally 代码块,并且 Java 7 之后提供了自动资源管理(Automatic Resource Management)技术。
finally 语句可以与上面介绍的 try catch 语句块匹配使用,语法格式如下:
try { // 可能会发生异常的语句 } catch(ExceptionType e) { // 处理异常语句 } finally { // 清理代码块 }对于以上格式,无论是否发生异常(除特殊情况外),finally 语句块中的代码都会被执行。此外,finally 语句也可以和 try 语句匹配使用,其语法格式如下:
try { // 逻辑代码块 } finally { // 清理代码块 }使用 try-catch-finally 语句时需注意以下几点:
- 异常处理语法结构中只有 try 块是必需的,也就是说,如果没有 try 块,则不能有后面的 catch 块和 finally 块;
- catch 块和 finally 块都是可选的,但 catch 块和 finally 块至少出现其中之一,也可以同时出现;
- 可以有多个 catch 块,捕获父类异常的 catch 块必须位于捕获子类异常的后面;
- 不能只有 try 块,既没有 catch 块,也没有 finally 块;
- 多个 catch 块必须位于 try 块之后,finally 块必须位于所有的 catch 块之后。
- finally 与 try 语句块匹配的语法格式,此种情况会导致异常丢失,所以不常见。
一般情况下,无论是否有异常拋出,都会执行 finally 语句块中的语句,执行流程如图 1 所示。

图 1 try catch finally 语句执行流程图
try catch finally 语句块的执行情况可以细分为以下 3 种情况:
- 如果 try 代码块中没有拋出异常,则执行完 try 代码块之后直接执行 finally 代码块,然后执行 try catch finally 语句块之后的语句。
- 如果 try 代码块中拋出异常,并被 catch 子句捕捉,那么在拋出异常的地方终止 try 代码块的执行,转而执行相匹配的 catch 代码块,之后执行 finally 代码块。如果 finally 代码块中没有拋出异常,则继续执行 try catch finally 语句块之后的语句;如果 finally 代码块中拋出异常,则把该异常传递给该方法的调用者。
- 如果 try 代码块中拋出的异常没有被任何 catch 子句捕捉到,那么将直接执行 finally 代码块中的语句,并把该异常传递给该方法的调用者。
除非在 try 块、catch 块中调用了退出虚拟机的方法
System.exit(int status)
,否则不管在 try 块或者 catch 块中执行怎样的代码,出现怎样的情况,异常处理的 finally 块总会执行。【示例3】当 Windows 系统启动之后,即使不作任何操作,在关机时都会显示“谢谢使用”。下面编写 Java 程序使用 try catch finally 语句这个过程,具体代码如下:
import java.util.Scanner; public class Demo{ public static void main(String[] args) { Scanner input = new Scanner(System.in); System.out.println("Windows 系统已启动!"); String[] pros = { "记事本", "计算器", "浏览器" }; try { // 循环输出pros数组中的元素 for (int i = 0; i < pros.length; i++) { System.out.println(i + 1 + ":" + pros[i]); } System.out.println("是否运行程序:"); String answer = input.next(); if (answer.equals("y")) { System.out.println("请输入程序编号:"); int no = input.nextInt(); System.out.println("正在运行程序[" + pros[no - 1] + "]"); } } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("www.weixueyuan.net 谢谢您的使用!"); } } }上述代码在 main() 方法中使用 try catch finally 语句模拟了系统的使用过程。当系统启动之后显示提示语,无论是否运行了程序,或者在运行程序时出现了意外,程序都将执行 finally 块中的语句,即显示“谢谢使用!”。输出时的结果如下所示。
Windows 系统已启动! 1:记事本 2:计算器 3:浏览器 是否运行程序: y 请输入程序编号: 2 正在运行程序[计算器] www.weixueyuan.net 谢谢您的使用!
Windows 系统已启动! 1:记事本 2:计算器 3:浏览器 是否运行程序: y 请输入程序编号: 5 www.weixueyuan.net 谢谢您的使用! java.lang.ArrayIndexOutOfBoundsException: 4 at text.text.main(text.java:23)
Windows 系统已启动! 1:记事本 2:计算器 3:浏览器 是否运行程序: asdfasd www.weixueyuan.net 谢谢您的使用!
声明:《Java系列教程》为本站“54笨鸟”官方原创,由国家机构和地方版权局所签发的权威证书所保护。