基本的try-cathc-finally异常处理

原文链接 作者:Jakob Jenkov 译者:fangqiang08(fangqiang08@gmail.com)

这一小节概述了try-catch-finally 语句是怎样处理错误的,文中的例子是Java的,但是同样的规则也适用于C#。java和C#中异常的唯一区别就是C#中没有已检查异常。已检查异常和未检查异常将在后面小节更加详细地介绍。

程序中的异常表明一些错误或者异常情况发生了,异常如果没有被处理,继续程序流程是没有意义的。一个方法可能会因为各种原因抛出异常,比如输入参数是无效的(当期待获得正数时输入了负数等。)

调用栈

下面介绍调用栈。调用栈是指从当前的方法开始一直到main方法处的方法调用序列。如果方法A调用方法B,方法B调用方法C,则调用栈如下所示:

    A
    B
    C

当方法C返回后,调用栈仅仅包含A和B,如果方法B再调用方法D,则调用栈如下所示:

    A
    B
    D

理解调用栈对理解异常传播是非常重要的。从抛出异常的方法开始,异常根据调用栈传播,直到调用栈中某个方法捕获该异常。更多细节稍后讨论。

抛出异常

如果一个方法要抛出异常,那么要在方法签名中声明这个异常,然后在方法里包含一条throw语句。下面是一个例子:

    public void divide(int numberToDivide, int numberToDivideBy)
    throws BadNumberException{
        if(numberToDivideBy == 0){
            throw new BadNumberException("Cannot divide by 0");
        }
        return numberToDivide / numberToDivideBy;
    }

当一个异常被抛出时,方法在抛出异常处停止运行。throw语句后的任何语句都将不会被执行了。在上面这个例子中,如果抛出BadNumberException异常,那么”return numberToDivide / numberToDivideBy;”这条语句将不会被执行。当异常被catch语句块捕获时,程序才会继续执行。捕获异常稍后解释。

只要在方法签名中做了声明,你可以抛出任何异常。你也可以创建自己的异常。异常是继承自java.lang.Exception的标准java类或者任何其他的内置异常类。如果一个方法声明抛出异常A,那么方法抛出异常A的子类异常也是合法的。

捕获异常

如果一个方法调用了另一个会抛出已检查异常的方法,那么这个调用方法必须要么传递这个异常,要么捕获这个异常。捕获异常是利用try-catch语句块来实现的。下面是一个例子:

    public void callDivide(){
        try {
            int result = divide(2,1);
            System.out.println(result);
        } catch (BadNumberException e) {
            //do something clever with the exception
            System.out.println(e.getMessage());
        }
        System.out.println("Division attempt done");
    }

当抛出异常时,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()方法在这种情况下的例子:

public void callDivide() throws BadNumberException{
       int result = divide(2,1);
       System.out.println(result);
}

注意try-catch语句块不见了,callDivide 方法声明它会抛出一个BadNumberException异常。如果divide方法里抛出一个异常,程序仍将被中断。因此如果divide方法里抛出一个异常,”System.out.println(result);” 语句将不会被执行。但是这样的话callDivide 方法中(因为抛出异常而中断的的)程序执行不会被恢复。异常会被传播到调用callDivide的方法中。直到调用栈中的某个catch语句块捕获了这个异常,程序执行才会恢复。根据调用栈,从抛出异常的方法到捕获异常的方法之间的所有方法的运行都将停止在异常抛出或者传播的代码处。

捕获IOException示例

如果一个异常在try语句块中被抛出,程序的顺序执行将会被中断,控制流将直接跳转到catch语句块。代码将会因为异常在几个地方被中断:

public void openFile(){
        try {
            // constructor may throw FileNotFoundException
            FileReader reader = new FileReader("someFile");
            int i=0;
            while(i != -1){
                //reader.read() may throw IOException
                i = reader.read();
                System.out.println((char) i );
            }
            reader.close();
            System.out.println("--- File End ---");
        } catch (FileNotFoundException e) {
            //do something clever with the exception
        } catch (IOException e) {
            //do something clever with the exception
        }
    }

如果reader.read()方法调用抛出一个IO异常,下面的System.out.println((char) i );语句不会被执行。最后的reader.close() 和System.out.println(“— File End —“);语句也不会被执行。程序直接调转到catch(IOException e){ … }语句块处。如果new FileReader(“someFile”); 构造器中调用抛出一个异常,那么try语句块中的代码都不会被执行了。

传播IOException示例

下面的代码仍然以上面的方法为例,不过这里没有捕获异常:

