java

位置:IT落伍者 >> java >> 浏览文章

Java异常处理原理及应用


发布日期:2019年02月15日
 
Java异常处理原理及应用

readLine 方法有时产生 IOException如何处理潜在的故障?编译器需要捕获声明IOException

捕获 (catch)指当 readLine 方法产生错误时截获该错误并处理和记录该问题声明 (declare)指错误可能引发 IOException并通知调用该方法的任何代码可能产生异常

若要捕获异常必须添加一个特殊的处理代码块来接收和处理 IOException于是程序改为如下

import javaio*; public class EchoInputHandle { public static void main(String args[]){ Systemoutprintln(Enter text to echo:); InputStreamReader isr = new InputStreamReader(Systemin); BufferedReader inputReader = new BufferedReader(isr); try{ String inputLine = inputReaderreadLine(); Systemoutprintln(Read: + inputLine); } catch(IOException exc){ Systemoutprintln(Exception encountered: + exc); } } } 新添的代码块包含关键字 try 和 catch(第 行)表示要读取输入若成功则正常运行若读取输入时错误则捕获问题(由 IOException 对象表示)并采取相应措施在本例采用的处理方式是输出异常

若不准备捕获 IOException仅声明异常则要特别指定 main 方法可能出错而且特别说明可能产生 IOException于是程序改为如下

import javaio*; public class EchoInputDeclare { public static void main(String args[]) throws IOException{ Systemoutprintln(Enter text to echo:); InputStreamReader isr = new InputStreamReader(Systemin); BufferedReader inputReader = new BufferedReader(isr); String inputLine = inputReaderreadLine(); Systemoutprintln(Read: + inputLine); } } 从上面的这个简单的例子中我们可以看出异常处理在 Java 代码开发中不能被忽视

Java 异常以及异常处理

可将 Java 异常看作是一类消息它传送一些系统问题故障及未按规定执行的动作的相关信息异常包含信息以将信息从应用程序的一部分发送到另一部分

编译语言为何要处理异常?为何不在异常出现位置随时处理具体故障?因为有时候我们需要在系统中交流错误消息以便按照统一的方式处理问题有时是因为有若干处理问题的可能方式但您不知道使用哪一种此时可将处理异常的任务委托给调用方法的代码调用者通常更能了解问题来源的上下文能更好的确定恢复方式

必定在运行的 Java 应用程序的一些类或对象中产生异常出现故障时发送者将产生异常对象异常可能代表 Java 代码出现的问题也可能是 JVM 的相应错误或基础硬件或操作系统的错误

异常本身表示消息指发送者传给接收者的数据负荷首先异常基于类的类型来传输有用信息很多情况下基于异常的类既能识别故障本因并能更正问题其次异常还带有可能有用的数据(如属性)

在处理异常时消息必须有接收者否则将无法处理产生异常的底层问题

在上例中异常产生者是读取文本行的 BufferedReader在故障出现时将在 readLine 方法中构建 IOException 对象异常接收者是代码本身EchoInputHandle 应用程序的 trycatch 结构中的 catch 块是异常的接收者它以字符串形式输出异常将问题记录下来

Java 异常类的层次结构

在我们从总体上了解异常后我们应该了解如何在 Java 应用程序中使用异常即需要了解 Java 类的层次结构

必定在运行的 Java 应用程序的一些类或对象中产生异常出现故障时发送者将产生异常对象异常可能代表 Java 代码出现的问题也可能是 JVM 的相应错误或基础硬件或操作系统的错误

异常本身表示消息指发送者传给接收者的数据负荷首先异常基于类的类型来传输有用信息很多情况下基于异常的类既能识别故障本因并能更正问题其次异常还带有可能有用的数据(如属性)

在处理异常时消息必须有接收者否则将无法处理产生异常的底层问题

在上例中异常产生者是读取文本行的 BufferedReader在故障出现时将在 readLine 方法中构建 IOException 对象异常接收者是代码本身EchoInputHandle 应用程序的 trycatch 结构中的 catch 块是异常的接收者它以字符串形式输出异常将问题记录下来

Java 异常类的层次结构

