`

Java异常:选择Checked Exception还是Unchecked Exception?

    博客分类:
  • Java
 
阅读更多

Java包含两种异常:checked异常unchecked异常。C#只有unchecked异常。checked和unchecked异常之间的区别是:

 

  1. Checked异常(非运行时异常)必须被显式地捕获或者传递,强制捕获或抛出,如Basic try-catch-finally Exception Handling一文中所说。而unchecked异常(运行时异常)则可以不必捕获或抛出。
  2. Checked异常(非运行时异常)继承java.lang.Exception类。Unchecked异常(运行时异常)继承自java.lang.RuntimeException类。

 

有许多支持或者反对二者甚至是否应该使用checked异常的争论。本文将讨论一些常见的观点。开始之前,先澄清一个问题:

Checked和unchecked异常从功能的角度来讲是等价的。可以用checked异常实现的功能必然也可以用unchecked异常实现,反之亦然。

选择checked异常还是unchecked异常是个人习惯或者组织规定问题。并不存在谁比谁强大的问题。

一个简单的例子

在讨论checked和unchecked异常的优缺点前先看一下代码中如下使用它们。下面是一个抛出checked异常的方法,另一个方法调用了它:

 

[java] view plain copy
 
  1. public void storeDataFromUrl(String url){  
  2.     try {  
  3.         String data = readDataFromUrl(url);  
  4.     } catch (BadUrlException e) {  
  5.         e.printStackTrace();  
  6.     }  
  7. }  
  8.   
  9. public String readDataFromUrl(String url)  
  10. throws BadUrlException{  
  11.     if(isUrlBad(url)){  
  12.         throw new BadUrlException("Bad URL: " + url);  
  13.     }  
  14.   
  15.     String data = null;  
  16.     //read lots of data over HTTP and return  
  17.     //it as a String instance.  
  18.   
  19.     return data;  
  20. }  

readDataFromUrl()方法抛出了BadUrlException。BadUrlException是我自己实现的一个类。由于BadUrlException继承自java.lang.Exception,因而它是checked异常:

 

[java] view plain copy
 
  1. public class BadUrlException extends Exception {  
  2.     public BadUrlException(String s) {  
  3.         super(s);  
  4.     }  
  5. }  

如果storeDataFromUrl()方法想要调用readDataFromUrl(),它只有两种选择。要么捕获BadUrlException,要么沿着调用栈继续向上传播该异常。上面的代码中storeDataFromUrl() 捕获了异常。向上传播异常的实现方法如下:

 

 

[java] view plain copy
 
  1. public void storeDataFromUrl(String url)  
  2. throws BadUrlException{  
  3.     String data = readDataFromUrl(url);  
  4. }  

可以看到,上述代码去掉了catch块,方法声明中加上了throws BadUrlException。下面,讨论一下unchecked异常的实现方法。首先,将BadUrlException改为继承自java.lang.RuntimeException:

 

 

[java] view plain copy
 
  1. public class BadUrlException extends RuntimeException {  
  2.     public BadUrlException(String s) {  
  3.         super(s);  
  4.     }  
  5. }  

然后,把方法中的异常改为unchecked BadUrlException:

 

 

[java] view plain copy
 
  1. public void storeDataFromUrl(String url){  
  2.     String data = readDataFromUrl(url);  
  3. }  
  4.   
  5. public String readDataFromUrl(String url) {  
  6.     if(isUrlBad(url)){  
  7.         throw new BadUrlException("Bad URL: " + url);  
  8.     }  
  9.   
  10.     String data = null;  
  11.     //read lots of data over HTTP and  
  12.     //return it as a String instance.  
  13.   
  14.     return data;  
  15. }  

注意,readDataFromUrl()方法不再声明抛出BadUrlException。storeDataFromUrl()方法也不必捕获BadUrlException。storeDataFromUrl()也可以捕获异常,但不再是必须的了,而且它也不必声明传播异常。

 

Checked 还是Unchecked?

上一节我们已经讨论了checked异常和unchecked异常代码实现上的区别,下面深入分析二者的适用情况(支持和反对二者的观点)。

一些Java书籍(如Suns Java Tutorial)中建议在遇到可恢复的错误时采用checked异常,遇到不可恢复的异常时采用unchecked异常。事实上,大多数应用必须从几乎所有异常(包括NullPointerException,IllegalArgumentException和许多其他unchecked异常)中恢复。执行失败的action/transaction会被取消,但是应用程序必须能继续处理后续的action或transaction。关闭一个应用的唯一合法时机是应用程序启动时。例如,如果配置文件丢失而且应用程序依赖于它,那么这时关闭应用程序是合法的。

我建议的使用策略是:选择checked异常或unchecked异常中的一种使用。混合使用经常导致混乱和不一致。如果你是一个经验丰富的程序员,那么根据自己的需要使用吧。

下面是支持和反对checked/unchecked异常的一些最常见的观点。支持一种类型的exception的观点通常意味着反对另一种(支持checked = 反对unchecked,支持unchecked = 反对checked)。因此,只列出了支持checked异常或unchecked异常的列表。

 

  1. 支持Checked异常:
    编译器强制检查,checked异常必须被捕获或者传播,这样就不会忘记处理异常。
  2. 支持Checked异常:
    Unchecked异常容易忘记处理,由于编译器不强制程序员捕获或传播它(第一条的反面表述)。
  3. 支持Unchecked异常:
    沿调用栈向上传播的Checked异常破坏了顶层的方法,因为这些方法必须声明抛出所有它们调用的方法抛出的异常。
  4. 支持Checked异常:
    当方法不声明它们会抛出何种异常时,就难以处理它们抛出的异常。
  5. 支持Unchecked异常:
    Check异常的抛出作为方法接口的一部分,这使得添加或移除早期版本中方法的异常难以实现。

上述每一个观点都有相反的观点,下面我会详细讨论这些观点。

观点1(支持Checked异常):

编译器强制检查,checked异常必须被捕获或者传播,这样就不会忘记处理异常。

相反观点:

当被强制捕获或传播许多异常时,开发人员的效率会受到影响,也可能会只写

 

[java] view plain copy
 
  1. try{  
  2.    callMethodThatThrowsException();  
  3. catch(Exception e){  
  4. }  

来忽略错误(糊弄了事)。

 

观点2(支持Checked异常):

Unchecked异常容易忘记处理,由于编译器不强制程序员捕获或传播它(第一条的反面表述)。

相反观点1:

强制处理或传播checked异常导致的草率地异常处理非常糟糕。

相反观点2:

在近期的一个大型项目中我们决定采用unchecked异常。我在这个项目中获得的经验是:使用unchecked异常时,任何方法都可能抛出异常。因此我不论在写哪一部分代码都时刻注意异常。而不只是声明了checked异常的地方。

此外,许多没有声明任何checked异常的标准的Java API方法会抛出诸如NullPointerException或者InvalidArgumentException之类的unchecked异常。你的应用程序需要处理这些unchecked异常。你可能会说checked异常的存在让我们容易忘记处理unchecked异常,因为unchecked异常没有显式地声明。

观点3(支持Unchecked异常):

沿调用栈向上传播的Checked异常破坏了顶层的方法,因为这些方法必须声明抛出所有它们调用的方法抛出的异常。即,声明的异常聚合了调用栈中所有的方法抛出的异常。例如:

 

[java] view plain copy
 
  1. public long readNumberFromUrl(String url)  
  2. throws BadUrlExceptions, BadNumberException{  
  3.     String data = readDataFromUrl(url);  
  4.     long number = convertData(data);  
  5.     return number;  
  6. }  
  7.   
  8. private String readDataFromUrl(String url)  
  9. throws BadUrlException {  
  10.    //throw BadUrlException if url is bad.  
  11.    //read data and return it.  
  12. }  
  13.   
  14. private long convertData(String data)  
  15. throws BadNumberException{  
  16.     //convert data to long.  
  17.     //throw BadNumberException if number isn't within valid range.  
  18. }  

readNumberFromUrl()必须声明抛出BadUrlException和BadNumberException,而这两个异常是readNumberFromUrl()调用的readDataFromUrl() 和 converData()方法抛出的异常。可以想象一个有数千个类的应用程序的顶层方法需要声明多少异常。这使得checked异常传播是一件非常痛苦的事。

 

相反观点1:

异常声明传播聚合在实际应用程序中很少发生。开发人员时常使用异常包装机制来优化。如下:

 

[java] view plain copy
 
  1. public void readNumberFromUrl(String url)  
  2. throws ApplicationException{  
  3.     try{  
  4.         String data = readDataFromUrl(url);  
  5.         long number = convertData(data);  
  6.     } catch (BadUrlException e){  
  7.         throw new ApplicationException(e);  
  8.     } catch (BadNumberException e){  
  9.         throw new ApplicationException(e);  
  10.     }  
  11. }  

readNumberFromUrl()方法只需要声明抛出ApplicationException即可。BadUrlException和BadNumberException被捕获并包装进一个更通用的ApplicationException中。通过异常包装就可以避免异常声明聚合。

 

我的个人观点是,如果你只是包装异常但并不提供更多信息,那为什么要包装它呢?try-catch块就成了多余的代码,没有做任何有意义的事。只需将ApplicationException,BadUrlException和BadNumberException定义为unchecked异常。下面是上述代码的unchecked版本:

 

[java] view plain copy
 
  1. public void readNumberFromUrl(String url){  
  2.     String data = readDataFromUrl(url);  
  3.     long number = convertData(data);  
  4. }  

也可以包装unchecked异常。下面是unchecked代码的包装版本。注意readNumberFromUrl()方法不声明抛出ApplicationException,即使它可能抛出该异常。

 

 

[java] view plain copy
 
  1. public void readNumberFromUrl(String url)  
  2.     try{  
  3.         String data = readDataFromUrl(url);  
  4.         long number = convertData(data);  
  5.     } catch (BadUrlException e){  
  6.         throw new ApplicationException(  
  7.             "Error reading number from URL", e);  
  8.     } catch (BadNumberException e){  
  9.         throw new ApplicationException(  
  10.             "Error reading number from URL", e);  
  11.     }  
  12. }  

 

相反观点2:

 

另一种常用于避免异常声明聚集的技术是创建一个应用程序基础异常类。应用程序中抛出的所有异常必须是基础异常类的子类。所有抛出异常的方法只需声明抛出基础异常。比如一个抛出Exception的方法可能抛出Exception的任何子类。如下代码:

 

[java] view plain copy
 
  1. public long readNumberFromUrl(String url)  
  2. throws ApplicationException {  
  3.     String data = readDataFromUrl(url);  
  4.     long number = convertData(data);  
  5.     return number;  
  6. }  
  7.   
  8. private String readDataFromUrl(String url)  
  9. throws BadUrlException {  
  10.    //throw BadUrlException if url is bad.  
  11.    //read data and return it.  
  12. }  
  13.   
  14. private long convertData(String data)  
  15. throws BadNumberException{  
  16.     //convert data to long.  
  17.     //throw BadNumberException if number isn't within valid range.  
  18. }  
  19.   
  20.   
  21. public class ApplicationException extends Exception{ }  
  22. public class BadNumberException   extends ApplicationException{}  
  23. public class BadUrlException      extends ApplicationException{}  

注意BadNumberException和BadUrlException不再被声明抛出,也不再被捕获,也没有包装。它们是ApplicationException的子类,因此它们会沿着调用栈向上传播。

 

我还是支持异常包装:如果应用程序的所有方法都声明抛出ApplicationException(基础异常),为什么不直接将ApplicationException定义为unchecked?这样不但省去了一些try-catch块,也省去了throws语句。

观点4(支持Checked异常):

当方法不声明它们会抛出何种异常时,就难以处理它们抛出的异常。如果没有声明,你就不会知道方法会抛出什么样的异常。因此你也就不会知道如何处理它们。当然,如果你能访问源代码,就不存在这个问题,因为你可以从源代码中看出来会抛出何种异常。

相反观点:

在多数情况下,处理异常的措施仅仅是向用户弹出一个错误提示消息,将错误消息写入日志,回滚事务等。无论发生何种异常,你可能会采用相同的处理措施。因此,应用程序通常包含一些集中的通用错误处理代码。如此一来,确切获知抛出了何种异常也就不那么重要了。

观点5(支持Unchecked异常):

Check异常的抛出作为方法接口的一部分,这使得添加或移除早期版本中方法的异常难以实现。

相反观点:

如果方法采用了基础异常机制,就不存在这个问题。如果方法声明抛出基础异常,那么可以方便抛出新异常。唯一的需求是新异常必须是基础异常的子类。

需要再强调一遍的是,让所有可能抛出异常的方法声明抛出相同的基础异常的意义何在?这样能比抛出unchecked异常更好地处理异常吗?

总结

我过去支持checked异常,但是最近我改变了我的观点。Rod Johnson(Spring Framework),Anders Hejlsberg(C#之父),Joshua Bloch(Effective Java,条目41:避免checked异常的不必要的使用)和其他一些朋友使我重新考虑了checked异常的真实价值。最近我们尝试在一个较大的项目中使用unchecked异常,效果还不错。错误处理被集中在了少数几个类中。会有需要本地错误处理的地方,而不是将异常传播给主错误处理代码。但是这种地方不会很多。由于代码中不会到处都是try-catch块,我们的代码变得可读性更好。换句话说,使用unchecked异常比使用checked异常减少了无用的catch-rethrow try-catch块。总之,我建议使用unchecked异常。至少在一个工程中尝试过。我总结了以下原因:

 

  • Unchecked异常不会使代码显得杂乱,因为其避免了不必要的try-catch块。
  • Unchecked异常不会因为异常声明聚集使方法声明显得杂乱。
  • 关于容易忘记处理unchecked异常的观点在我的实践中没有发生。
  • 关于无法获知如何处理未声明异常的观点在我的实践中没有发生。
  • Unchecked异常避免了版本问题。

 

你的项目中使用何种异常由你自己决定。下面是相关的资料。

Anders Hejlsberg on checked vs. unchecked exceptions
http://www.artima.com/intv/handcuffs.html 

James Gosling on checked exceptions
http://www.artima.com/intv/solid.html 

Bill Venners on Exceptions
http://www.artima.com/interfacedesign/exceptions.html 

Bruce Eckel on checked exceptions
http://www.artima.com/intv/typingP.html 

Designing with Exceptions (Bill Venners - www.artima.com)
http://www.artima.com/designtechniques/desexcept.html 

Effective Java (Joshua Bloch - Addison Wesley 2001) 

Daniel Pietraru - in favor of checked exceptions
http://littletutorials.com/2008/05/06/exceptional-java-checked-exceptions-are-priceless-for-everything-else-there-is-the-the-runtimeexception/

 

英文原文:http://tutorials.jenkov.com/java-exception-handling/checked-or-unchecked-exceptions.html

 

装载自http://blog.csdn.net/kingzone_2008/article/details/8535287

分享到:
评论

相关推荐

    Checked and Unchecked Exception

    异常分为两种类型:已检查异常(Checked Exception)和未检查异常(Unchecked Exception)。 **已检查异常(Checked Exception)** 已检查异常是那些在编译时需要处理的异常。Java强制程序员要么在方法签名中声明...

    java 异常详解,java异常分类,创建自己的异常

    异常可以分为两大类:-checked 异常和 unchecked 异常。Checked 异常必须在编译时捕捉并处理,而 unchecked 异常可以在编译时忽略,但是在运行时总是会被抛出。 在 Java 中,每个异常都是一个对象,它是 java.lang....

    详解Java中的checked异常和unchecked异常区别

    Java中的checked异常和unchecked异常区别详解 Java中的checked异常和unchecked异常是Java语言中两种不同的异常类型,它们之间的区别是很多开发者容易混淆的。下面,我们将详细介绍checked异常和unchecked异常的概念...

    Java常见工具类及异常处理

    在本文中,我们将详细介绍 Java 异常处理机制,包括 try、catch、finally 块、throws、throw 关键字、Exception 和 Error 类、Checked Exception 和 Unchecked Exception 等概念,并提供了多种异常处理的示例代码,...

    Java中Error与Exception的区别.doc

    在 Java 中,定义了两类异常:Checked exception 和 Unchecked exception。 Checked exception 是 Exception 的子类,它们的异常向上抛出机制进行处理。例如,如果子类可能产生 A 异常,那么在父类中也必须 throws ...

    java 异常 问题收集 Exception

    Java异常处理是编程过程中至关重要的一个环节,它帮助开发者捕获和处理程序运行时可能出现的错误,确保程序的健壮性和稳定性。...无论是对于初学者还是经验丰富的开发者,掌握好Java异常处理都是至关重要的。

    Java异常处理机制及其在项目中的应用.pdf

    Java 异常处理机制可以分为两大类:Checked Exception 和 Unchecked Exception。Checked Exception 是在编译期检查的异常,而 Unchecked Exception 是在运行期检查的异常。 在 Java 中,异常对象都是继承自 ...

    高效的java异常处理框架高效的java异常处理框架高效的java异常处理框架

    其中异常类 Exception 又分为运行时异常(RuntimeException)和非运行时异常,这两种异常有很大的区别,也称之为不检查异常(Unchecked Exception)和检查异常(Checked Exception)。 二、异常的捕获和处理 Java ...

    java异常处理习题

    * `throw` 语句可以抛出 checked 异常和 unchecked 异常。 八、异常处理的 best practice * 尽量避免使用 `catch` 子句来捕捉所有异常,而是根据实际情况捕捉特定的异常类型。 * 使用 `finally` 子句来释放资源和...

    java异常 异常的概念 应用

    Java异常处理是编程中至关重要的一个环节,它关乎程序的健壮性和稳定性。在Java中,异常(Exception)被用来表示程序运行时发生的错误或不正常情况。异常并不等同于错误,它是程序设计的一部分,允许我们优雅地处理...

    通过实例了解java checked和unchecked异常

    通过实例了解 Java checked 和 unchecked 异常 Java 异常分为两种类型:checked 异常和 unchecked 异常。checked 异常是可以在执行过程中恢复的,例如无效的用户输入、文件不存在、网络或者数据库链接错误等。这些...

    java异常处理试题及答案.doc

    * Java 中有两种类型的异常:Checked Exception 和 Unchecked Exception。 二、Java 异常处理的关键字 * throw:用于抛出异常。 * catch:用于捕捉异常。 * finally:用于执行资源释放或其他必要的操作。 * try:...

    Java编程异常处理最佳实践【推荐】

    Java中的异常处理机制可以分为两大类:Checked Exception和Unchecked Exception。Checked Exception是编译器检查的异常,必须在编译时处理,而Unchecked Exception是运行时检查的异常,可以在运行时抛出。 在Java...

    java异常(Exception)处理机制详解

    异常的使用可以分为两类:CheckedException 和 UncheckedException。 CheckedException 需要用 try...catch... 显示的捕获,而 UncheckedException 不需要捕获。 例如,IOException 是一个 Checked Exception,需要...

    浅谈Java异常.docx

    通过合理地划分`CheckedException`和`UncheckedException`,我们可以更好地管理程序的异常情况,提高代码的健壮性和可读性。在实际编程中,应该根据异常的性质和预期的处理方式来选择合适的异常类型,从而确保程序在...

    Java throw Exception实现异常转换

    Java throw Exception实现异常转换是Java语言中的一种异常处理机制,它允许开发者将 checked exception 转换为 unchecked exception,从而使得异常处理变得更加灵活和便捷。本文将详细介绍Java throw Exception实现...

    基于JAVA语言的异常处理探讨分析 (1).pdf

    JAVA语言的异常可以分为两大类:Checked Exception和Unchecked Exception。Checked Exception是编译器强制捕获的异常,而Unchecked Exception是运行时抛出的异常。常见的Checked Exception包括IOException、...

    Java异常处理-异常处理概述与常见异常举例

    Exception分为编译时异常(Checked Exception)和运行时异常(Unchecked Exception)。 - 编译时异常:如FileNotFoundException、IOException等,这些异常在编译阶段就需要处理,否则代码无法编译。 - 运行时异常:...

    实验六:Java异常处理

    Java异常分为两大类:检查性异常(Checked Exceptions)和运行时异常(Unchecked Exceptions)。检查性异常是那些在编译时需要被处理的异常,例如`IOException`。运行时异常则在编译时不强制处理,但在运行时如果...

Global site tag (gtag.js) - Google Analytics