java —— 异常(三)

_

问题引入看如下测试代码:

import java.io.*;

public class HideException {

//一个函数同时读取两个文件

public void readTwoFile() throws FileNotFoundException, IOException{

    BufferedReader br1 = null;

    BufferedReader br2 = null;

    FileReader fr = null;

    try{

        fr = new FileReader("A.txt"); //1

        br1 = new BufferedReader(fr); 

        int count = br1.read();    //2

        //process code1....

        fr = new FileReader("B.txt"); //3

        br2 = new BufferedReader(fr);

        count = br2.read(); //4

        //process code2

    }finally{

        if(br1 != null)

            br1.close(); //5

        if(br2 != null)

            br2.close(); //6

    }

}

//测试客户端

public static void main(String[] args){

    HideException he = new HideException();

    try {

        he.readTwoFile();

    } catch (FileNotFoundException e) {

        //...

        e.printStackTrace(); //7

    } catch (IOException e) {

        //...

        e.printStackTrace(); //8

    }

}

}

源码

  如代码片段3所示,readTwoFile用来读取两个文件。其中语句//1与语句//3有可能会抛出FileNotFoundException;语句//2、//4、//5、//6有可能会抛出IOException。按照对控制流的执行逻辑分析,上述代码中的finally子句是无论如何都会被执行的。当//3、//5同时发生异常时,会执行main函数中的哪条/哪些语句?

  答案揭晓:当//3产生了一个FileNotFoundException时,控制流会转向到finally子句,但是其中//5又发生了IOException,那么这个时候返回main调用端的将会是IOException而不是FileNotFoundException因为//3产生的异常被//5抛出的异常所覆盖了。为此,将执行main函数中的语句//8

重点一个函数尽管抛出了多个异常,但是只有一个异常可被传播到调用端。

  记住:最后被抛出的异常时唯一被调用端接收的异常,其他异常都会被吞没掩盖。如果调用端要知道造成失败的最初原因,程序之中就绝不能掩盖任何异常。

一、自定异常封装类

  上述的问题原因是异常被抛弃了为此,一个简单的想法就是我们需要把函数执行过程中被【丢弃】的异常保存下来即可。一个解决思路是:可以定义一个列表,用于包含所有的异常。在函数最后抛出一个异常,该异常包括了上述所有的异常。这样,我们就可以保证不会出现【丢弃】的现象。

  

  第一步:定义一个自定义的异常封装类,用于包含所有的抛出的异常。如下述代码所示:

代码片段1

  

  CustomException异常只是一个异常集合,可以容纳多个异常,但是它本身并非真正意义上的异常,它只是为了解决可一次抛出多个异常。

  第二步:改造业务代码,解决异常【丢弃】的问题。建立一个容纳可能抛出多个异常的容器,然后在可能抛出异常位置均把异常加入到容器内即可。如代码片段2所示。

代码片段2

  

public class HideException {

    // 自定义异常类

    class CustomeException extends Exception {

        // 此处省略,详见代码片段1

    }

    // 一个函数同时读取两个文件

    public void readTwoFile() throws CustomeException {

        BufferedReader br1 = null;

        BufferedReader br2 = null;

        FileReader fr = null;

        List<Throwable> list = new ArrayList<Throwable>();

        try {

            fr = new FileReader("A.txt"); // 1

            br1 = new BufferedReader(fr);

            int count = br1.read(); // 2

            // process code1....

            fr = new FileReader("B.txt"); // 3

            br2 = new BufferedReader(fr);

            count = br2.read(); // 4

            // process code2

        } catch (FileNotFoundException ffe) {

            list.add(ffe); //防止丢弃异常

        } catch (IOException ie) {

            list.add(ie);//防止丢弃异常

        } finally {

            if (br1 != null) {

                try {

                    br1.close();

                } catch (IOException ie) {

                    list.add(ie);//防止丢弃异常

                }

            }

            if (br2 != null) {

                try {

                    br2.close();

                } catch (IOException ie) {

                    list.add(ie);//防止丢弃异常

                }

            }

        }

        // 检查异常的数目

        if (list.size() > 0)

            throw new CustomeException(list);

    }

    // 测试客户端

    public static void main(String[] args) {

        HideException he = new HideException();

        try {

            he.readTwoFile();

        } catch (CustomeException ce) {

            // 异常处理代码

            // ......

        }

    }

}