public void openFile() throws IOException {
        FileReader reader = new FileReader("someFile");
        int i=0;
        while(i != -1){
            i = reader.read();
            System.out.println((char) i );
        }
        reader.close();
        System.out.println("--- File End ---");
    }

如果reader.read()  方法抛出一个异常,程序将会停止运行,异常根据调用栈传播到调用openFile()方法的方法处。如果这个调用方法有try-catch语句块,异常将在这里被捕获。如果这个调用方法也只是抛出异常,这个调用方法的运行将在调用openFile()方法处中断,异常根据调用栈往外传播。异常就是这样根据调用栈往外传播,直到某个方法或者java虚拟机捕获了这个异常。

Finally

你也可以在try-catch语句块后增加一个finally语句块。不论try还是catch语句块中抛出异常,finally语句块中的代码将永远执行。如果代码里的try或者catch语句块中有个return语句,finally语句块中的代码将会在方法返回之前执行。finally示例见下:

public void openFile(){
        FileReader reader = null;
        try {
            reader = new FileReader("someFile");
            int i=0;
            while(i != -1){
                i = reader.read();
                System.out.println((char) i );
            }
        } catch (IOException e) {
            //do something clever with the exception
        } finally {
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    //do something clever with the exception
                }
            }
            System.out.println("--- File End ---");
        }
    }

不论try或者catch语句块中是否有异常抛出,finally语句块中的代码都将被执行。这个例子表明不论try、catch语句块中的程序运行情况如何,文件读取始终会被关闭。

注意:如果finally语句块中抛出一个异常,并且没有被捕获,finally语句块就像try、catch语句块抛出异常一样也会被中断运行。这就是为什么上面的例子中finally语句块中的reader.close() 方法也被try-catch语句块包裹原因:

} finally {
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    //do something clever with the exception
                }
            }
            System.out.println("--- File End ---");
        }

通过这样,System.out.println(“— File End —“);语句将会永远被执行。更准确地说是没有未检查异常被抛出时是这样。更多关于已检查异常和未检测异常的知识将在后面章节介绍。

你的程序中不需要同时又catch、finally语句块。try语句块后可以仅仅有catch语句块或者finally语句块,但是try语句块后既没有catch语句块也没有finally语句块是不行的。下面的代码不会捕获异常而是让异常根据调用栈往外传播,但是由于finally语句块的存在,即是有异常抛出,程序仍旧能关闭打开的文件。

public void openFile() throws IOException {
        FileReader reader = null;
        try {
            reader = new FileReader("someFile");
            int i=0;
            while(i != -1){
                i = reader.read();
                System.out.println((char) i );
            }
        } finally {
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    //do something clever with the exception
                }
            }
            System.out.println("--- File End ---");
        }
    }

注意上面catch语句块不见了。

捕获异常还是传播异常?

你可能疑问:程序中抛出的异常是应该捕获还是让其传播?这取决于具体情况。在许多应用程序中,除了告诉用户请求操作失败,你不能对异常做其他更多事情。在这种情况下,你可以在调用栈中的第一个方法里捕获所有的、或者大多数的异常。在传播异常时,你仍然可以处理异常(通过finally语句)。比如,在一个web应用程序中的数据库连接出现了错误,即使你所能做的只是告诉用户这个操作失败,你仍然应该在finally语句中关闭这个数据库连接。最终如何处理异常也取决于你的程序中抛出的是已检查异常还是未检查异常。关于已检查异常、未检查异常,后面将有更多文章介绍。

时间: 2024-11-03 21:19:28

基本的try-cathc-finally异常处理的相关文章

用实例分析PHP5异常处理

<?php /** * ■㈠PHP5的异常处理 * * PHP 5 添加了类似于其它语言的异常处理模块.在 PHP 代码中所产生的异常可被 throw * 语句抛出并被 catch 语句捕获.需要进行异常处理的代码都必须放入 try 代码块内,以 * 便捕获可能存在的异常.每一个 try 至少要有一个与之对应的 catch.使用多个 catch * 可以捕获不同的类所产生的异常.当 try 代码块不再抛出异常或者找不到 catch 能匹配 * 所抛出的异常时,PHP 代码就会在跳转到最后一个 c

php的异常处理基础方法

以下是引用片段: 1.允许一个方法给出一个出错标记给客户代码 2.提供程序错误的详细信息 3.让你同时判断多个出错条件,将你的错误报告和程序处理流程分开. 4.返回值必须是独立的类型,不会与正常返回的类型相混淆   两个实例掌握PHP异常处理 //例[1]用 try...catch  代码如下 复制代码 /* PDO连接mysql数据库,如果没看过PDO,先看下PDO的构造函数,要不跳过例1看例2 */ $dsn = 'mysql:host=localhost;dbname=testdb'; $

