5

在您看来,拦截 Java 应用程序中所有异常的最简单方法是什么?是否需要 AOP 来提供这种功能,或者可以使用动态代理来完成,还是有其他方法?关于对执行性能的影响,最简单的解决方案也是一个很好的解决方案吗?我想从更有经验的开发人员那里听到可能的解决方案,因为我正在尝试掌握有关该主题的技术知识。

编辑:

感谢您的好建议,但当前的建议是否仅适用于已检查的异常?未经检查的异常(例如 NullPointerExceptions)如果可以被捕获并且捕获它们的应用程序会转储堆/堆栈以在崩溃时为您提供应用程序的当前上下文,这不是很有用吗?

4

7 回答 7

11

您想要拦截每个异常的目的是什么 - 用于记录、错误报告?

拦截 Java 程序每一行中的每一个异常是可能的,但可能会导致相当大的性能损失。如果不可避免,最好使用像AspectJ这样的东西,它可以在编译时运行(即它“编织”到你的类文件中),因此比动态代理快得多。

但这是我会不惜一切代价避免做的事情!一般来说,我会说最好限制要捕获的异常的范围。此外,您可能想查看Thread.setDefaultUncaughtExceptionHandler,我发现它对于在 GUI 应用程序中显示错误对话框很有用。

于 2009-10-02T13:05:12.293 回答
9

与 Phil 的回答类似,这里有一些示例代码,展示了如何使用未捕获的异常处理程序。这适用于已检查和未检查的异常。

编辑:更新以根据问题中的更新评论打印堆栈跟踪。

import java.lang.Thread.UncaughtExceptionHandler;

public class Test {

    public static void main(String[] args) throws Exception {
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {

            @Override
            public void uncaughtException(Thread t, Throwable e) {
                e.printStackTrace();
            }}

        );

        // throw new RuntimeException(); /* this works too */
        throw new Exception();
    }

}
于 2009-10-02T13:23:06.370 回答
5

在 Nate 和 Konamiman ......你的建议根本不起作用,也没有回答 OP 的问题。

如果 OP 从您的 try/catch 中启动一个新线程怎么办?

例如:

public static void main(String[] args) {
    try {
        final Thread t = new Thread( new Runnable() {
            public void run() {
                System.out.println( 3 / Math.min(0,4) );
            }
        } );
        t.start();
    catch(Throwable t) {
        ...
    }
}

那么你没有捕捉到异常。

正确的做法是使用 Thread.setDefaultUncaughtExceptionHandler。

于 2009-10-02T13:49:41.523 回答
3
public static void main(String[] args) {
    try {
        ...
    catch(Throwable t) {
        ...
    }
}
于 2009-10-02T13:00:56.897 回答
0

比简单地捕获所有异常更重要的是在哪里捕获异常:

  1. 不要捕获异常,除非你能做些什么。如果您对此无能为力,要么让它冒泡到下一个级别,要么抓住它,将其重新包装为更具体的异常并重新抛出它。

  2. 总是有一个全局异常处理块(就像在 Nate 的回答中一样)来捕捉任何你无法在任何地方处理的错误,这样你就可以优雅地失败。

于 2009-10-02T13:05:41.483 回答
0

如果你只是想捕获一个异常,一个 try/catch 块就足够了。如果您想防止它们被抛出或对异常进行一些记录或包装,您可能需要一些 AOP 来执行此操作。AspectJ 可以很好地处理它,但要注意潜在的性能瓶颈。

动态代理仅在方法被代理时才起作用,因此如果在代理对象的执行过程中抛出并捕获任何异常,则代理无法拦截该异常。

要考虑的一件事是,您是要捕获所有异常,还是仅捕获已检查异常。为了捕获所有异常,AspectJ 的around throwingafter throwing建议将围绕每个方法编织一些处理,这涉及在每个方法调用时创建 JoinPoint 对象和合成方法。这通常不是问题,除非该过程处于紧密循环中,垃圾收集可以通过屋顶进行。

于 2009-10-02T13:09:26.693 回答
0

对于所有质疑需要捕获所有异常的人......

捕获所有异常有很多正当理由:

  • 有人提到在 GUI 应用程序中显示一个对话框来说明问题
  • 解决您真正需要的第 3 方 API 中的错误
  • 解决一些 JVM 实现中的错误
  • 自我修复软件。
  • 自动在线异常报告。

请注意,Java本身会拦截 EDT(事件调度线程)上的所有异常,并在 EDT 终止时生成一个新的 EDT。你可以认为这是一种“自我修复”的软件。EDT 死亡并非闻所未闻,也不会完全阻止应用程序正常运行(相反)。(而且,是的,Swing 和 AWT 有相当多的错误,看看 Sun 错误游行;)

有人可能会争辩说,任何有自尊的软件实际上都应该考虑到意外例外的情况(您发布的所有软件都是 100% 无错误并且永远不会获得新版本、错误修复版本吗?)并在这种情况下做一些聪明的事情发生不可预见的异常。

“聪明的东西”可能是防止软件崩溃(如在 EDT 案例中)、警告用户、发送报告等。

回答质疑是否需要做这样的事情或建议这样做是一种不好的做法,应该修改恕我直言。

于 2009-10-02T14:05:22.440 回答