业务代码

  上诉代码把每一个异常都添加到一个异常集合里,解决了异常丢弃异常,同时也可以将异常进行集中处理。

二、异常链

  定义:代码由一层层的函数调用组成。如果一个函数抛出了异常,一种方法是就地解决;一种方法是调用throw方法将异常抛出给上层调用函数;同理,上层调用函数仍然可以再次调用throw方法继续抛出该方法的异常;如此,就会产生一条由异常构成的异常链。

  为什么:对于有些异常如果采取就地解决,就会让上层调用函数不知道是什么原因引起了程序异常,为此就不便于程序排错或用户操作反馈。为此,需要异常链的层层传递,最终让有相应处理职责的函数进行异常处理。

  异常链实现异常传递如下代码:

public class ExceptionChainTest {

    class CustomeException extends Exception {

        //1 空构造函数

        public CustomeException() {

            super();

        }

        //2 定义异常原因

        public CustomeException(String message) {

            super(message);

        }

        //3 定义异常原因,并保留原始信息

        public CustomeException(String message, Throwable cause) {

            super(message, cause);

        }

        //4 保留原始信息

        public CustomeException(Throwable cause) {

            super(cause);

        }

    }

    //底层测试函数

    public void func2() throws Exception {

        throw new Exception("func2 exception ....");

    }

    //上层测试函数

    public void func1() throws Exception {

        try {

            func2();

        } catch (Exception ex) {

            throw new CustomeException("func1 exception");//5 

        }

    }

    //客户端测试函数

