Java 我该如何让未经检查的异常冒泡并记录?

Java 我该如何让未经检查的异常冒泡并记录?,java,exception,exception-handling,Java,Exception,Exception Handling,所以引用了一个名为“异常处理反模式博客”的页面,该页面似乎是由Oracle编写的(或至少是批准的) 未检查的异常可能不应该重试,正确的响应通常是什么也不做,让它从方法中冒出来并通过执行堆栈。这就是为什么不需要在throws子句中声明它。最终,在高级别执行时,可能应该记录异常 我不确定我是否理解这一点。如何记录未检查的异常?如果我有类似于: public static void main(String args) { foo(); // How do I know what to

所以引用了一个名为“异常处理反模式博客”的页面,该页面似乎是由Oracle编写的(或至少是批准的)

未检查的异常可能不应该重试,正确的响应通常是什么也不做,让它从方法中冒出来并通过执行堆栈。这就是为什么不需要在throws子句中声明它。最终,在高级别执行时,可能应该记录异常

我不确定我是否理解这一点。如何记录未检查的异常?如果我有类似于:

public static void main(String args) {
    foo();
    // How do I know what to log here? The method I am calling
    // is not throwing an Exception. 
    // Do I just blindly catch(Exception ex)?
}

static void foo() {
    bar();
}

static void bar() {
    baz();
}

static void baz() {
    // I will do nothing as Oracle suggests and let this exception bubble up.. I wonder who is going to catch it and how this is going to be logged though!
    throw new NullPointerException();
}
try {
 bar();
} catch(NullPointerException e) {
 throw new HigherLevelException(...);
}
你能帮我理解Oracle的建议吗?我看不到任何直接(或明确)的方法可以在更高级别捕获运行时异常(我不明白为什么它不被称为未检查异常…),我不确定这种建议的做法是否有用。对我来说,如果它讨论的是已检查的异常,则更有意义。类似于


如果在方法中抛出一个已检查的异常,而该方法不适合重新尝试,正确的响应是让它冒泡并记录


您可以像使用try-catch块捕获任何其他异常一样捕获它们。但好处是你不必这么做

用例可能会有所不同。在我看来,最流行的是当在该位置捕获异常没有意义时,或者应该实现比该方法高几个级别(就方法而言)的适当处理,调用抛出异常的方法(抱歉,如果这不够清楚的话)

例如,java中典型的web应用程序布局如下:有一层控制器、一层服务和一层dao。第一个负责调度请求,第二个负责管理业务逻辑,最后一个负责实际调用db。例如,在这里,如果dao级别出现问题,在服务层捕获异常通常没有多大意义。这里可以使用未检查的异常。您记录一个异常并抛出一个未经检查的异常,这样用户就可以在上面的某个级别处理该异常,从而获得对应用程序工作的有价值的反馈

在这种情况下,如果抛出一个选中的异常,您将不得不在上面的每一级对其进行重新排序,以便将其冒泡到实际处理的位置。因此,这里最好使用未经检查的异常,以免复制和粘贴所有丑陋的try-catch块,重新引发异常并将throws子句添加到方法中

public static void main(String[] args) {
        try {
            foo();
        }
        catch(NullPointerException e){
               System.out.println("NullPointerException in main.");
        }        
    }

    static void foo() {
        bar();
    }

    static void bar() {
        baz();
    }

    static void baz() {
        // I will do nothing as Oracle suggests and let this exception bubble up.. I wonder who is going to catch it and how this is going to be logged though!
        throw new NullPointerException();
    }
输出

NullPointerException in main.

基本上,错误是在更高的级别上出现的,因此不需要在baz()方法级别捕获它。如果我理解正确的话。

据我所知,文档建议您在代码的高层有一个通用处理程序,它可以像主方法中的注释所建议的那样记录这种“意外”(不可恢复?)的异常。所以它可能看起来像这样

public static void main(String args) {
    try {
      foo();
    } 
    catch (ArithmeticException aex) { //if it's arithmetic log differently
         log("arith issue! "+aex.getMessage());
    } 
    catch (Exception ex) { //Otherwise do the best we can 
            log("unknown issue! "+ex.getMessage())
    } 
}

因此,仍然没有恢复的途径,但至少在过程结束之前,您有机会记录问题。在许多情况下,您还可以使用Exception(或throwable)方法来获取堆栈跟踪和第一个因果异常-因此可能会记录许多额外的有用信息。

您还可以注册一个全局异常处理程序,该处理程序将处理代码未捕获的异常:

Thread.setDefaultUncaughtExceptionHandler

然后,此异常句柄可以记录发生的任何事件。

我如何知道在此处记录什么?我正在调用的方法没有引发异常。

正如Joshua Bloch在《有效Java》中所建议的那样

使用Javadoc@throws标记记录每个未检查的异常 方法可以抛出,但不要使用throws关键字来包含 方法声明中未检查的异常

如果您在多层应用程序中使用方法包装,我建议您使用异常翻译:

更高的层应该捕获较低级别的异常,并在它们的位置抛出可以用更高级别抽象来解释的异常

因此,我认为在你的例子中,实际上你应该使用如下内容:

public static void main(String args) {
    foo();
    // How do I know what to log here? The method I am calling
    // is not throwing an Exception. 
    // Do I just blindly catch(Exception ex)?
}

static void foo() {
    bar();
}

static void bar() {
    baz();
}

static void baz() {
    // I will do nothing as Oracle suggests and let this exception bubble up.. I wonder who is going to catch it and how this is going to be logged though!
    throw new NullPointerException();
}
try {
 bar();
} catch(NullPointerException e) {
 throw new HigherLevelException(...);
}

首先,这是一个一般性的建议,它取决于上下文。其背后的思想是,当发生运行时异常时(例如,
NullPointerException
),系统通常处于不确定状态,这意味着不能保证其余代码按预期执行,因此最好停止所有操作

在大多数情况下,代码将在单独的线程中运行,异常只会停止当前线程,而程序的其余部分将继续运行

在您的示例中并非如此,因为所有内容都在单个线程中执行,因此未捕获的异常将有效地停止整个程序。在这种情况下,您可能希望捕获异常并处理它

public static void main(String args) {
    try {
        foo();
    catch(Throwable t) {
        t.printStackTrace(); // log exception
        // handle the failure
    }
}
您还可以更早地捕获异常,记录并进一步重试它

static void bar() {
    try {
        baz();
    catch (Throwable t) {    // catch
        t.printStackTrace(); // log
        throw t;             // rethrow further
    }
}
编辑:捕获可丢弃的
而不是
异常
,也将捕获
错误


注意:捕捉可丢弃的东西通常是一个坏主意,应该只在特定的目的下进行,而不是在一般情况下。请参阅@RC.的注释。

有一种非常简单的方法可以捕获未检查的异常,因为它们都是
RuntimeException
Error
的子类:

public static void main(String[] args) {
    try {
        // your code
    } catch (RuntimeException | Error e) {
        // handle uncaught exceptions, e.g.
        e.printStackTrace();
    }
}

关于异常最重要的指导原则是,无法成功完成任务的方法应该抛出异常

只有在能够保证成功完成方法任务的情况下,才应该捕获方法内部的异常(而不重新抛出此异常或其他异常)。根据我的经验,只有在非常特定的情况下才是如此,例如,如果第一次尝试失败,或者如果你真的,你有另一种方法来尝试<