Java:已检查vs未经检查的异常说明

Tha*_*ham 678 java exception runtimeexception checked-exceptions unchecked-exception

我已经在StackOverFlow上阅读了有关已检查和未经检查的异常的多个帖子.老实说,我还是不太确定如何正确使用它们.

Joshua Bloch在" Effective Java "中说过

对可恢复条件使用已检查的异常,对编程错误使用运行时异常(第2版中的第58项)

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

以下是我对已检查异常的理解:

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
}
Run Code Online (Sandbox Code Playgroud)

1.以上是否考虑了检查异常?

2. RuntimeException是未经检查的异常吗?

以下是我对未经检查的异常的理解:

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);?
}
Run Code Online (Sandbox Code Playgroud)

4.现在,上述代码也不能成为检查异常吗?我可以尝试恢复这样的情况吗?我可以吗?(注意:我的第3个问题在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.
}
Run Code Online (Sandbox Code Playgroud)

5.为什么人们会这样做?

public void someMethod throws Exception{

}
Run Code Online (Sandbox Code Playgroud)

他们为什么要让异常泡沫化?是不是更快地处理错误?为什么泡起来?

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

以下是我的阅读材料

在Java中,何时应该创建一个已检查的异常,何时应该是运行时异常?

何时选择已检查和未检查的例外

Boz*_*zho 458

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

但是,我认为已检查的异常很有用 - 当您希望强制API的用户考虑如何处理异常情况(如果它是可恢复的)时,会使用它们.只是被检查的异常在Java平台中被过度使用,这让人们讨厌它们.

这是我对该主题的扩展视图.

至于具体问题:

  1. NumberFormatException考虑一个检查异常?
    编号NumberFormatException未选中(=是子类RuntimeException).为什么?我不知道.(但应该有一种方法isValidInteger(..))

  2. RuntimeException未经检查的例外吗?
    对,就是这样.

  3. 我该怎么办?
    这取决于此代码的位置以及您希望发生的内容.如果它在UI层中 - 捕获它并显示警告; 如果它在服务层 - 根本不抓住它 - 让它冒泡.只是不要吞下这个例外.如果在大多数情况下发生异常,您应该选择以下其中一种:

    • 记录并返回
    • 重新抛出它(声明它被方法抛出)
    • 通过在构造函数中传递当前的异常来构造一个新的异常
  4. 现在,上面的代码难道也不是一个经过检查的例外吗?我可以尝试恢复这样的情况吗?我可以吗?
    它可能是.但是没有什么可以阻止你捕获未经检查的异常

  5. 为什么人们Exception在throws子句中添加类?
    大多数情况下,因为人们懒得考虑要捕捉什么和重新抛出什么.投掷Exception是一种不好的做法,应该避免.

唉,没有一条规则可以让您确定何时捕获,何时重新抛出,何时使用已检查以及何时使用未经检查的异常.我同意这会引起很多混乱和很多不好的代码.Bloch说明了一般原则(你引用了它的一部分).一般原则是重新抛出可以处理它的图层的异常.

  • 关于抛出异常,并不总是因为人们很懒惰,当你实现框架时,让框架的用户能够抛出任何异常也是很常见的.例如,您可以在JSE中检查Callable接口的签名 (36认同)
  • @Kaj - 是的,像Callable这样的一般事物,拦截器等都是特殊情况.但在大多数情况下,这是因为人们很懒:) (10认同)
  • re:3.1"记录并返回"明智地这样做.这非常接近吃或隐藏和例外.我这样做是为了表示没有问题的东西,这并不是特别的.日志被淹没并且太容易被忽略. (6认同)
  • "当你想强迫你的API用户思考如何处理异常情况时" - 你不能强迫任何人思考,如果他们不想这样做.如果他们不想思考,他们会编写一个糟糕的异常块,它什么也不做,或者更糟糕的是,删除或干扰关键错误信息.这就是检查异常失败的原因. (4认同)
  • @adrianos"......你不能强迫任何人思考,如果他们不想......"通过这种思路我们也可以删除编译错误....我不是真的针对你,我听说过这个争论一次又一次,仍然发现它是将Checked Exceptions标记为失败的最可能的解释.作为旁注,我已经看到过这样一种语言,其中编译(以及实际上的运行时错误)实际上是不可能的.那条路通向一些非常黑暗的地方. (3认同)