    public static void main(String[] args) {

        ExceptionChainTest test = new ExceptionChainTest();

        try {

            test.func1();

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

  第一步:我们首先自定义了一个异常类:CustomeException。该构造函数中的第//3与第//4种方式是实现异常链的核心,即保留了原始的异常信息。

  第二步,我们模拟了一个例子来说明是如何实现异常链传递的,函数调用链是:main函数->func1函数->func1函数。按照执行逻辑,最底层函数func2() 函数会抛出一个Exception类型的异常给上层func1( ) ,func1()函数没有对异常做具体的处理,只是重新生成了一个异常对象抛给main函数。

大家思考一分钟,想想会输出什么信息:

    

  觉得上面的信息是不是少了一点什么?是的,居然丢弃了最原始的引起异常的原因。老铁们想想可以怎么解决了?

解决问题的方法也很简单,就是把代码片段中的//5

        throw new CustomeException("func1 exception");//5

  替换为:

                             throw new CustomeException("func1 exception", ex); //6

  替换以后,程序运行结果为:

    

  OK!世界又回到了和谐状态,从抛出的异常中我们找到了造成异常的最原始原因。注意代码片段//5 中,丢失了原始异常信息,而修改后的//6 是真正的异常封装,保留了原始异常信息。

  重点:上层调用函数捕捉到异常后,可以对异常进行封装(如上例中第3、4种构造函数的方式)后再抛出,这样后续调用函数所获得的异常信息就不会丢失,进而就能获得产生异常的根本原因,以便程序员解决问题或反馈给使用用户。

三、小结

  1. 异常需要封装和传递,对待异常,我们不要“吞噬”异常,也不要直接抛出异常,可采取一个异常容器对代码执行过程中抛出的异常进行收集,最后反馈给调用端,如此就不会丢弃异常,方便用户获取产生异常的根本原因。该技术常用于一个函数可能会抛出多种异常的情况。
  2. 异常链也是一种传递异常的实用方法,该技术常用于有一定函数调用深度的业务场景。

本文来自该公众号——程序员Chatbook

     

原文地址:https://www.cnblogs.com/SacredOdysseyHD/p/8411567.html

时间: 2024-08-02 13:29:51

java —— 异常(三)的相关文章

有效处理Java异常三原则

Java中异常提供了一种识别及响应错误情况的一致性机制,有效地异常处理能使程序更加健壮.易于调试.异常之所以是一种强大的调试手段,在于其回答了以下三个问题: 什么出了错? 在哪出的错? 为什么出错? 在有效使用异常的情况下,异常类型回答了"什么"被抛出,异常堆栈跟踪回答了"在哪"抛出,异常信息回答了"为什么"会抛出,如果你的异常没有回答以上全部问题,那么可能你没有很好地使用它们.有三个原则可以帮助你在调试过程中最大限度地使用好异常,这三个原则是:

【JAVA教程】如何有效处理Java异常三原则

我们都知道,Java中异常提供了一种识别及响应错误情况的一致性机制,有效地异常处理能使程序更加健壮.易于调试.异常之所以是一种强大的调试手段,在于其回答了以下三个问题: 1.什么出了错? 2.在哪出的错? 3.为什么出错? 在有效使用异常的情况下,异常类型回答了"什么"被抛出,异常堆栈跟踪回答了"在哪"抛出,异常信息回答了"为什么"会抛出,如果你的异常没有回答以上全部问题,那么可能你没有很好地使用它们.有三个原则可以帮助你在调试过程中最大限度地使

java知识点三:异常

异常 一.概念 异常时程序中的一些错误,但并不是所以的错误都是异常,并且错误有时候是可以避免的. 异常体: Throwable:所以异常类的超类 Error:表示不希望被程序捕获,或者是程序无法处理的错误 Exception:表示用户程序可能捕捉的异常情况或者说可以处理的异常 其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常. Java异常又可以分为不受检查异常(Unchecked Exception)和检查异常(Checked Exception)

面试准备(三) Java 异常类层次结构

在Java中,异常分为受检查的异常,与运行时异常. 两者都在异常类层次结构中.这类容易出选择题 考试你是否掌握了异常类并清楚哪些异常类必须捕获 下面的图展示了Java异常类的继承关系. 图1 粉红色的是受检查的异常(checked exceptions),其必须被 try{}catch语句块所捕获,或者在方法签名里通过throws子句声明.受检查的异常必须在编译时被捕捉处理,命名为 CHecked Exception 是因为Java编译器要进行检查,Java虚拟机也要进行检查,以确保这个规则得到

Java异常总结

http://blog.csdn.net/hguisu/article/details/6155636 [-] 1 引子 2JAVA异常 4处理异常机制 1 捕获异常trycatch 和 finally try-catch语句 trycatch-finally语句 try-catch-finally 规则异常处理语句的语法规则 trycatchfinally语句块的执行顺序 2 抛出异常 throws抛出异常 使用throw抛出异常 4 Throwable类中的常用方法 5Java常见异常 ru

Java异常初步

所谓异常就是程序在运行时出现的不正常情况; 1.异常的体系: Throwable: 1) Error:通常指JVM出现重大问题如:运行的类不存在或者内存溢出等,这种异常是程序员无法处理的 2) Exception:在运行时运行出现的一些情况,可以通过try,catch,finally处理.比如在计算除法时把除数置0,参数传值是类型不符等. Java异常其实是对不正常情况的一种描述,并将其封装成对象 Java在 设计异常体系时,将容易出现的异常情况都封装成了对象 下面的代码在运行时会将除数置0,在

Atitit.js javascript异常处理机制与java异常的转换.js exception process Voae

Atitit.js javascript异常处理机制与java异常的转换.js exception processVoae 1. 1. javascript异常处理机制 1 2. 2. Web前后台异常的统一处理 1 3.  java异常转换为js异常 1 3. -------------详细代码 2 1. 1. javascript异常处理机制 Throw str Not throw error  ..cause ie,ff error obj is diff.. 2. 2. Web前后台异常

java 异常总结

一:异常结构图及基本概念 Throwable类:所有异常与错误的超类. Error类:程序无法处理的错误,表示运行应用程序中较严重问题,不需要捕获 Exception类:程序本身可以处理的异常,可以捕获 RuntimeException及其子类:可以捕获此异常,也可以不捕获.建议不捕获. 非运行时异常:必须捕获的异常. 二 try...catch...finally 1 try{ 2 //监控有可能出现异常的代码 3 }catch(Exception e){ 4 //捕获异常后执行的代码 5 }

Java异常(1)

一.要达到的效果 如果出现错误而是某些操作没有完成,程序应该: (1)返回到一种安全状态,并能够让用户执行一些其他的命令. (2)允许用户保存所有操作的结果,并以适当的方式终止程序. 异常处理的任务就是将控制权从错误产生的地方转移到能够处理这种情况的错误处理器   二.异常分类: 1.Throwable - ---------Error -     - -     ------- XXXX -      - -      ----- XXXXX ---Exception -      - -  

浅谈java异常[Exception]

本文转自:focusJ 一. 异常的定义 在<java编程思想>中这样定义 异常:阻止当前方法或作用域继续执行的问题.虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常.绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败.之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意. 记得当初学习java的时候,异常总是搞不太清楚,不知道这个异常是什么意思,为什么会有这个机制?但是随着知识的积累逐渐也