如何正确使用Java异常处理机制
个人博客地址 本文的目标并不是介绍Java异常处理机制相关概念和语法,如果你有这方面的需求,请参考我的“ Java异常 官方文档翻译系列”文章。本文的目标是如何正确使用Java异常处理机制。
第一节 异常处理概述在理想境界中,程序永远不会出现问题,用户输入的数据永远是正确的,逻辑没有任何问题 ,选择打开的文件也一定是存在的,内存永远是够用的……反正没有任何问题!但是一旦出现这些问题,如果处理不好,程序就不能正常运行了,用户就有可能再也不使用这个程序了。 要处理异常,必先知道异常发生的原因;要知道异常发生的原因,必先知道异常发生的场景。你的程序可能和任何其他实体交互的时候,都可能发生异常。Java程序中,都是利用方法(Method)和其他实体进行交互。所以异常的发生、抛出、声明和处理都是在方法内。下图是Java程序可能和其他实体交互的概要图: 图1:你的方法与其他实体交互概要图 如图1所示,你写的方法和外部实体交互大概可以分为五类:
Java方法和每一类实体进行交互时,都可能发生异常。 当和资源交互时,常常会因为资源不可用而发生异常,比如发生找不到文件、数据库连接错误、找不到类、找不到方法……等等状况。有可能是直接产生的,见图⑤处;有可能是间接产生的,比如图⑥处发生异常,Server Method把异常抛给Your Method,图③处就间接发生了异常。一般来说,你写的方法间接发生这类异常的可能性比直接发生要大得多,因为直接产生这类异常的方法在Java平台中已经提供了。对于这类异常,通常有以下几个特点:
这时,你的方法应该这样处理:
然后,你应该协调各方,促进资源恢复可用,消除异常。 当给用户方法(User Method )提供服务时,用户可能会传入一些不合法的数据(或者其他不恰当的使用方法),进而对程序的正常流程造成破坏。你的方法应该检查每一个输入数据,如果发现不合法的数据,马上阻止执行流程,并通知用户方法。 当调用服务方法(Server Method )时,有可能会发生两类异常。一类是你的使用方法不正确,导致服务中止;一类是服务方法出了异常,然后传递给你的方法。如果是第一种异常,你应该检查并修改你的方法逻辑,消除BUG。对于第二类异常,你要么写一个处理器处理,要么继续传递给上层方法。 当和系统环境交互时,有可能因为JVM参数设置不当,有可能因为程序产生了大量不必要的对象,也有可能因为硬故障(操作系统或硬件出了问题),导致整个程序不可用。当这类异常发生时,最终用户没法选择其他替代方案,操作到一半的数据会全部丢失。你的方法对这类异常一般没什么办法,既不能通过修改主流程逻辑来消除,也不能通过增加异常处理器来处理。所以通常你的方法对这类异常不需要做任何处理。但是你必须检查进程内的所有程序和系统环境是否正常,然后协调各方,修改BUG或恢复环境。 Java的异常都是发生在方法内,所以研究Java异常,要以你设计的方法为中心。我们以“你的方法 ”为中心,总结一下处理办法:当服务方法告诉“你的方法 ”的主流程逻辑有问题时,就要及时修复BUG来消除异常;当用户方法非法使用“你的方法”时,应该直接中止主流程,并通知用户方法,强迫用户方法使用正确的方式,防止问题蔓延;当服务方法传递一个异常给“你的方法”时,你要判断“你的方法”是否合适处理这个异常,如果不合适,传递给上层方法,如果合适,写一个异常处理器处理这个异常。当系统环境出了问题,“你的方法”什么也做不了。 刚才以“你的方法”为中心,总结了在“你的方法”内部的处理办法。现在以“你”为中心,总结一下方法外部的处理方法:当资源不可用的时候,你应该协调各方,恢复资源;当发生系统故障时,你应该协调各方,恢复系统。 现在,已经基本分析清楚了异常发生的原因,以及相应的应对方法。下一节正式介绍Java异常处理机制。
第二节 Java异常处理类Java把异常当做是破坏正常流程的一个事件,当事件发生后,就会触发处理机制。 Java有一套独立的异常处理机制,在遇到异常时,方法并不返回任何值(返回值属于正常流程),而是抛出一个封装了错误信息的对象。下图是Java异常处理机制类层级结构图: 图2:Java异常处理机制类层级结构图
2.1 Throwable所有的异常对象都派生于Throwable类的一个实例。 2.1.1 Throwable有五种构造方法:
备注:
2.1.2 Throwable的所有成员方法:
备注:
从图2可以看出,Throwable类只有两个直接继承者:Error和Exception。然后Exception又分为RuntimeException和Checked Exception。 2.2 Error在Java中, 由系统环境问题引起的异常,一般都继承于Error类。 对于Error类:
下列是Java平台中直接继承于Error的错误类型:
2.3 Exception在Java中,除了系统环境问题引起的异常,一般都继承于Exception类。Exception分为RuntimeException和Checked Exception。Checked Exception必须要捕获或声明。而RuntimeException不强制。 对于Exception类:
2.4 RuntimeException在Java中,由于接口方法使用不当造成的异常,一般属于RuntimeException,也就是运行时异常。 对于RuntimeException:
下列是Java平台中直接继承于RuntimeException的运行时异常:
2.5 Checked Exception在Java中,直接或间接因为“资源”问题引起的异常,一般属于检查异常(Checked Exception) 。检查异常继承于Exception,而不继承于RuntimeException。 对于检查异常:
下列是Java平台中直接继承于Exception的检查异常:
2.6 Uncheck ExceptionError和RuntimeException统称为非检查异常。两者的共同点就是都不被强制捕获或声明。实际上两者描述问题的范围完全没有交集。 2.7 总结所有的功能都在Throwable类里面实现了,子类只需要直接继承或间接继承它,并且加上需要的构造方法就行(一般而言,第一第二个构造方法是必须的,也可以全部加上),而且构造方法通常只需要一行代码:super(...),也就是说只要调用父类的构造方法就行了。Java把异常分为三类(Error,Checked Exception,RuntimeException),只是在语法层面上有不同的标记而已。它们自身拥有的功能一样,运行时系统处理它们的方式也是一样的(你也可以捕获或声明非检查异常),不同的是编译器对它们的区别对待(检查异常必须要在代码里处理,非检查异常就不需要),以及程序员对它们的区别对待(这需要程序员遵循良好的实践原则)。 这三类异常全部覆盖了第一节中所描述的异常发生场景,图1中,④⑤⑥处可能会发生Checked Exception,②③处既可能会发生RuntimeException也可能会发生Checked Exception,⑦⑧⑨处可能会发生Error。①处已经超出了Java异常处理机制的范畴(这属于容器要考虑的问题),通常在数据中加入返回码来通知异常信息。 理解了每一类异常对应的场景,很多人其实已经知道该怎么用了,不必往下看了。
第三节 Java异常处理执行流程探究首先设计两个方法,一个方法可能会抛出RuntimeException,一个方法可能会抛出Checked Exception。 public String runtimeServerMethod(String s) { if(s==null) { throw new RuntimeException("runtimeServerMethod方法的字符串不能为空"); } return s; }
private BufferedReader bufferedReader;
public String checkedServerMethod(String s) throws IOException { File file = new File(s); Reader reader = new FileReader(file); bufferedReader = new BufferedReader(reader); String result = bufferedReader.readLine(); return result; }
3.1 流程一public void currentMethod() { System.out.println("--------------------try-catch-before"); String result = this.runtimeServerMethod(null); System.out.println("--------------------result:"+result); System.out.println("--------------------try-catch-after"); }
执行结果:
分析: 违反了runtimeServerMethod方法的使用规则——入参不能为null,导致产生了一个运行时异常。主流程线程直接中断,后面的代码不再执行。 3.2 流程二public void currentMethod() { System.out.println("--------------------try-catch-before"); String result = null; try { result = this.runtimeServerMethod(null); System.out.println("--------------------result:"+result); } catch (Exception e) { System.out.println("--------------------in-catch"); } System.out.println("--------------------try-catch-after"); } 执行结果:
分析: RuntimeException也可以捕获处理(这是一种不好的实践),运行时系统并不会区分异常类型。异常发生以后,try代码块后面的代码不再执行,而是跳到catch代码块,线程不中断,执行完整个方法。 3.3 流程三public void currentMethod() { System.out.println("--------------------try-catch-before"); String result = null; result = this.runtimeServerMethod("Conform to the rules"); System.out.println("--------------------result:"+result); System.out.println("--------------------try-catch-after"); } 执行结果:
分析: 当符合服务方法的规则时,就不会抛出运行时异常。方法就可以正常执行完成。 3.4 流程四public void currentMethod() { System.out.println("--------------------try-catch-before"); String result = null; try { result = this.checkedServerMethod(""); System.out.println("--------------------result:"+result); } catch (IOException e) { System.out.println("--------------------in-catch"); } finally { System.out.println("--------------------in-finally"); if(bufferedReader!=null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("--------------------try-catch-after"); } 执行结果:
分析: 当调用了checkedServerMethod方法,并且发生了Checked Exception时,一定要捕获或声明该异常,否则编译不通过。上例中,异常发生后,try代码块后面的代码不再执行,跳到catch代码块,再执行finally代码块(在这里有关闭资源的操作),然后再执行其余部分。 3.5 流程五public void currentMethod() { System.out.println("--------------------try-catch-before"); String result = null; try { result = this.checkedServerMethod(""); System.out.println("--------------------result:"+result); } catch (IOException e) { System.out.println("--------------------in-catch"); return; } finally { System.out.println("--------------------in-finally"); if(bufferedReader!=null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("--------------------try-catch-after"); } 执行结果:
分析: 和流程四不同之处在于,catch代码块多了一条return语句。执行结果也相应发生了变化,try-catch-finally代码块后面的代码不再执行。而且值得注意的是,finally代码块的代码依然执行了。这就是finally代码块的意义。 3.6 流程六public void userMethod() { try { this.currentMethod(); } catch (IOException e) { e.printStackTrace(); } }
public void currentMethod() throws IOException { System.out.println("--------------------try-catch-before"); String result = null; try { result = this.checkedServerMethod(""); System.out.println("--------------------result:"+result); } catch (IOException e) { System.out.println("--------------------in-catch"); this.checkedServerMethod("catch"); } finally { System.out.println("--------------------in-finally"); if(bufferedReader!=null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("--------------------try-catch-after"); } 执行结果:
分析: 和流程五类似,只不过catch代码块不是返回一个正常值,而是抛出一个Checked Exception。 3.7 流程七public void userMethod() { try { this.currentMethod(); } catch (IOException e) { e.printStackTrace(); } } public void currentMethod() throws IOException { System.out.println("--------------------try-catch-before"); String result = null; try { result = this.checkedServerMethod(""); System.out.println("--------------------result:"+result); } catch (IOException e) { System.out.println("--------------------in-catch"); this.checkedServerMethod("catch"); } finally { System.out.println("--------------------in-finally"); if(bufferedReader!=null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } this.checkedServerMethod("finally"); } System.out.println("--------------------try-catch-after"); } 执行结果:
分析: 和流程六比起来,流程七在finally代码块也抛出了一个异常,最终在userMethod方法里面捕获到的是finally代码块的异常,catch代码块里抛出的异常被压抑了。 3.8 流程八public void userMethod() { try { this.currentMethod(); } catch (IOException e) { e.printStackTrace(); } }
public void currentMethod() throws IOException { System.out.println("--------------------try-catch-before"); String result = null; try { result = this.checkedServerMethod(""); System.out.println("--------------------result:"+result); }finally { System.out.println("--------------------in-finally"); if(bufferedReader!=null) { try { bufferedReader.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("--------------------try-catch-after"); } 执行结果:
|
|
来自: python_lover > 《待分类》