Java:检查与未经检查的exception说明

我已经阅读关于检查与未经检查的exceptionStackOverFlow多个职位。 我老实说还不太清楚如何正确使用它们。

Joshua Bloch在“ Effective Java ”中表示

对于可恢复的条件和运行时exception使用已检查的exception来编程错误(第二版中的第58项)

让我们看看我是否正确理解这一点。

这是我对一个检查的exception的理解:

try{ String userInput = //read in user input Long id = Long.parseLong(userInput); }catch(NumberFormatException e){ id = 0; //recover the situation by setting the id to 0 } 

1.以上是否考虑检查exception?

2. RuntimeException是一个未经检查的exception吗?

这是我对未经检查的例外的理解:

 try{ File file = new File("my/file/path"); FileInputStream fis = new FileInputStream(file); }catch(FileNotFoundException e){ //3. What should I do here? //Should I "throw new FileNotFoundException("File not found");"? //Should I log? //Or should I System.exit(0);? } 

4.现在,上面的代码不能成为一个检查exception? 我可以尝试恢复这种情况? 我可以吗? (注意:我的第三个问题在上面的catch

 try{ String filePath = //read in from user input file path File file = new File(filePath); FileInputStream fis = new FileInputStream(file); }catch(FileNotFoundException e){ //Kindly prompt the user an error message //Somehow ask the user to re-enter the file path. } 

5.为什么人们这样做?

 public void someMethod throws Exception{ } 

他们为什么让这个例外冒出来呢? 没有更好地处理错误? 为什么泡起来?

编辑:我应该冒泡确切的exception或使用exception掩盖它?

以下是我的阅读

在Java中,什么时候应该创build一个检查的exception,什么时候应该是一个运行时exception?

何时select已选中和未选中的例外

许多人说,检查exception(即应明确捕捉或重新抛出的exception)不应该被使用。 例如,他们在C#中被淘汰,大多数语言都没有。 所以你总是可以抛出一个RuntimeException的子类(未经检查的exception)

不过,我认为检查exception是有用的 – 当你想要强制你的API的用户思考如何处理exception情况(如果它是可恢复的)时使用它们。 只是检查的exception在Java平台中被滥用,这使得人们讨厌它们。

这是我对这个话题的扩展观点 。

至于具体的问题:

  1. NumberFormatException是否考虑检查exception?
    No. NumberFormatException选中(=是RuntimeException子类)。 为什么? 我不知道。 (但应该有一个方法isValidInteger(..)

  2. RuntimeException是一个未经检查的exception?
    对,就是这样。

  3. 我应该在这里做什么?
    这取决于这个代码在哪里,你想要发生什么。 如果它在UI层 – 抓住它并显示警告; 如果它在服务层 – 根本不要捕捉 – 让它冒泡。 只是不要吞下例外。 如果在大多数情况下发生exception,您应该select其中一个:

    • logging并返回
    • 重新抛出它(声明它是由方法抛出)
    • 通过在构造函数中传递当前构造函数来构造一个新的exception
  4. 现在,上面的代码不能成为一个检查exception吗? 我可以尝试恢复这种情况? 我可以吗?
    它可能是。 但是没有什么能够阻止你捕捉未经检查的exception

  5. 为什么人们在throws子句中添加类Exception
    通常是因为人们懒得考虑要捕捉什么和重新抛出什么。 抛出Exception是一个不好的做法,应该避免。

唉,没有单一的规则可以让你确定什么时候抓,何时重新抛出,何时使用检查以及何时使用未检查的exception。 我同意这会导致很多混淆和很多错误的代码。 Bloch声明了一般原则(你引用了它的一部分)。 一般的原则是重新抛出一个例外,你可以处理它的层。

不pipe是什么东西都是“检查exception”,与你是否抓住它或你在捕捉块中做什么都无关。 这是exception类的属性。 除了 RuntimeException及其子类之外 ,任何属于Exception的子类都是检查的exception。

Java编译器强制您捕获检查的exception或在方法签名中声明它们。 这应该是为了提高程序的安全性,但是大多数人的意见似乎是不值得它所创造的devise问题。

他们为什么让这个例外冒出来呢? 不是处理错误越早越好? 为什么泡起来?

因为这是例外的全部。 没有这种可能性,你就不需要例外。 它们使您能够按照您select的级别来处理错误,而不是强迫您在最初出现的低级方法中处理这些错误。

  1. 以上是否考虑检查exception? 否如果是RuntimeException,则处理exception的事实不会使其成为“检查exception”。

  2. RuntimeException是一个未经检查的exception? 是

检查exception是java.lang.Exception的子类Unchecked Exceptions是java.lang.RuntimeException的子类

调用抛出检查exception需要包含在一个try {}块中,或者在方法的调用者的上一级中处理。 在这种情况下,当前的方法必须声明它抛出exception,以便调用者可以做出适当的安排来处理exception。

希望这可以帮助。

问:我应该冒出一个确切的exception或使用exception屏蔽它?

答:是的,这是一个非常好的问题和重要的devise考虑因素。 类Exception是一个非常普遍的exception类,可以用来包装内部的低级exception。 您最好创build一个自定义的exception并将其包装在其中。 但是,也是一个很大的问题 – 永远不要掩盖其根本原因。 对于前, Dont ever做以下 –

 try { attemptLogin(userCredentials); } catch (SQLException sqle) { throw new LoginFailureException("Cannot login!!"); //<-- Eat away original root cause, thus obscuring underlying problem. } 

而是做以下事情:

 try { attemptLogin(userCredentials); } catch (SQLException sqle) { throw new LoginFailureException(sqle); //<-- Wrap original exception to pass on root cause upstairs!. } 

吃掉原始的根本原因掩盖了超出恢复的实际原因对于生产支持团队来说是一个噩梦,他们可以访问应用程序日志和错误消息。 虽然后者是一个更好的devise,但很多人不经常使用它,因为开发人员只是不能将底层消息传递给调用者。 因此,请牢记:无论是否包含任何特定于应用程序的exception, Always pass on the actual exceptionexception。

尝试捕获RuntimeExceptions

作为一般规则的RuntimeExceptions不应该被试用。 他们通常表示编程错误,应该单独留下。 相反,程序员应该在调用一些可能导致RuntimeException的代码之前检查错误情况。 例如:

 try { setStatusMessage("Hello Mr. " + userObject.getName() + ", Welome to my site!); } catch (NullPointerException npe) { sendError("Sorry, your userObject was null. Please contact customer care."); } 

这是一个糟糕的编程习惯。 相反,一个空检查应该像 –

 if (userObject != null) { setStatusMessage("Hello Mr. " + userObject.getName() + ", Welome to my site!); } else { sendError("Sorry, your userObject was null. Please contact customer care."); } 

但有时候这样的错误检查如数字格式化是昂贵的,考虑这个 –

 try { String userAge = (String)request.getParameter("age"); userObject.setAge(Integer.parseInt(strUserAge)); } catch (NumberFormatException npe) { sendError("Sorry, Age is supposed to be an Integer. Please try again."); } 

这里的预调用错误检查是不值得的,因为它本质上意味着在parseInt()方法中复制所有的string到整数的转换代码,而且如果由开发人员实现的话,它是很容易出错的。 所以,最好是消除尝试抓住。

所以NullPointerException和NumberFormatException都是RuntimeException,捕获一个NullPointerException应该被一个正常的空检查取代,而我build议显式地捕获一个NumberFormatException以避免可能引入容易出错的代码。

1。 如果您不确定是否有exception,请检查API:

  java.lang.Object extended by java.lang.Throwable extended by java.lang.Exception extended by java.lang.RuntimeException //<-NumberFormatException is a RuntimeException extended by java.lang.IllegalArgumentException extended by java.lang.NumberFormatException 

2。 是的,每一个例外都是它的延伸。

3。 没有必要捕捉并抛出相同的exception。 在这种情况下,您可以显示一个新的文件对话框。

4。 FileNotFoundException已经是一个检查的exception。

5。 如果期望调用someMethod来捕获exception的方法,后者可能被抛出。 它只是“传球”。 它的一个例子是如果你想把它放在你自己的私有方法中,而在你的公共方法中处理exception。

一个很好的阅读是Oracle文档本身: http : //download.oracle.com/javase/tutorial/essential/exceptions/runtime.html

为什么devise者决定强制一个方法来指定所有可以在其范围内抛出的被检查的exception? 方法抛出的任何exception都是方法公共编程接口的一部分。 那些调用方法的人必须知道方法可以抛出的exception,以便他们可以决定如何处理它们。 这些exception与其参数和返回值一样是该方法编程接口的一部分。

下一个问题可能是:“如果logging一个方法的API非常好,包括它可以抛出的exception,为什么不指定运行时exception呢? 运行时exception表示由编程问题导致的问题,因此API客户端代码无法合理地从它们中恢复或以任何方式处理它们。 这样的问题包括算术例外,例如除以零; 指针exception,比如试图通过空引用访问一个对象; 和索引exception,比如试图通过太大或太小的索引访问数组元素。

Java语言规范中还有一些重要的信息:

在throws子句中指定的检查的exception类是实现者与方法或构造函数的用户之间的契约的一部分

底线恕我直言,你可以捕捉任何运行时RuntimeException ,但你并不需要和,实际上不需要保持相同的非检查exception抛出,因为这些不是合同的一部分。

1)不,NumberFormatException是一个未经检查的exception。 即使你抓住了它(你不需要),它是没有检查的。 这是因为它是IllegalArgumentException的子类,它是RuntimeException的子类。

2)RuntimeException是所有未经检查的exception的根。 RuntimeException的每个子类都是未选中的。 所有其他exception和Throwables检查除了错误(哪些在Throwable之下)。

3/4)你可以提醒用户,他们select了一个不存在的文件,并要求一个新的。 或者只是通知用户他们input的东西无效。

5)投掷和捕捉“例外”是不好的做法。 但更一般的情况下,你可能会抛出其他exception,以便调用者可以决定如何处理它。 例如,如果您编写了一个库来处理读取某个文件的input,并且您的方法被传递了一个不存在的文件,那么您就不知道如何处理这个文件。 来电者是否想再次提问或退出? 所以你把这个链条抛回给调用者。

在许多情况下,由于程序员没有validationinput(在第一个问题中出现NumberFormatException),所以会发生未经检查的exception。 这就是为什么它可以捕捉它们,因为有更好的方法来避免产生这些exception。

检查 – 易发生。 检查编译时间。

例如.. FileOperations

未检查 – 由于数据不正确。 检查运行时间。

例如..

 String s = "abc"; Object o = s; Integer i = (Integer) o; Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at Sample.main(Sample.java:9) 

这里的exception是由于数据不好,决不能在编译期间确定。

检查exception在编译时由JVM及其相关资源(files / db / stream / socket等)检查。 检查exception的动机是在编译时如果资源不可用,应用程序应该定义一个替代行为来处理catch / finally块中的这个问题。

未经检查的exception纯粹是程序错误,计算错误,空数据,甚至业务逻辑中的失败都可能导致运行时exception。 它绝对很好处理/捕获代码中的未检查的exception。

解释来自http://coder2design.com/java-interview-questions/

为了回答最后一个问题(其他人似乎已经在上面得到了彻底的回答),“我应该冒出确切的exception还是使用exception屏蔽?

我假设你的意思是这样的:

 public void myMethod() throws Exception { // ... something that throws FileNotFoundException ... } 

不,总是尽可能地声明最精确的例外,或者是一个清单。 您将您的方法声明为可抛出的exception是您的方法和调用方之间合同的一部分。 抛出“FileNotFoundException”意味着有可能文件名无效,文件不会被find; 来电者将需要智能处理。 投掷“例外”意味着“嘿,不要发生,交易”。 这是一个非常差的API。

在第一篇文章的评论中,有一些例子,“抛出exception”是一个有效和合理的声明,但是对于大多数“正常”的代码你不会写。

  • Java区分两类exception(选中和取消选中)
  • Java对检查的exception执行捕获或声明的要求
  • exception的types决定是否检查exception。
  • 所有RuntimeException类的直接或间接子类的exceptiontypes都是未经检查的exception。
  • 所有从Exception类inheritance而不是RuntimeException的类都被认为是检查exception。
  • 从类错误inheritance的类被认为是未选中的。
  • 编译器检查每个方法的调用和减速,以确定该方法是否引发检查exception。
    • 如果是这样,编译器确保exception被捕获或在throws子句中声明。
  • 为了满足catch-or-declare要求的声明部分,生成exception的方法必须提供一个包含checked-exception的throws子句。
  • exception类被定义为当它们被认为足够重要时可以被检查或者声明。

他们为什么让这个例外冒出来呢? 没有更好地处理错误? 为什么泡起来?

例如,假设您有一些客户端 – 服务器应用程序,并且客户端已经请求了一些无法find的资源,或者在处理用户请求的时候在服务器端发生了某些其他错误,那么这是一种责任服务器告诉客户端为什么他不能得到他要求的东西,所以为了在服务器端实现这一点,代码被写入抛出exception使用抛出关键字,而不是吞咽或处理it.if服务器处理它/吞咽那么,就没有机会向客户通知发生了什么错误。

注意:为了给出错误types已经发生的一个清楚的描述,我们可以创build我们自己的Exception对象并把它抛给客户端。

运行时exception运行时exception被称为未经检查的exception。 所有其他的exception都是检查exception,而不是从java.lang.RuntimeException派生。

检查exception检查的exception必须在代码中的某处被捕获。 如果你调用一个引发一个检查exception的方法,但是你没有捕获到检查的exception,你的代码将不能编译。 这就是为什么他们被称为检查exception:编译器检查,以确保他们处理或声明。

Java API中的许多方法都会检查exception,因此您经常会编写exception处理程序来处理由您未写入的方法生成的exception。

这是一个简单的规则,可以帮助你决定。 这与在Java中如何使用接口有关。

带上你的课并设想为它devise一个接口,使得接口描述类的function,但是没有底层实现(作为接口)。 也许假装你可能以另一种方式实施课程。

看看接口的方法,并考虑它们可能抛出的exception:

如果一个方法抛出一个exception,而不pipe底层的实现(换言之,它只描述了这个function),那么它应该是接口中的一个检查exception。

如果一个exception是由底层实现引起的,那么它不应该在接口中。 因此,它必须是类中的一个未经检查的exception(因为未经检查的exception不需要出现在接口签名中),或者必须将其封装并作为接口方法的一部分检查的exception重新抛出。

要决定是否应该包装和重新抛出,您应该再次考虑是否有必要立即处理exception情况是否有意义,或者exception是如此普遍以至于您无能为力传播堆栈。 当被定义为新界面的function时,包装的exception是否有意义,或者它只是一个可能发生其他方法的错误情况的包的载体? 如果是前者,它可能仍然是一个检查exception,否则应该不检查。

你通常不应该打算“冒泡”exception(捕获和重新抛出)。 一个exception应该由调用者处理(在这种情况下,它被检查),或者它应该一直到一个高级处理程序(在这种情况下,它是最容易的,如果它是未选中的)。

Java教程path文章“ Unchecked Exceptions – the Controversy ”提供了关于未经检查和已检查exception之间区别的绝对的最爱描述(抱歉,要获得这篇文章的所有小结 – 但是,嘿,基本有时是最好的):

以下是底线指导原则:如果客户可以合理预期从exception中恢复,请将其作为检查exception。 如果客户端无法做任何事情来恢复exception,使其成为一个unchecked例外

“抛出什么types的例外”是语义的(在某种程度上),上面的引用提供了很好的指导(因此,我仍然被C#抛弃了检查exception的观念所吹捧 – 特别是Liskov认为的他们的用处)。

其余的则变得合乎逻辑:编译器希望我明确地回应哪些exception? 您希望客户恢复的那些。

我认为检查exception对于使用外部库的开发人员来说是一个很好的提示,在特殊情况下,可能会出现库的代码出错的情况。

阅读更多关于检查与未经检查的exception在这里http://learnjava.today/2015/11/checked-vs-unchecked-exceptions/

我只是想添加一些不使用检查exception的推理。 这不是一个完整的答案,但我觉得它确实回答了你的问题的一部分,并补充了许多其他的答案。

每当涉及到检查exception时, throws CheckedException在方法签名中throws CheckedExceptionexception( CheckedException可能是任何检查的exception)。 签名不抛出exception,抛出exception是实现的一个方面。 接口,方法签名,父类,所有这些东西都不应该依赖于它们的实现。 在这里检查exception的用法(事实上,你必须在方法签名中声明throws )将绑定你的高层接口和这些接口的实现。

让我给你看一个例子。

让我们有一个很好的,干净的界面

 public interface IFoo { public void foo(); } 

现在我们可以写许多方法foo() ,像这样

 public class Foo implements IFoo { @Override public void foo() { System.out.println("I don't throw and exception"); } } 

Foo类非常好。 现在让我们来第一次尝试Bar类

 public class Bar implements IFoo { @Override public void foo() { //I'm using InterruptedExcepton because you probably heard about it somewhere. It's a checked exception. Any checked exception will work the same. throw new InterruptedException(); } } 

这个类吧不会编译。 由于InterruptedException是一个检查的exception,您必须捕获它(使用try-catch内部方法foo())或声明您正在抛出它(向方法签名添加throws InterruptedException )。 因为我不想在这里捕获这个exception(我希望它向上传播,所以我可以在其他地方正确处理),让我们改变签名。

 public class Bar implements IFoo { @Override public void foo() throws InterruptedException { throw new InterruptedException(); } } 

这个类吧不会编译! Bar的方法foo()不会覆盖IFoo的方法foo(),因为它们的签名是不同的。 我可以删除@Override注释,但我想要IFoo IFoo foo;对IFoo进行编程IFoo foo; 后来决定使用哪个实现,比如foo = new Bar(); 。 如果Bar的方法foo()不会覆盖IFoo的方法foo,那么当我做foo.foo(); 它不会调用Bar的foo()的实现。

使Bar的public void foo() throws InterruptedException重写IFoo的public void foo()我必须添加throws InterruptedException IFoo的方法签名throws InterruptedException 。 但是,这会对我的Foo类产生问题,因为它的foo()方法的签名与IFoo的方法签名不同。 此外,如果我添加throws InterruptedException Foo的方法foo()我会得到另一个错误,指出Foo的方法foo()声明它会引发InterruptedException,但它永远不会引发InterruptedException。

As you can see (if I did a decent job at explaining this stuff), the fact that I'm throwing a checked exception like InterruptedException is forcing me to tie my interface IFoo to one of it's implementations, which in turn causes havoc on IFoo's other implementations!

This is one big reason why checked exceptions are BAD. In caps.

One solution is to capture the checked exception, wrap it in an unchecked exception and throw the unchecked exception.

Just to point out that if you throw a checked exception in a code and the catch is few levels above, you need to declare the exception in the signature of each method between you and the catch. So, encapsulation is broken because all functions in the path of throw must know about details of that exception.

All of those are checked exceptions. Unchecked exceptions are subclasses of RuntimeException. The decision is not how to handle them, it's should your code throw them. If you don't want the compiler telling you that you haven't handled an exception then you use an unchecked (subclass of RuntimeException) exception. Those should be saved for situations that you can't recover from, like out of memory errors and the like.

If anybody cares for yet another proof to dislike checked exceptions, see the first few paragraphs of the popular JSON library:

"Although this is a checked exception, it is rarely recoverable. Most callers should simply wrap this exception in an unchecked exception and rethrow: "

So why in the world would anyone make developers keep checking the exception, if we should "simply wrap it" instead? 大声笑

http://developer.android.com/reference/org/json/JSONException.html