【问题标题】:Catching an exception that is nested into another exception捕获嵌套到另一个异常中的异常
【发布时间】:2011-02-26 16:20:44
【问题描述】:

我想捕获一个异常,它嵌套在另一个异常中。 我目前正在这样做:

} catch (RemoteAccessException e) {
    if (e != null && e.getCause() != null && e.getCause().getCause() != null) {
        MyException etrp = (MyException) e.getCause().getCause();
        ...
    } else {
        throw new IllegalStateException("Error at calling service 'service'");
    }
}

有没有办法更高效、更优雅地做到这一点?

【问题讨论】:

    标签: java exception try-catch nested-exceptions


    【解决方案1】:

    如果您正在调查异常是否由自定义异常(例如 MyException)引起,您可以使用 while 循环进行迭代,直到找到 MyException. 的实例

    boolean isCausedByMyException(Throwable exception) {
        do {
            if (exception instanceof MyException) {
                return true;
            }
    
            exception = exception.getCause();
        } while (exception != null);
    
        return false;
    }
    

    【讨论】:

      【解决方案2】:

      我想你也可以像 here 那样使用ExceptionUtils.throwableOfThrowable()

      【讨论】:

        【解决方案3】:

        我刚刚通过编写一个简单的实用方法解决了这样的问题,它将检查整个引起链。

          /**
           * Recursive method to determine whether an Exception passed is, or has a cause, that is a
           * subclass or implementation of the Throwable provided.
           *
           * @param caught          The Throwable to check
           * @param isOfOrCausedBy  The Throwable Class to look for
           * @return  true if 'caught' is of type 'isOfOrCausedBy' or has a cause that this applies to.
           */
          private boolean isCausedBy(Throwable caught, Class<? extends Throwable> isOfOrCausedBy) {
            if (caught == null) return false;
            else if (isOfOrCausedBy.isAssignableFrom(caught.getClass())) return true;
            else return isCausedBy(caught.getCause(), isOfOrCausedBy);
          }
        

        当您使用它时,您只需创建一个从最具体的异常到最不具体的 if 列表,并带有一个备用 else 子句:

        try {
          // Code to be executed
        } catch (Exception e) {
          if (isCausedBy(e, MyException.class)) {
            // Handle MyException.class
          } else if (isCausedBy(e, AnotherException.class)) {
            // Handle AnotherException.class
          } else {
            throw new IllegalStateException("Error at calling service 'service'");
          }
        }
        

        cmets 中每个请求的替代/添加

        如果您想使用类似的方法来获取您要查找的类的 Exception 对象,您可以使用这样的方法:

          private boolean getCausedByOfType(Throwable caught, Class<? extends Throwable> isOfOrCausedBy) {
            if (caught == null) return null;
            else if (isOfOrCausedBy.isAssignableFrom(caught.getClass())) return caught;
            else return getCausedByOfType(caught.getCause(), isOfOrCausedBy);
          }
        

        除了isCausedBy()之外还可以这样使用:

          if (isCausedBy(e, MyException.class)) {
            Throwable causedBy = getCausedBy(e, MyException.class);
            System.err.println(causedBy.getMessage());
          }
        

        也可以直接使用它来代替isCausedBy(),尽管这是否更具可读性可能是一个见仁见智的问题。

          Throwable causedBy;
          if ((causedBy = getCausedBy(e, IllegalAccessException.class)) != null) {
            System.err.println(causedBy.getMessage());
          }
        

        【讨论】:

        • 很好的解决方案,但我猜 isCausedBy 应该是 getCausedBy 并返回搜索到的类而不仅仅是布尔值。否则要处理异常,如果 isCausedBy 返回 true,则必须再次搜索它。
        • @MaksimMaksimov 不错。如果您尝试处理多个特定的实现,它会使处理代码更加麻烦。但是,如果您只是在寻找一个特定的和/或您需要没有堆栈跟踪的 causedBy.getMessage(),那么需要您建议的调整。
        • 嘿..你们可以举一个@MaksimMaksimov给出的建议的例子吗?会很有帮助..
        • @Vicky 我添加了一个示例。如果这个答案有用,别忘了点赞! :)
        【解决方案4】:

        没有更优雅的方式选择性地“捕获”嵌套异常。我想如果你经常捕获这种嵌套异常,你可能会将代码重构为一个通用的实用方法。但它仍然不会优雅或高效。

        优雅的解决方案是取消异常嵌套。要么一开始就不将异常链接起来,要么(有选择地)展开并重新抛出嵌套的异常,使其进一步向上堆栈。

        异常倾向于嵌套的原因有 3 个:

        1. 您已决定原始异常的详细信息不太可能对应用程序的错误恢复有用......但您希望保留它们以用于诊断目的。

        2. 您正在实现的 API 方法不允许特定的已检查异常,但您的代码不可避免地会抛出该异常。一种常见的解决方法是将已检查的异常“走私”到未检查的异常中。

        3. 1234563 /p>

        在第一种情况下,如果您现在需要区分包装的异常,那么您最初的假设是不正确的。最好的解决方案是更改方法签名,这样您就可以摆脱嵌套。

        在第二种情况下,您可能应该在控制通过有问题的 API 方法后立即解包异常。

        在第三种情况下,你应该重新考虑你的异常处理策略;即正确地做到这一点2.


        1 - 事实上,由于在 Java 7 中引入了多异常捕获语法,这样做的半正当理由之一已经消失。

        2 - 不要将您的 API 方法更改为 throws Exception。那只会让事情变得更糟。现在,您每次调用方法时都必须“处理”或传播Exception。这是癌症...

        【讨论】:

        • 第三个原因也可以通过抛出可以抛出的异常类型的更通用的超类来解决(如IOException)。仅当存在许多相似的异常类型并因此可以分组时才会出现这种情况。
        • @Marc - 仅在某些情况下“有效”。如果异常的常见超类型是Exception,那么声明 that 是一个非常糟糕的主意。此外,从代码可读性和可维护性的角度来看,声明单个异常通常更好。或者至少,在一定程度上。无论哪种方式包装都是案例#3“问题”的懒惰解决方案。
        【解决方案5】:

        你可以这样做:

        catch (RemoteAccessException e) {
            int index = ExceptionUtils.indexOfThrowable(e, MyExcetption.class)
            if (index != -1) {
                 //handleMyException
            } else {
            }
        }
        

        【讨论】:

          【解决方案6】:

          ExceptionUtils#getRootCause() 方法在这种情况下会派上用场。

          【讨论】:

          • 这是最好的答案,女士们和男士们
          • 如果您只需要根本原因,那么就是这样。但是,如果您在 CauseBy 链中寻找特定的 Exception 实现,请在下面查看我的答案。
          【解决方案7】:

          您应该添加一些检查以查看e.getCause().getCause() 是否真的是MyException。否则此代码将抛出ClassCastException。我可能会这样写:

          } catch(RemoteAccessException e) {
              if(e.getCause() != null && e.getCause().getCause() instanceof MyException) {
                  MyException ex = (MyException)e.getCause().getCause();
                  // Do further useful stuff
              } else {
                  throw new IllegalStateException("...");
              }
          }
          

          【讨论】:

          • 在这种情况下不需要检查 e.getCause().getCause() != null 因为 instanceof 如果为 null 将返回 false。
          【解决方案8】:

          我认为您没有理由希望异常处理高效而优雅,我满足于有效。它们被称为异常是有原因的。

          此代码将是维护的噩梦。你不能重新设计调用堆栈来抛出你感兴趣的异常吗?如果它很重要,则方法签名应该显示它,而不是隐藏在其他 2 个异常中。

          第一个 (e != null) 是不必要的。

          您可以将第 3 个更好地更改为 e.getCause().getCause() instanceof MyException)

          【讨论】:

          • “我看不出你为什么希望异常处理高效而优雅......” - 真的吗?真的吗? (当然,它不会发生,但这不是不希望它发生的理由。)
          • :-) 我知道,但我一天只有有限的几个小时,我宁愿让其余的时间高效而优雅。我的意思是试图优雅地处理 Java 异常就像试图教犀牛芭蕾舞:它永远不会漂亮。并且抛出异常很慢,所以它永远不会有效。所以我满足于有效。
          • 如果它包含错误(在此代码示例中确实如此),那将是一场维护噩梦。对于支持工程师来说,最大的挫折是异常处理代码中的错误——它应该已经捕获有关情况的相关信息,但它没有(然后消失得无影无踪)。
          • 我全心全意地食人魔。异常处理越少越好。做错比做对容易 1000 倍。如果捕获异常的地方少一些,允许更多异常飞行,生活会更好。是的,有时您无法有效地“隐藏实现”等,但这比隐藏由缺陷触发的故障要好得多。
          【解决方案9】:

          我怀疑,但您可以通过instanceof 检查异常类型是否正确。

          编辑:嵌套异常被包装应该是有原因的,所以你要问自己捕获嵌套异常的目的是什么。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-10-19
            • 2011-11-27
            • 2013-08-09
            • 2010-09-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多