Mic*_*rdt 229

某些东西是否是"已检查的异常"与您是否捕获它或在catch块中执行的操作无关.它是异常类的属性.凡是是的一个子类Exception ,除了RuntimeException和它的子类是经过检查的异常.

Java编译器强制您捕获已检查的异常或在方法签名中声明它们.它本来应该提高程序安全性,但大多数意见似乎是它不值得它创造的设计问题.

他们为什么要让异常泡沫化?不处理错误越快越好?为什么泡起来?

因为这是例外的全部要点.没有这种可能性,您不需要例外.它们使您能够在您选择的级别处理错误,而不是强迫您在最初发生的低级方法中处理它们.

  • "除了RuntimeException及其子类之外,任何东西都是Throwable的子类是一个经过检查的异常." - 你的陈述不正确.错误也继承了Throwable,并且未经检查. (11认同)
  • @JonasEicher:基本上,异常的一个主要优点是它们允许您选择调用堆栈中要处理错误的位置,这通常非常高,同时保持层之间完全没有错误处理工件.检查异常会破坏这种优势.另一个问题是,检查/未检查的区别与异常类相关联,异常类也表示异常的概念分类 - 混合两个不一定相关的方面. (8认同)
  • 谢谢!我偶尔会从我的方法中抛出异常,因为它有蹩脚的原则.我的团队中的一个开发人员想要输入一个无效的xpath表达式来处理异常.在不太可能的情况下,他们会发现异常并且什么都不做,他们会在代码审查中听到它. (3认同)
  • "但多数意见似乎是它不值得创造它的设计问题." - 请引用? (2认同)
  • @Bartzilla是的.为了完整性,因为`Throwable`的javadoc说:"Throwable和Throwable的任何子类都不是RuntimeException或Error的子类,被视为已检查的异常" (2认同)

d-l*_*ive 70

  1. 以上是否被认为是一个经过检查的例外?不,你是处理异常的事实并不使它成为一个Checked Exception如果它是一个RuntimeException.

  2. RuntimeException一个unchecked exception?是

Checked Exceptionssubclassesjava.lang.Exception Unchecked ExceptionsARE subclassesjava.lang.RuntimeException

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

希望这可以帮助.

问:我应该冒泡确切的异常还是使用Exception掩盖它?

答:是的,这是一个非常好的问题和重要的设计考虑因素.Exception类是一个非常通用的异常类,可用于包装内部低级异常.您最好创建一个自定义异常并将其包装在其中.但是,也是一个重大问题 - 永远不要掩盖潜在的原始根本原因.例如,Don't ever请执行以下操作 -

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

而是做以下事项:

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

吞噬原始根源会导致实际原因无法恢复,这对于生产支持团队来说是一场噩梦,他们可以访问的是应用程序日志和错误消息.虽然后者是一个更好的设计,但许多人不经常使用它,因为开发人员只是无法将基础消息传递给调用者.所以要做一个坚定的说明:Always pass on the actual exception回复是否包含在任何特定于应用程序的异常中.

试试 RuntimeExceptions

RuntimeException作为一般规则,不应该试图抓住.它们通常表示编程错误,应该保持不变.相反,程序员应该在调用可能导致a的代码之前检查错误情况RuntimeException.例如:

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

这是一个糟糕的编程习惯.相反,应该像以下一样进行空检查 -

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

但有时候这种错误检查很昂贵,例如数字格式化,请考虑这个 -

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.");
}
Run Code Online (Sandbox Code Playgroud)

这里预调用错误检查不值得付出努力,因为它本质上意味着复制parseInt()方法中的所有字符串到整数转换代码 - 并且如果由开发人员实现则容易出错.所以最好不要试试try-catch.

所以NullPointerExceptionNumberFormatExceptionRuntimeExceptions,捉NullPointerException应以优美的空检查更换,而我建议捉NumberFormatException明确,以避免可能的引进容易出错的代码.


Ale*_*dam 18

1.如果您不确定异常,请检查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
Run Code Online (Sandbox Code Playgroud)

2.是的,以及扩展它的每个例外.

3.没有必要捕获并抛出相同的异常.在这种情况下,您可以显示新的文件对话框.

4.FileNotFoundException异常已经经过检查的异常.

