美文网首页Java 杂谈程序员
Java的Error和Exception

Java的Error和Exception

作者: PigeonSoar | 来源:发表于2018-06-28 23:11 被阅读82次

    程序在运行时难免会存在一些意外的情况的发生,只有正确的处理好意外情况,才能保证程序的可靠。Java提供了一套相对完善的异常处理机制,可以通过使用少量的代码达到可靠的效果。Java语言的设计者根据不同的异常情况分成了Error和Exception两类,该两类都继承了Throwable类,在Java中只有Throwable类能够抛出和捕获,因此它为Java异常处理机制的基础。

    Error

    Errror类描述了Java运行时系统的内部错误和资源耗尽错误,绝大部分的Error都会导致程序处于非正常的、不可恢复的状态,因为是非正常且不可恢复的,因此程序不应该抛出这种类型的错误。如果出现了这样的错误,除了通告给用户,并尽力使程序安全地终止外,再也无能为力了。但这种情况很少出现,常见的Error如:OutOfMemoryError, StackOverFlowError等。

    Exception

    Java语言规范将派生于Error类或RuntimeException类的所有异常称为unchecked异常,所有其他的异常称为checked异常。

    unchecked异常

    在Exception结构下,uncheckn异常继承于RuntimeException,也就是常说的运行时异常,如NullPointException,ArrayIndexOutOfBoundsException和illegalArgumentException等异常,该类异常在编译期不强制要求捕捉,通常是因为程序逻辑错误造成的,因此如果出现了该类异常,多半是你自己的问题,需要修改程序避免这类异常。

    PS:RuntimeException容易让人误解,事实上所有的异常都是发生在运行期。

    checked异常

    与unchecked异常的差别在于,该类异常在编译期就强制要求抛出或捕获,如IOException、InterruptedException等,需要使用try...catch...finally块或者try-with-resources等进行处理,或者通过throw直接向上层抛出异常。

    Exception实践

    仅捕获有必要的代码

    由于try-catch块会产生额外的性能开销,会影响JVM对代码进行优化,因此建议仅捕获有必要的代码段,尽量不要一个大的try包住整段的代码。同时,也意味着不要使用异常来控制代码的流程,远比使用if/else等控制语句要低效。

    捕获特定异常

    尽量不要捕获Exception这样的通用异常,而是应该捕获特地的异常,如在上述中使用IOException。直接捕获Exception会降低程序的可读性,并且我们需要保证程序不会捕获到我们不希望捕获的异常,比如,我们更希望RuntimeException被扩散出来,而不是被捕获。

    不要忽略异常

    ;有时候我们会认为某段代码根本不会发生异常,而对异常进行忽略(在catch块中不做任何操作)。如果我们不把异常抛出来或者输出到日志之类,那么一旦异常发生,程序可能在后续代码以不可控的方式结束,没人能够轻易判断是哪里以及什么原因产生了异常。

    输出异常信息到日志中

    try {
        ...
        OutputStream os= new FileOutputStream(file);
        os.write(byteArray);
    } catch (IOException e) {
        e.printStackTrace();
    }
    

    在实验代码中,我们经常直接输出异常信息到标准错误(STERR)中,但是在稍微复杂的环境中,这不是一个合适的选择,尤其是在分布式的系统中,你根本无法判断信息输出到了哪里。所以最好使用日志产品记录错误信息。

    考虑将异常上抛

    我们有时候会遇到捕获异常后不知道如何处理的情况,那么与其捕获异常,不如将异常直接抛出或者捕获后经过一定处理,构建一个新的异常抛出。往往在更高层面,因为有了清晰的业务逻辑,会更清楚合适的处理方式。

    注意try...finally块中的return

    public static void main(String[] args) {
            System.out.println(getValue());
        }
    
        public static int getValue() {
            int i = 1;
            try {
                return i;
            } finally {
                i ++;
                retutn i;
            }
        }
    

    在Java中,在try块中执行return语句前会执行finally块,如果在finally中也有一个return语句,那么try语句块中的return将不会被执行。如上述中结果将返回2。

    public static void main(String[] args) {
            System.out.println(getValue());
        }
    
        public static int getValue() {
            int i = 1;
            try {
                return i;
            } finally {
                i ++;
            }
        }
    

    再看这段代码,按照之前的逻辑,变量i在返回前先调用了finally块,i会变为2,但实际上结果任然为1.实际上,Java 虚拟机会把 finally 语句块作为 subroutine(子程序)直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch 语句块会保留其返回值到本地变量表(Local Variable Table)中。待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者。更加的详细内容请查看 Java关键字 Finally执行与break, continue, return等关键字的关系

    相关文章

      网友评论

        本文标题:Java的Error和Exception

        本文链接:https://www.haomeiwen.com/subject/fvfoyftx.html