在我们从总体上了解异常后我们应该了解如何在 Java 应用程序中使用异常即需要了解 Java 类的层次结构

必定在运行的 Java 应用程序的一些类或对象中产生异常出现故障时发送者将产生异常对象异常可能代表 Java 代码出现的问题也可能是 JVM 的相应错误或基础硬件或操作系统的错误

异常本身表示消息指发送者传给接收者的数据负荷首先异常基于类的类型来传输有用信息很多情况下基于异常的类既能识别故障本因并能更正问题其次异常还带有可能有用的数据(如属性)

在处理异常时消息必须有接收者否则将无法处理产生异常的底层问题

在上例中异常产生者是读取文本行的 BufferedReader在故障出现时将在 readLine 方法中构建 IOException 对象异常接收者是代码本身EchoInputHandle 应用程序的 trycatch 结构中的 catch 块是异常的接收者它以字符串形式输出异常将问题记录下来

Java 异常类的层次结构

在我们从总体上了解异常后我们应该了解如何在 Java 应用程序中使用异常即需要了解 Java 类的层次结构

在 Java 中所有的异常都有一个共同的祖先 Throwable(可抛出)Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性

Throwable 有两个重要的子类Exception(异常)和 Error(错误)二者都是 Java 异常处理的重要子类各自都包含大量子类

Exception(异常)是应用程序中可能的可预测可恢复问题一般大多数异常表示中度到轻度的问题异常一般是在特定环境下产生的通常出现在代码的特定方法和操作中在 EchoInput 类中当试图调用 readLine 方法时可能出现 IOException 异常

Error(错误)表示运行应用程序中较严重问题大多数错误与代码编写者执行的操作无关而表示代码运行时 JVM(Java 虚拟机)出现的问题例如当 JVM 不再有继续执行操作所需的内存资源时将出现 OutOfMemoryError

Exception 类有一个重要的子类 RuntimeExceptionRuntimeException 类及其子类表示JVM 常用操作引发的错误例如若试图使用空值对象引用除数为零或数组越界则分别引发运行时异常(NullPointerExceptionArithmeticException)和 ArrayIndexOutOfBoundException

Java 异常的处理

在 Java 应用程序中对异常的处理有两种方式处理异常和声明异常

处理异常trycatch 和 finally

若要捕获异常则必须在代码中添加异常处理器块这种 Java 结构可能包含 个部分

都有 Java 关键字下面的例子中使用了 trycatchfinally 代码结构