5.如果期望调用someMethod捕获异常的方法,则可以抛出后者.它只是"传球".它的一个使用示例是,如果您想将它放在自己的私有方法中,并在公共方法中处理异常.

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

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

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

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

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

底线恕我直言,你可以捕获任何RuntimeException,但你不需要,实际上不需要实现维护相同的非检查异常抛出,因为那些不是合同的一部分.


don*_*ata 9

1)否,NumberFormatException是未经检查的异常.即使你抓住它(你不是必须的)因为它没有被检查.这是因为它是其子类的IllegalArgumentException子类RuntimeException.

2)RuntimeException是所有未经检查的异常的根.每个子类RuntimeException都未选中.所有其他例外和Throwable检查除了错误(其中包含Throwable).

3/4)您可以提醒用户他们选择了一个不存在的文件并要求新文件.或者只是退出通知用户他们输入的内容无效.

5)投掷和捕捉'Exception'是不好的做法.但更一般地说,您可能会抛出其他异常,以便调用者可以决定如何处理它.例如,如果您编写了一个库来处理读取某些文件输入并且您的方法传递了一个不存在的文件,那么您根本不知道如何处理它.来电者是想再次询问还是退出?所以你将Exception向上链回到调用者.

在许多情况下,unchecked Exception由于程序员没有验证输入(在NumberFormatException第一个问题的情况下),因此发生了.这就是为什么它可以选择捕获它们,因为有更优雅的方法来避免产生这些异常.


bha*_*ran 7

检查 - 容易发生.签入编译时间.

例如.. FileOperations

UnChecked - 由于数据不佳.检查运行时间.

例如..

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)
Run Code Online (Sandbox Code Playgroud)

这里的异常是由于数据不良而无法在编译期间确定.


Ome*_*mer 7

运行时异常:运行时异常指的是未经检查的异常。所有其他异常都是检查异常,它们不是从 java.lang.RuntimeException 派生的。

Checked Exceptions:必须在代码中的某处捕获已检查的异常。如果您调用一个抛出已检查异常的方法,但没有在某处捕获已检查异常,则您的代码将无法编译。这就是为什么它们被称为受检异常:编译器检查以确保它们被处理或声明。

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


Tom*_*ble 6

要回答最后一个问题(其他人似乎在上面已经得到了彻底回答),“我应该冒泡确切的异常还是使用 Exception 掩盖它?”

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

public void myMethod() throws Exception {
    // ... something that throws FileNotFoundException ...
}
Run Code Online (Sandbox Code Playgroud)

不,始终声明可能的最精确的异常,或此类异常的列表。你声明你的方法能够抛出的异常是你的方法和调用者之间契约的一部分。抛出"FileNotFoundException"意味着有可能文件名无效,找不到文件;调用者需要聪明地处理它。投掷Exception意味着“嘿,该死的事情发生了。成交。” 这是一个很差的API

在第一篇文章的评论中有一些示例,其中“throws Exception”是一个有效且合理的声明,但对于normal您将要编写的大多数“ ”代码而言,情况并非如此。


Dan*_*van 6

我认为对于使用外部库的开发人员来说,检查异常是一个很好的提醒,在异常情况下,该库中的代码可能会出错。


小智 5

检查的异常在编译时由JVM检查,并且与资源(文件/ db/stream/socket等)相关.检查异常的动机是在编译时如果资源不可用,应用程序应该定义一个替代行为来在catch/finally块中处理它.

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

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


Tho*_*mas 5

最喜欢的关于未检查异常和已检查异常之间差异的描述由 Java 教程跟踪文章“未检查异常 - 争议”提供(很抱歉在这篇文章中获得了所有基本知识 - 但是,嘿,基础知识有时是最好的):

这是底线准则:如果可以合理地期望客户端从异常中恢复,请将其设为已检查的异常。如果客户端无法从异常中恢复,则将其设为未经检查的异常

“抛出什么类型的异常”的核心是语义(在某种程度上),上面的引用提供了很好的指导(因此,我仍然对 C# 摆脱了检查异常的概念感到震惊 - 特别是正如 Liskov 所主张的那样它们的用处)。

剩下的就变得合乎逻辑了:编译器希望我明确响应哪些异常?您希望客户从中恢复的那些。