基本的try-cathc-finally异常处理
这一小节概述了try-catch-finally 语句是怎样处理错误的,文中的例子是Java的,但是同样的规则也适用于C#。java和C#中异常的唯一区别就是C#中没有已检查异常。已检查异常和未检查异常将在后面小节更加详细地介绍。
程序中的异常表明一些错误或者异常情况发生了,异常如果没有被处理,继续程序流程是没有意义的。一个方法可能会因为各种原因抛出异常,比如输入参数是无效的(当期待获得正数时输入了负数等。)
调用栈
下面介绍调用栈。调用栈是指从当前的方法开始一直到main方法处的方法调用序列。如果方法A调用方法B,方法B调用方法C,则调用栈如下所示:
1
|
A
|
2
|
B
|
3
|
C
|
当方法C返回后,调用栈仅仅包含A和B,如果方法B再调用方法D,则调用栈如下所示:
1
|
A
|
2
|
B
|
3
|
D
|
理解调用栈对理解异常传播是非常重要的。从抛出异常的方法开始,异常根据调用栈传播,直到调用栈中某个方法捕获该异常。更多细节稍后讨论。
抛出异常
如果一个方法要抛出异常,那么要在方法签名中声明这个异常,然后在方法里包含一条throw语句。下面是一个例子:
1
|
public void divide( int numberToDivide, int numberToDivideBy)
|
2
|
throws BadNumberException{
|
3
|
if (numberToDivideBy == 0 ){
|
4
|
throw new BadNumberException( "Cannot divide by 0" );
|
5
|
}
|
6
|
return numberToDivide / numberToDivideBy;
|
7
|
}
|
当一个异常被抛出时,方法在抛出异常处停止运行。throw语句后的任何语句都将不会被执行了。在上面这个例子中,如果抛出BadNumberException异常,那么”return numberToDivide / numberToDivideBy;”这条语句将不会被执行。当异常被catch语句块捕获时,程序才会继续执行。捕获异常稍后解释。
只要在方法签名中做了声明,你可以抛出任何异常。你也可以创建自己的异常。异常是继承自java.lang.Exception的标准java类或者任何其他的内置异常类。如果一个方法声明抛出异常A,那么方法抛出异常A的子类异常也是合法的。
捕获异常
如果一个方法调用了另一个会抛出已检查异常的方法,那么这个调用方法必须要么传递这个异常,要么捕获这个异常。捕获异常是利用try-catch语句块来实现的。下面是一个例子:
01
|
public void callDivide(){
|
02
|
try {
|
03
|
int result = divide( 2 , 1 );
|
04
|
System.out.println(result);
|
05
|
} catch (BadNumberException e) {
|
06
|
//do something clever with the exception
|
07
|
System.out.println(e.getMessage());
|
08
|
}
|
09
|
System.out.println( "Division attempt done" );
|
10
|
}
|
当抛出异常时,catch语句中的BadNumberException 类型参数e就代表从divide方法中抛出的异常。
如果try语句块中任何被调用的方法和执行的语句都没有抛出异常,catch语句块仅仅被忽略,不会被执行。
如果try语句块中抛出异常,例如在divide方法中抛出异常,调用方法(即callDrive方法)中的程序流将会像divide方法中的程序流一样被中断。程序流将在调用栈中某个能够捕获这个异常的catch语句块处恢复。上面例子中,如果一个异常从divide方法中抛出,那么“System.out.println(result);” 语句将不会被执行。这时程序将会在catch (BadNumberException e) { }语句块里恢复执行。
如果在ctach语句块里抛出了一个没有被捕获的异常,那么catch语句块的运行将会像try语句中块抛出异常一样被中断。
当catch语句块执行完,程序将会执行catch语句块后面的所有语句。上面例子中的”System.out.println(“Division attempt done”);” 语句将永远会被执行。
异常传播
你也没有必要捕获其他方法中抛出的异常。如果你在方法抛出异常的地方不能对异常做任何事,你可以让方法根据调用栈将这个异常传播到调用这个方法的其他方法处。如果你这样做,调用这个将抛出异常方法的其他方法必须要在方法签名中声明抛出这个异常。下面是callDivide()方法在这种情况下的例子:
1
|
public void callDivide() throws BadNumberException{
|
2
|
int result = divide( 2 , 1 );
|
3
|
System.out.println(result);
|
4
|
}
|
注意try-catch语句块不见了,callDivide 方法声明它会抛出一个BadNumberException异常。如果divide方法里抛出一个异常,程序仍将被中断。因此如果divide方法里抛出一个异常,”System.out.println(result);” 语句将不会被执行。但是这样的话callDivide 方法中(因为抛出异常而中断的的)程序执行不会被恢复。异常会被传播到调用callDivide的方法中。直到调用栈中的某个catch语句块捕获了这个异常,程序执行才会恢复。根据调用栈,从抛出异常的方法到捕获异常的方法之间的所有方法的运行都将停止在异常抛出或者传播的代码处。
捕获IOException示例
如果一个异常在try语句块中被抛出,程序的顺序执行将会被中断,控制流将直接跳转到catch语句块。代码将会因为异常在几个地方被中断:
01
|
public void openFile(){
|
02
|
try {
|
03
|
// constructor may throw FileNotFoundException
|
04
|
FileReader reader = new FileReader( "someFile" );
|
05
|
int i= 0 ;
|
06
|
while (i != - 1 ){
|
07
|
//reader.read() may throw IOException
|
08
|
i = reader.read();
|
09
|
System.out.println(( char ) i );
|
10
|
}
|
11
|
reader.close();
|
12
|
System.out.println( "--- File End ---" );
|
13
|
} catch (FileNotFoundException e) {
|
14
|
//do something clever with the exception
|
15
|
} catch (IOException e) {
|
16
|
//do something clever with the exception
|
17
|
}
|
18
|
}
|
如果reader.read()方法调用抛出一个IO异常,下面的System.out.println((char) i );语句不会被执行。最后的reader.close() 和System.out.println(“— File End —“);语句也不会被执行。程序直接调转到catch(IOException e){ … }语句块处。如果new FileReader(“someFile”); 构造器中调用抛出一个异常,那么try语句块中的代码都不会被执行了。
传播IOException示例
下面的代码仍然以上面的方法为例,不过这里没有捕获异常:
01
|
public void openFile() throws IOException {
|
02
|
FileReader reader = new FileReader( "someFile" );
|
03
|
int i= 0 ;
|
04
|
while (i != - 1 ){
|
05
|
i = reader.read();
|
06
|
System.out.println(( char ) i );
|
07
|
}
|
08
|
reader.close();
|
09
|
System.out.println( "--- File End ---" );
|
10
|
}
|
如果reader.read() 方法抛出一个异常,程序将会停止运行,异常根据调用栈传播到调用openFile()方法的方法处。如果这个调用方法有try-catch语句块,异常将在这里被捕获。如果这个调用方法也只是抛出异常,这个调用方法的运行将在调用openFile()方法处中断,异常根据调用栈往外传播。异常就是这样根据调用栈往外传播,直到某个方法或者java虚拟机捕获了这个异常。
Finally
你也可以在try-catch语句块后增加一个finally语句块。不论try还是catch语句块中抛出异常,finally语句块中的代码将永远执行。如果代码里的try或者catch语句块中有个return语句,finally语句块中的代码将会在方法返回之前执行。finally示例见下:
01
|
public void openFile(){
|
02
|
FileReader reader = null ;
|
03
|
try {
|
04
|
reader = new FileReader( "someFile" );
|
05
|
int i= 0 ;
|
06
|
while (i != - 1 ){
|
07
|
i = reader.read();
|
08
|
System.out.println(( char ) i );
|
09
|
}
|
10
|
} catch (IOException e) {
|
11
|
//do something clever with the exception
|
12
|
} finally {
|
13
|
if (reader != null ){
|
14
|
try {
|
15
|
reader.close();
|
16
|
} catch (IOException e) {
|
17
|
//do something clever with the exception
|
18
|
}
|
19
|
}
|
20
|
System.out.println( "--- File End ---" );
|
21
|
}
|
22
|
}
|
不论try或者catch语句块中是否有异常抛出,finally语句块中的代码都将被执行。这个例子表明不论try、catch语句块中的程序运行情况如何,文件读取始终会被关闭。
注意:如果finally语句块中抛出一个异常,并且没有被捕获,finally语句块就像try、catch语句块抛出异常一样也会被中断运行。这就是为什么上面的例子中finally语句块中的reader.close() 方法也被try-catch语句块包裹原因:
01
|
} finally {
|
02
|
if (reader != null ){
|
03
|
try {
|
04
|
reader.close();
|
05
|
} catch (IOException e) {
|
06
|
//do something clever with the exception
|
07
|
}
|
08
|
}
|
09
|
System.out.println( "--- File End ---" );
|
10
|
}
|
通过这样,System.out.println(“— File End —“);语句将会永远被执行。更准确地说是没有未检查异常被抛出时是这样。更多关于已检查异常和未检测异常的知识将在后面章节介绍。
你的程序中不需要同时又catch、finally语句块。try语句块后可以仅仅有catch语句块或者finally语句块,但是try语句块后既没有catch语句块也没有finally语句块是不行的。下面的代码不会捕获异常而是让异常根据调用栈往外传播,但是由于finally语句块的存在,即是有异常抛出,程序仍旧能关闭打开的文件。
01
|
public void openFile() throws IOException {
|
02
|
FileReader reader = null ;
|
03
|
try {
|
04
|
reader = new FileReader( "someFile" );
|
05
|
int i= 0 ;
|
06
|
while (i != - 1 ){
|
07
|
i = reader.read();
|
08
|
System.out.println(( char ) i );
|
09
|
}
|
10
|
} finally {
|
11
|
if (reader != null ){
|
12
|
try {
|
13
|
reader.close();
|
14
|
} catch (IOException e) {
|
15
|
//do something clever with the exception
|
16
|
}
|
17
|
}
|
18
|
System.out.println( "--- File End ---" );
|
19
|
}
|
20
|
}
|
注意上面catch语句块不见了。
捕获异常还是传播异常?
你可能疑问:程序中抛出的异常是应该捕获还是让其传播?这取决于具体情况。在许多应用程序中,除了告诉用户请求操作失败,你不能对异常做其他更多事情。在这种情况下,你可以在调用栈中的第一个方法里捕获所有的、或者大多数的异常。在传播异常时,你仍然可以处理异常(通过finally语句)。比如,在一个web应用程序中的数据库连接出现了错误,即使你所能做的只是告诉用户这个操作失败,你仍然应该在finally语句中关闭这个数据库连接。最终如何处理异常也取决于你的程序中抛出的是已检查异常还是未检查异常。关于已检查异常、未检查异常,后面将有更多文章介绍。
- 转载自 并发编程网 - ifeve.com
基本的try-cathc-finally异常处理相关推荐
- 复习心得 JAVA异常处理
java中的异常处理机制主要依赖于try,catch,finally,throw,throws五个关键字.其中, try关键字后紧跟一个花括号括起来的代码块(花括号不可省略)简称为try块.里面放置可 ...
- [导入]PHP5的异常处理
<?php /** * ■㈠PHP5的异常处理 * * PHP 5 添加了类似于其它语言的异常处理模块.在 PHP 代码中所产生的异常可被 throw * 语句抛出并被 catch 语句捕获.需 ...
- PHP5异常处理,PHP5异常处理分析实例
PHP5异常处理分析实例 导语:PHP 5 添加了类似于其它语言的异常处理模块.下面的是百分网小编为大家搜集的用实例分析PHP5异常处理实例,希望对你能有所帮助. <?php /** * ■㈠P ...
- Go语言的错误异常处理机制及其应用
一.背景 在日常编写golang程序或阅读别人的golang代码时,我们总会看到如下的一堆代码块: xx, err = func(xx) if err != nil {//do sth. to tac ...
- SpringBoot (二) :全局异常处理设置
说在前面 在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有 ...
- 2021年大数据常用语言Scala(三十四):scala高级用法 异常处理
目录 异常处理 捕获异常 抛出异常 异常处理 Scala中 无需在方法上声明异常 来看看下面一段代码. def main(args: Array[String]): Unit = {val i = 1 ...
- Java基础(七)--Exception异常处理
发现错误的理想时机是程序运行之前(编译期),然后不太现实,很多异常无法被发现(特别是业务上的数据),需要在运行时解决. 错误恢复机制保证代码健壮性的方式,异常处理在程序中很常见,也是必须的,必须考虑有 ...
- 零基础学习python_异常处理(32-33课)
我们写完python执行的时候是不是经常会遇到报错,而且报错都是大片红字,这样给别人的感受就是你写的程序怎么老是出问题啊,这样我们还咋么混下去呢?于是乎,就有了异常处理的东东. python的try语 ...
- Spring Security 实战干货:自定义异常处理
Spring Security 实战干货:自定义异常处理 转自:https://www.cnblogs.com/felordcn/p/12142514.html 文章目录 1. 前言 2. Sprin ...
- C++ 笔记(20)— 异常处理(抛出异常、捕获异常)
异常提供了一种转移程序控制权的方式. C++ 异常处理涉及到三个关键字: try . catch . throw . throw : 当问题出现时,程序会抛出一个异常.这是通过使用 throw 关键字 ...
最新文章
- Redis 那些故障转移、高可用方案
- linux安装grub
- nagios结合pnp4nagios图表
- TensorFlow之conv2d函数解析
- 【PMCAFF大咖分享会】揭秘大数据驱动下的京东供应链体系
- YonStore应用+摩天知识,用友树立云生态新标杆
- 0726------Linux基础----------线程池
- JavaScript基础01
- (网页)中的简单的遮罩层
- 给控件做数字签名之三:进行数字签名
- Egret入门学习日记 --- 第十二篇(书中 5.1节 内容)
- Crackme 25
- 中移动入侵防御设备集采,总限价1.6亿;爱立信斩获95个5G商用合同
- Requests库爬虫详解
- 查找书籍!!!查找书籍!!查找书籍!
- 一例所有文件都打不开故障的数据恢复过程(转)
- 11款最受欢迎的亚马逊卖家工具
- 回溯法解01背包问题(最通俗易懂,附C++代码)
- 【实时数仓】热度关键词接口、项目整体部署流程
- 基于Java+SpringBoot+vue的口腔管家平台设计与实现