import javaio*; public class EchoInputTryCatchFinally { public static void main(String args[]){ Systemoutprintln(Enter text to echo:); InputStreamReader isr = new InputStreamReader(Systemin); BufferedReader inputReader = new BufferedReader(isr); try{ String inputLine = inputReaderreadLine(); Systemoutprintln(Read: + inputLine); } catch(IOException exc){ Systemoutprintln(Exception encountered: + exc); } finally{ Systemoutprintln(End ); } } 其中

try 块将一个或者多个语句放入 try 时则表示这些语句可能抛出异常编译器知道可能要发生异常于是用一个特殊结构评估块内所有语句

catch 块当问题出现时一种选择是定义代码块来处理问题catch 块的目的便在于此catch 块是 try 块所产生异常的接收者基本原理是一旦生成异常则 try 块的执行中止JVM 将查找相应的 JVM

finally 块还可以定义 finally 块无论运行 try 块代码的结果如何该块里面的代码一定运行在常见的所有环境中finally 块都将运行无论 try 块是否运行完无论是否产生异常也无论是否在 catch 块中得到处理finally 块都将执行

trycatchfinally 规则

必须在 try 之后添加 catch 或 finally 块try 块后可同时接 catch 和 finally 块但至少有一个块

必须遵循块顺序若代码同时使用 catch 和 finally 块则必须将 catch 块放在 try 块之后

catch 块与相应的异常类的类型相关

一个 try 块可能有多个 catch 块若如此则执行第一个匹配块

可嵌套 trycatchfinally 结构

在 trycatchfinally 结构中可重新抛出异常

除了下列情况总将执行 finally 做为结束JVM 过早终止(调用 Systemexit(int))在 finally 块中抛出一个未处理的异常计算机断电失火或遭遇病毒攻击

声明异常

若要声明异常则必须将其添加到方法签名块的结束位置下面是一个实例

public void errorProneMethod(int input) throws javaioIOException { //Code for the methodincluding one or more method //calls that may produce an IOException } 这样声明的异常将传给方法调用者而且也通知了编译器该方法的任何调用者必须遵守处理或声明规则声明异常的规则如下

必须声明方法可抛出的任何可检测异常(checked exception)

非检测性异常(unchecked exception)不是必须的可声明也可不声明

调用方法必须遵循任何可检测异常的处理和声明规则若覆盖一个方法则不能声明与覆盖方法不同的异常声明的任何异常必须是被覆盖方法所声明异常的同类或子类

Java 异常处理的分类

Java 异常可分为可检测异常非检测异常和自定义异常

可检测异常

可检测异常经编译器验证对于声明抛出异常的任何方法编译器将强制执行处理或声明规则例如sqlExecption 这个异常就是一个检测异常你连接 JDBC 时不捕捉这个异常编译器就通不过不允许编译

非检测异常

非检测异常不遵循处理或声明规则在产生此类异常时不一定非要采取任何适当操作编译器不会检查是否已解决了这样一个异常例如一个数组为 个长度当你使用下标为3时就会产生数组下标越界异常这个异常 JVM 不会进行检测要靠程序员来判断有两个主要类定义非检测异常RuntimeException 和 Error

Error 子类属于非检测异常因为无法预知它们的产生时间若 Java 应用程序内存不足则随时可能出现 OutOfMemoryError起因一般不是应用程序的特殊调用而是 JVM 自身的问题另外Error 一般表示应用程序无法解决的严重问题

RuntimeException 类也属于非检测异常因为普通 JVM 操作引发的运行时异常随时可能发生此类异常一般是由特定操作引发但这些操作在 Java 应用程序中会频繁出现因此它们不受编译器检查与处理或声明规则的限制

自定义异常

自定义异常是为了表示应用程序的一些错误类型为代码可能发生的一个或多个问题提供新含义可以显示代码多个位置之间的错误的相似性也可以区分代码运行时可能出现的相似问题的一个或者多个错误或给出应用程序中一组错误的特定含义例如对队列进行操作时有可能出现两种情况空队列时试图删除一个元素满队列时试图添加一个元素则需要自定义两个异常来处理这两种情况

Java 异常处理的原则和忌讳

Java 异常处理的原则

尽可能的处理异常

要尽可能的处理异常如果条件确实不允许无法在自己的代码中完成处理就考虑声明异常如果人为避免在代码中处理异常仅作声明则是一种错误和依赖的实践

具体问题具体解决

异常的部分优点在于能为不同类型的问题提供不同的处理操作有效异常处理的关键是识别特定故障场景并开发解决此场景的特定相应行为为了充分利用异常处理能力需要为特定类型的问题构建特定的处理器块

记录可能影响应用程序运行的异常

至少要采取一些永久的方式记录下可能影响应用程序操作的异常理想情况下当然是在第一时间解决引发异常的基本问题不过无论采用哪种处理操作一般总应记录下潜在的关键问题别看这个操作很简单但它可以帮助您用很少的时间来跟蹤应用程序中复杂问题的起因

根据情形将异常转化为业务上下文

若要通知一个应用程序特有的问题有必要将应用程序转换为不同形式若用业务特定状态表示异常则代码更易维护从某种意义上讲无论何时将异常传到不同上下文(即另一技术层)都应将异常转换为对新上下文有意义的形式

Java 异常处理的忌讳

一般不要忽略异常

在异常处理块中一项最危险的举动是不加通告地处理异常如下例所示

try{ ClassforName(businessdomainCustomer); } catch (ClassNotFoundException exc){} 经常能够在代码块中看到类似的代码块有人总喜欢在编写代码时简单快速地编写空处理器块自我安慰地宣称准备在后期添加恢复代码但这个后期变成了无期

这种做法有什么坏处?如果异常对应用程序的其他部分确实没有任何负面影响这未尝不可但事实往往并非如此异常会扰乱应用程序的状态此时这样的代码无异于掩耳盗铃

这种做法若影响较轻则应用程序可能出现怪异行为例如应用程序设置的一个值不见了 或 GUI 失效若问题严重则应用程序可能会出现重大问题因为异常未记录原始故障点难以处理如重复的 NullPointerExceptions

如果采取措施记录了捕获的异常则不可能遇到这个问题实际上除非确认异常对代码其余部分绝无影响至少也要作记录进一步讲永远不要忽略问题否则风险很大在后期会引发难以预料的后果

不要使用覆盖式异常处理块

另一个危险的处理是覆盖式处理器(blanket handler)该代码的基本结构如下

try{ // … } catch(Exception e){ // … } 使用覆盖式异常处理块有两个前提之一

代码中只有一类问题

这可能正确但即便如此也不应使用覆盖式异常处理捕获更具体的异常形式有利物弊

单个恢复操作始终适用

这几乎绝对错误几乎没有哪个方法能放之四海而皆准能应对出现的任何问题

分析下这样编写代码将发生的情况只要方法不断抛出预期的异常集则一切正常但是如果抛出了未预料到的异常则无法看到要采取的操作当覆盖式处理器对新异常类执行千篇一律的任务时只能间接看到异常的处理结果如果代码没有打印或记录语句则根本看不到结果

更糟糕的是当代码发生变化时覆盖式处理器将继续作用于所有新异常类型并以相同方式处理所有类型

一般不要把特定的异常转化为更通用的异常

将特定的异常转换为更通用异常时一种错误做法一般而言这将取消异常起初抛出时产生的上下文在将异常传到系统的其他位置时将更难处理见下例

try{ // Errorprone code } catch(IOException e){ String msg = If you didn t have a problem beforeyou do now!; throw new Exception(msg); } 因为没有原始异常的信息所以处理器块无法确定问题的起因也不知道如何更正问题

不要处理能够避免的异常

对于有些异常类型实际上根本不必处理通常运行时异常属于此类范畴在处理空指针或者数据索引等问题时不必求助于异常处理

Java 异常处理的应用实例

在定义银行类时若取钱数大于余额时需要做异常处理

定义一个异常类 insufficientFundsException取钱(withdrawal)方法中可能产生异常条件是余额小于取额

处理异常在调用 withdrawal 的时候因此 withdrawal 方法要声明抛出异常由上一级方法调用

异常类

class InsufficientFundsExceptionextends Exception{ private Bank excepbank; // 银行对象 private double excepAmount; // 要取的钱 InsufficientFundsException(Bank ba double dAmount) { excepbank=ba; excepAmount=dAmount; } public String excepMessage(){ String str=The balance is+excepbankbalance + \n+The withdrawal was+excepAmount; return str; } }// 异常类 银行类

class Bank{ double balance;// 存款数 Bank(double balance){thisbalance=balance;} public void deposite(double dAmount){ if(dAmount>) balance+=dAmount; } public void withdrawal(double dAmount) throws InsufficientFundsException{ if (balance<dAmount) throw new InsufficientFundsException(this dAmount); balancebalance=balancedAmount; } public void showBalance(){ Systemoutprintln(The balance is +(int)balance); } } 前端调用

public class ExceptionDemo{ public static void main(String args[]){ try{ Bank ba=new Bank(); bawithdrawal(); Systemoutprintln(Withdrawal successful!); }catch(InsufficientFundsException e) { Systemoutprintln(etoString()); Systemoutprintln(eexcepMessage()); } } } 总结

Java 异常处理是使用 Java 语言进行软件开发和测试脚本开发中非常重要的一个方面对异常处理的重视会是您开发出的代码更健壮更稳定本文系统的阐述了 Java 异常处理的原理和方法能帮助读者更加清楚的理解 Java 异常处理机制在开发代码时更加灵活的使用它

               

上一篇:Java垃圾收集算法与内存洩露

下一篇:java与其他web开发编程语言的比较