了解Java中的检查与未检查异常

| 约书亚·布洛赫(Joshua Bloch)在《有效的Java》中说   将检查的异常用于   可恢复的条件和运行时间   编程错误的例外   (第二版中的项目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
}
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);?
}
4.现在,上面的代码也不能成为检查异常吗?我可以尝试恢复这种情况吗?我可以吗? (注意:我的第三个问题在上面的
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{

}
为什么他们让异常冒出来?尽快解决错误不是更好吗?为什么冒泡? 6.我应该冒充确切的异常还是使用Exception屏蔽它? 以下是我的读物 在Java中,什么时候应该创建一个检查异常,什么时候应该是运行时异常? 何时选择已检查和未检查的异常     
已邀请:
许多人说完全不应该使用检查的异常(即应明确捕获或重新抛出的异常)。例如,它们在C#中已被淘汰,并且大多数语言都没有它们。因此,您始终可以抛出
RuntimeException
的子类(未经检查的异常) 但是,我认为检查异常非常有用-当您要强制API用户考虑如何处理特殊情况(如果可恢复)时,可以使用它们。只是在Java平台中过度使用了已检查的异常,这使人们讨厌它们。 这是我对该主题的扩展视图。 至于特定的问题: ѭ6是否考虑过检查异常? 否。
NumberFormatException
是未选中的(=是
RuntimeException
的子类)。为什么?我不知道。 (但是应该有方法
isValidInteger(..)
RuntimeException
是未经检查的例外吗? 对,就是这样。 我该怎么办? 这取决于该代码在哪里以及您想要发生什么。如果它在UI层中-捕获并显示警告;如果它在服务层中-根本不要抓住它-让它冒泡。只是不要吞下异常。如果在大多数情况下发生异常,则应选择以下一种: 记录并返回 重新抛出它(声明它被方法抛出) 通过在构造函数中传递当前异常来构造新异常 现在,上面的代码难道不是一个检查过的异常吗?我可以尝试恢复这种情况吗?我可以吗? 可以的。但是也没有什么可以阻止您捕获未经检查的异常 人们为什么在throws子句中添加类
Exception
? 大多数情况下,是因为人们懒于考虑要捕获什么和重新抛出什么。投掷ѭ11是一个坏习惯,应避免。 las,没有一个单一的规则可以让您确定何时捕获,何时重新抛出,何时使用已检查的异常以及何时使用未检查的异常。我同意这会引起很多混乱和很多错误代码。 Bloch陈述了一般原则(您引用了其中一部分)。通常的原则是将异常抛出到可以处理它的层。     
某项是否为“受检查的异常”与您是否捕获它或在catch块中执行的操作无关。它是异常类的属性。除
RuntimeException
及其子类外,is11ѭ的子类的任何内容都是已检查的异常。 Java编译器迫使您要么捕获检查的异常,要么在方法签名中声明它们。本来可以提高程序安全性,但是大多数人似乎认为它不值得产生它的设计问题。   他们为什么让异常冒泡   起来吗?不是句柄错误越早   更好?为什么冒泡? 因为这就是例外的全部内容。没有这种可能性,您将不需要例外。它们使您能够在选择的级别上处理错误,而不是强迫您使用错误最初发生的低级方法进行处理。     
以上是否被视为检查异常? 没有 如果您处理异常,则不能将其设为15,如果将其设为5。
RuntimeException
unchecked exception
? 是
Checked Exceptions
java.lang.Exception
subclasses
Unchecked Exceptions
java.lang.RuntimeException
subclasses
引发检查异常的调用需要放在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.
}
相反,请执行以下操作:
try {
     attemptLogin(userCredentials);
} catch (SQLException sqle) {
     throw new LoginFailureException(sqle); //<-- Wrap original exception to pass on root cause upstairs!.
}
消除原始根本原因,埋藏无法恢复的实际原因是生产支持团队的噩梦,在生产支持团队中,他们只能访问应用程序日志和错误消息。 尽管后者是一个更好的设计,但是许多人不经常使用它,因为开发人员只是无法将底层消息传递给调用者。因此,请牢记:
Always pass on the actual exception
是否包装在任何特定于应用程序的异常中。   尝试try29 一般而言,ѭ5是不应尝试的。它们通常表示发生编程错误,应单独使用。相反,程序员应在调用某些可能导致ѭ5的代码之前检查错误情况。例如:
try {
    setStatusMessage(\"Hello Mr. \" + userObject.getName() + \", Welcome to my site!);
} catch (NullPointerException npe) {
   sendError(\"Sorry, your userObject was null. Please contact customer care.\");
}
这是不好的编程习惯。相反,应该执行null检查,例如-
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()方法中复制所有字符串到整数的转换代码-如果由开发人员实现,则容易出错。因此,最好不要尝试捕获。 所以
NullPointerException
和ѭ6both都是
RuntimeExceptions
,捕获
NullPointerException
应该用优美的空检查代替,而我建议明确捕获
NumberFormatException
,以避免可能引入容易出错的代码。     
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
2。是的,以及扩展它的每个异常。 3。无需捕获并抛出相同的异常。在这种情况下,您可以显示一个新的文件对话框。 4。 FileNotFoundException已为检查的异常。 5。如果期望调用
someMethod
的方法捕获异常,则可以抛出后者。它只是“传球”。它的用法示例是,如果您想将其放入自己的私有方法中,而改为在公共方法中处理异常。 甲骨文文档本身就是一本好书:http://download.oracle.com/javase/tutorial/essential/exceptions/runtime.html   设计人员为什么决定强制使用一种方法来指定可在其范围内抛出的所有未捕获的检查异常?方法可以引发的任何异常都是方法的公共编程接口的一部分。那些调用方法的人必须知道方法可能引发的异常,以便他们可以决定如何处理这些异常。这些异常与方法的参数和返回值一样,也是该方法的编程接口的一部分。      下一个问题可能是:\“如果很好地记录了方法的API,包括它可能抛出的异常,为什么也不要指定运行时异常?\”运行时异常表示由程序集导致的问题。编程问题,因此,不能合理地期望API客户端代码从它们中恢复或以任何方式进行处理。这些问题包括算术异常,例如除以零;指针异常,例如尝试通过空引用访问对象;以及索引异常,例如尝试通过太大或太小的索引访问数组元素。 Java语言规范中还有一些重要的信息:   throws子句中命名的已检查异常类是方法或构造函数的实现者与用户之间的合同的一部分。 恕我直言,底线是您可以捕获任何“ 5”,但您并不需要这样做,实际上,不需要维护该异常以抛出相同的未经检查的异常,因为这些异常不是合同的一部分。     
1)不,NumberFormatException是未经检查的异常。即使您抓住了它(也不需要),因为它未被选中。这是因为它是
IllegalArgumentException
的子类,也是
RuntimeException
的子类。 2)“ 5”是所有未检查的异常的根。
RuntimeException
的每个子类均未选中。除“错误”(位于“ѭ47”下)外,还将检查所有其他“ Exceptions”和“ѭ47”。 3/4)您可以提醒用户他们选择了不存在的文件,并要求新的文件。或者只是退出以通知用户他们输入的内容无效。 5)投掷ing49ѭ是不好的做法。但更一般而言,您可能会引发其他异常,以便调用方可以决定如何处理它。例如,如果您编写了一个库来处理读取一些文件输入,并且您的方法被传递了不存在的文件,则您不知道如何处理该文件。呼叫者想再次询问还是退出?因此,您可以将Exception链抛回到调用方。 在许多情况下,由于程序员未验证输入而导致出现“ 50”(在第一个问题中为“ 6”的情况)。这就是选择捕获它们的原因,因为有更优雅的方法来避免生成这些异常。     
已检查-容易发生。在编译时签入。 例如: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)
此处的异常是由于错误的数据所致,决不能在编译期间确定。     
被检查的异常由JVM及其与资源(文件/ db /流/套接字等)相关的编译时进行检查。检查异常的动机是,在编译时,如果资源不可用,应用程序应在catch / finally块中定义替代行为来处理此问题。 未经检查的异常纯粹是程序错误,错误的计算,空数据甚至业务逻辑故障都可能导致运行时异常。处理/捕获代码中未经检查的异常绝对好。 解释来自http://coder2design.com/java-interview-questions/     
我最喜欢的未检查和已检查异常之间的区别的描述是由Java教程跟踪文章\“ Unchecked Exceptions-The Controversy \”提供的(很抱歉,这篇文章中的所有基本知识都很少-但是,有时候基础知识是最好的):   这是底线指南:如果客户可以合理地做到   期望从异常中恢复,请使其成为已检查的异常。如果   客户无法做任何事情来从异常中恢复,使其成为   未经检查的异常 “要抛出的异常的类型”的核心是语义(某种程度上),上面的引用提供了出色的指导原则(因此,我仍然对C#摆脱检查异常的观念感到震惊,尤其是Liskov认为它们的用处)。 其余的则变得合乎逻辑:编译器希望我明确地响应哪些异常?您希望客户端从中恢复的服务器。     
要回答最后一个问题(上面的其他问题似乎已经完全回答了),“我应该冒出确切的异常还是使用Exception屏蔽它?” 我假设您的意思是这样的:
public void myMethod() throws Exception {
    // ... something that throws FileNotFoundException ...
}
不,总是声明尽可能精确的异常或此类异常的列表。您声明您的方法可以抛出的异常是您的方法与调用者之间的合同的一部分。抛出“ 54”表示文件名无效并且找不到该文件。呼叫者将需要智能地处理它。投掷
Exception
表示\“嘿,没事。请成交。\”这是很差的
API
。 在第一篇文章的评论中,有一些示例,其中“ throws 11”是有效且合理的声明,但对于大多数您编写的“ 58 58”代码而言,情况并非如此。     
运行时异常 运行时异常称为未检查的异常。所有其他例外 被检查的异常,它们不是从java.lang.RuntimeException派生的。 检查异常 必须在代码中的某个地方捕获已检查的异常。如果您调用 抛出一个已检查异常的方法,但是您没有捕获到该已检查异常 在某个地方,您的代码将无法编译。这就是为什么他们被称为已检查 例外:编译器检查以确保已对它们进行处理或声明。 Java API中的许多方法都会抛出已检查的异常,因此您经常会编写异常处理程序来应对未编写的方法所生成的异常。     
为什么他们让异常冒出来?尽快解决错误不是更好吗?为什么冒泡? 例如,假设您有一些客户端-服务器应用程序,并且客户端请求了一些无法找到的资源,或者由于某些其他错误,在处理用户请求时服务器端可能发生了某些错误,那么它就是服务器有责任告诉客户为什么他不能得到他所要求的东西,因此要在服务器端实现这一点,编写代码以使用throw关键字而不是吞咽或处理该异常来抛出异常。 /吞下它,那么就没有机会向客户端暗示发生了什么错误。 注意:要清楚地说明发生了什么错误类型,我们可以创建自己的Exception对象并将其扔给客户端。     
我认为检查异常对于使用外部库的开发人员是一个很好的提醒,在特殊情况下,该库中的代码可能会出错。 在此处阅读有关已检查与未检查异常的更多信息http://learnjava.today/2015/11/checked-vs-unchecked-exceptions/     
我只想添加一些根本不使用检查异常的理由。这不是一个完整的答案,但我认为它确实回答了您的部分问题,并补充了许多其他答案。 每当涉及检查的异常时,方法签名中的某个地方就会有一个
throws CheckedException
CheckedException
可以是任何检查的异常)。签名不会引发异常,引发异常是实现的一个方面。接口,方法签名,父类,所有这些都不应该取决于其实现。这里使用检查异常(实际上是必须在方法签名中声明“ 61”的事实)将您的高层接口与这些接口的实现绑定在一起。 让我给你看一个例子。 让我们有一个如此漂亮干净的界面
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();
    }
}
此类Bar无法编译。由于InterruptedException是一个已检查的异常,因此您必须捕获它(在foo()方法内部使用try-catch捕获)或声明您正在抛出它(在方法签名中添加
throws InterruptedException
)。由于我不想在这里捕获此异常(我希望它向上传播,以便可以在其他地方正确处理),因此让我们更改签名。
public class Bar implements IFoo {
    @Override
    public void foo() throws InterruptedException {
        throw new InterruptedException();
    }
}
此类的Bar也不会编译! Bar \的方法foo()不会覆盖IFoo \的方法foo(),因为它们的签名不同。我可以删除@Override批注,但是我想对接口IFoo进行编程,例如
IFoo foo;
,然后再决定要使用哪种实现,例如
foo = new Bar();
。如果Bar的方法foo()没有覆盖IFoo的方法foo,当我执行“ 70”时,它将不会调用Bar的foo()的实现。 要使Bar的ѭ71覆盖IFoo的ѭ72,我必须在IFoo的方法签名中添加
throws InterruptedException
。但是,这将导致我的Foo类出现问题,因为它的foo()方法的签名与IFoo的方法签名不同。此外,如果我在Foo \的方法foo()中添加“ 66”,则会收到另一个错误,指出Foo \的方法foo()声明它抛出了InterruptedException,但从未抛出InterruptedException。 如您所见(如果我在解释这些内容时做得不错),我抛出了InterruptedException之类的已检查异常的事实迫使我将接口IFoo绑定到其中的一种实现,这反过来对IFoo的其他实现造成破坏! 这是为什么检查异常是BAD的一大原因。大写。 一种解决方案是捕获已检查的异常,将其包装在未检查的异常中,然后抛出未检查的异常。     
Java区分两种类别的异常(已检查和未检查)。 Java对检查的异常强制执行catch或声明的要求。 异常的类型确定是否选中了异常。 所有直接或间接类
RuntimeException
subclasses
的异常类型 是未经检查的异常。 从类
Exception
继承但不是not5ѭ的所有类都被视为
checked exceptions
。 继承自Error类的类被视为未选中。 编译器检查每个方法调用和减速度以确定是否 方法抛出
checked exception
。 如果是这样,编译器将确保捕获异常或在throws子句中声明该异常。 为了满足catch-or-declare要求的声明部分,生成的方法 例外必须提供包含“ 82”的“ 61”子句。
Exception
类被定义为在被认为足够重要以进行捕获或声明时被检查。     
这是一条可以帮助您做出决定的简单规则。它与Java中如何使用接口有关。 以您的类为例,并设想为其设计一个接口,以使该接口描述该类的功能,但不描述任何基础实现(如接口应这样)。假设您可能以另一种方式实现该类。 查看接口的方法,并考虑它们可能引发的异常: 如果某个方法可以引发异常,而无论其基础实现如何(换句话说,它仅描述功能),则该异常可能应该是接口中的已检查异常。 如果异常是由基础实现引起的,则该异常不应在接口中。因此,它必须是您的类中的一个未检查的异常(因为未检查的异常不需要出现在接口签名中),或者您必须包装它并将其作为接口方法一部分的被检查的异常重新抛出。 要决定是否应该包装并重新抛出,您应该再次考虑对于接口用户必须立即处理异常情况是否有意义,或者该异常是如此普遍以至于您无能为力,因此应该向上传播。当表示为您正在定义的新接口的功能时,包装的异常是否有意义?或者它只是一堆可能的错误情况的载体,而其他方法也可能会发生这种情况?如果是前者,它可能仍然是检查异常,否则应取消选中。 通常,您不应该计划“破坏”异常(捕获并重新抛出)。要么由调用方处理异常(在这种情况下将其选中),要么将其一直处理到高级处理程序(在这种情况下,如果不对其进行检查则最容易)。     
只是要指出,如果在代码中引发了检查的异常,并且catch在上面几层,则需要在您和catch之间的每个方法的签名中声明异常。因此,封装被破坏了,因为throw路径中的所有函数都必须知道该异常的详细信息。     
简而言之,您上面的一个或多个模块应该在运行时处理的异常称为检查异常;其他是未经检查的例外,即
RuntimeException
Error
。 在此视频中,它说明了Java中已检查和未检查的异常: https://www.youtube.com/watch?v=ue2pOqLaArw     
所有这些都是检查异常。未检查的异常是RuntimeException的子类。决定不是如何处理它们,而是代码将它们抛出。如果您不希望编译器告诉您尚未处理异常,则可以使用未经检查的(RuntimeException的子类)异常。这些内容应保存在无法恢复的情况下,例如内存不足错误等。     
如果有人在乎另一种证明不喜欢检查异常的证据,请参见流行的JSON库的前几段: \“尽管这是一个已检查的异常,但是它几乎是不可恢复的。大多数调用者应该简单地将此异常包装到一个未检查的异常中并重新抛出:” 那么,如果我们应该“简单地包装它”,那么为什么世界上会有人让开发人员继续检查该异常呢?大声笑 http://developer.android.com/reference/org/json/JSONException.html     
已检查的异常: 由编译器检查以便在运行时顺利执行程序的异常称为“检查异常”。 这些发生在编译时。 如果这些处理不当,它们将给出编译时错误(Not Exception)。 除RuntimeException外,Exception类的所有子类均为Checked Exception。 假设示例-假设您要离开家进行考试,但是如果您检查自己是否在家里(编译时)拿了Hall Ticket,那么在Exam Hall(运行时)就不会有任何问题。 未检查的异常: 编译器未检查的异常称为未检查的异常。 这些发生在运行时。 如果这些异常处理不当,则不会产生编译时错误。但是该程序将在运行时过早终止。 RunTimeException和Error的所有子类都是未经检查的异常。 假设示例-假设您在考场中,但您的学校因某种原因发生了火灾(在运行时发生),当时您什么也不能做,但可以在编译前进行预防。     
所有异常都必须是检查异常。 未检查的异常是不受限制的异常。无限制的goto被认为是一件坏事。 未检查的异常会破坏封装。为了正确处理它们,必须知道抛出器和捕获器之间的调用树中的所有函数,以避免错误。 异常是引发异常的函数中的错误,而不是处理异常的函数中的错误。异常的目的是通过将是否有错误的决定推迟到另一个上下文中,从而使程序有第二次机会。只有在其他情况下,才能做出正确的决定。     

要回复问题请先登录注册