异常处理:android中添加按钮事件,出现NullPointerException

问题描述 异常处理:android中添加按钮事件,出现NullPointerException 解决方案 在maincsactivity的oncreate的72行找,有没有对象为null 解决方案二: 对象为空,要么你的按钮对象还没初始化,要么点击代码里有空对象,你调试一下便知 解决方案三: 空指针异常,没什么难找的,Debug一下即可很快找到: 解决方案四: 空指针异常,按钮对象没有初始化 解决方案五: 可能你声明了一个View,但是你没有findViewById,或者findViewById

异常处理汇总-后端系列

异常处理汇总:http://www.cnblogs.com/dunitian/p/4599258.html SignalR   01.SignalR:"System.Reflection.TargetInvocationException"类型的未经处理的异常在 mscorlib.dll 中发生 http://www.cnblogs.com/dunitian/p/5232229.html 02.SignalR代理对象异常:Uncaught TypeError: Cannot read

一个用于J2EE应用程序的异常处理框架

在大多数Java项目中,大部分代码都是样板代码.异常处理就属于此类代码.即使业务逻辑只有3到4行代码,用于异常处理的代码也要占10到20行.本文将讨论如何让异常处理保持简单和直观,使开发人员可以专心于开发业务逻辑,而不是把时间浪费在编写异常处理的样板代码上.本文还将说明用于在J2EE环境中创建和处理异常的基础知识和指导原则,并提出了一些可以使用异常解决的业务问题.本文将使用Struts框架作为表示实现,但该方法适用于任何表示实现. 使用checked和unchecked异常的场景 您是否曾经想过

PHP错误处理及异常处理笔记

 给新人总结一下PHP的错误处理. PHP提供了错误处理和日志记录的功能. 这些函数允许你定义自己的错误处理规则,以及修改错误记录的方式. 这样,你就可以根据自己的需要,来更改和加强错误输出信息以满足实际需要. 通过日志记录功能,你可以将信息直接发送到其他日志服务器,或者发送到指定的电子邮箱(或者通过邮件网关发送),或者发送到操作系统日志等,从而可以有选择的记录和监视你的应用程序和网站的最重要的部分. 错误报告功能允许你自定义错误反馈的级别和类型,可以是简单的提示信息或者使用自定义的函数进行处理

JAVA【异常二】异常处理机制

Java中异常提供了一种识别及响应错误情况的一致性机制,有效地异常处理能使程序更加健壮.易于调试.异常之所以是一种强大的调试手段,在于其回答了以下三个问题: 什么出了错? 在哪出的错? 为什么出错? 在有效使用异常的情况下,异常类型回答了"什么"被抛出,异常堆栈跟踪回答了"在哪"抛出,异常信息回答了"为什么"会抛出.   在Java 应用程序中,异常处理机制为:抛出异常,捕捉异常. 抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运

前端-java web 全局异常处理 如何配置

问题描述 java web 全局异常处理 如何配置 怎样配置java web 前端在访问该项目中根本不存在的方法的时候 跳转异常页面啊? 解决方案 java web异常处理java web 中几种异常处理Java web的几种异常处理 解决方案二: 看你使用什么技术了.要是ssh框架,直接使用struts2或者spring mvc的拦截器就行,如果是servlet,那就自己写一个拦截器. 解决方案三: 前端访问项目中不存在的方法是什么意思? 页面调用脚本方法不存在的时候浏览器是会执行异常的. 解

Spring4.1新特性——异步调度和事件机制的异常处理

Spring 4.1对异步调用提供了AsyncResult及SuccessCallback.FailureCallback.和异常处理的支持:对事件调度也提供了相应的异常处理器.   1.事件调度异常处理器 1.1.定义异常处理器: Java代码   public class MyErrorHandler implements ErrorHandler {       @Override       public void handleError(Throwable throwable) {  

异常处理汇总-服务器系列

异常处理汇总:http://www.cnblogs.com/dunitian/p/4599258.html   公司/本地服务器系列 1.IIS7.5+WebConfig实现页面伪静态和301重定向 http://www.cnblogs.com/dunitian/p/4324341.html 2.服务器环境~某个页面无法访问的处理 http://www.cnblogs.com/dunitian/p/4325027.html 3.win7+IIS7下木有4.0框架问题的解决方案 http://ww