【问题标题】:What is the advantage of chained exceptions链式异常有什么好处
【发布时间】:2011-02-16 19:03:48
【问题描述】:

我不明白在代码中使用链式异常的好处。

考虑到ResourceLoader example from java world,如果程序员知道遇到ResourceLoadException 的可能性,为什么不捕获相同的异常而不是SQLException?否则,程序员可以在同一代码中捕获这两个异常,而不必抛出一个新的Throwable 实例?

【问题讨论】:

    标签: java exception-handling


    【解决方案1】:

    谁能提供有关需要链式异常的信息吗?

    文章说得很好:

    异常链允许您将一种异常类型映射到另一种异常类型,以便方法可以抛出定义在与方法本身相同抽象级别的异常,而不会丢弃重要的调试信息。

    也就是说,如果您有一个从数据库加载某个对象的方法,您可能更想要一些ResourceLoadException(与方法抽象级别更密切相关)而不是低级别的SQLException,即使那是问题的原始根源。但是,如果您只是抓住SQLException 并抛出ResourceLoadException,您可能会丢失重要的调试信息。

    因此,链接异常是一个不错的选择。您抛出一个“高级”异常,非常适合特定方法,但将它与导致它的异常链接起来。

    否则,程序员可以在同一代码中同时捕获这两个异常,而不必抛出一个新的 Throwable 实例?

    我不太理解你的推理。关键是他不应该担心这个抽象级别的SQLException

    【讨论】:

    • 好的。我的观点是:以下更改会有什么问题(此处仅显示必要的更改)? try {}catch(SQLException e){}catch(ResourceLoadException f){} 。但我理解你关于有一个更密切相关的异常的观点。但现在我的问题是,如果自定义异常与引起的实际异常无关,即如果异常实际上是SQLException 而不是ResourceLoadException,那么异常处理代码可能是一个误导?
    • 该代码更改不会有任何错误,但正如我所说,必须在该抽象级别捕获SQLException 可能根本没有意义.例如,如果有两个资源加载类:MysqlResourceLoaderFileResourceLoader,它们共享一个公共接口 ResourceLoader。那么loadResource-方法显然不应该被声明为抛出SQLException。 (如果我给你一个ResourceLoader 而你不知道它的实际类型怎么办?)
    • 关键是你可能有一个在抽象级别抛出特定异常类型的接口。在实现该接口时,可能需要捕获 SQLException 或其他情况。接口的全部意义在于坚持使用它,无论实现的内容是什么,都不需要更改它……如果这有意义的话。此外,在这种情况下,父类将不知道如何处理 SQLException。它唯一需要做的就是得出“我们搞砸了”的结论,打印出一个错误,并让用户修复它。
    【解决方案2】:

    为什么链异常?

    我们需要链接异常以使日志可读。

    以1.无链式和2.链式为例,异常感受区别

    创建以下例外

        class NoLeaveGrantedException extends Exception {
    
            public NoLeaveGrantedException(String message, Throwable cause) {
                super(message, cause);
            }
    
            public NoLeaveGrantedException(String message) {
                super(message);
            }
        }
    
        class TeamLeadUpsetException extends Exception {
    
            public TeamLeadUpsetException(String message, Throwable cause) {
                super(message, cause);
            }
    
            public TeamLeadUpsetException(String message) {
                super(message);
            }
        }
    
        class ManagerUpsetException extends Exception {
    
            public ManagerUpsetException(String message, Throwable cause) {
                super(message, cause);
            }
    
            public ManagerUpsetException(String message) {
                super(message);
            }
        }
    
        class GirlFriendOfManagerUpsetException extends Exception {
    
            public GirlFriendOfManagerUpsetException(String message, Throwable cause) {
                super(message, cause);
            }
    
            public GirlFriendOfManagerUpsetException(String message) {
                super(message);
            }
        }
    

    现在使用它们

    1.没有链接

        public class MainClass {
    
            public static void main(String[] args) throws Exception {
                getLeave();
            }
    
            static void getLeave() throws NoLeaveGrantedException {
                try {
                    howIsTeamLead();
                } catch (TeamLeadUpsetException e) {
                    e.printStackTrace();
                    throw new NoLeaveGrantedException("Leave not sanctioned.");
                }
            }
    
            static void howIsTeamLead() throws TeamLeadUpsetException {
                try {
                    howIsManager();
                } catch (ManagerUpsetException e) {
                    e.printStackTrace();
                    throw new TeamLeadUpsetException(
                                "Team lead is not in good mood");
                }
            }
    
            static void howIsManager() throws ManagerUpsetException {
                try {
                    howIsGirlFriendOfManager();
                } catch (GirlFriendOfManagerUpsetException e) {
                    e.printStackTrace();
                    throw new ManagerUpsetException("Manager is in bad mood");
                }
    
            }
    
            static void howIsGirlFriendOfManager() 
                 throws GirlFriendOfManagerUpsetException {
                throw new GirlFriendOfManagerUpsetException(
                 "Girl friend of manager is in bad mood");
            }
        }
    

    2。链接

        public class MainClass {
    
            public static void main(String[] args) throws Exception {
                getLeave();
            }
    
            static void getLeave() throws NoLeaveGrantedException {
                try {
                    howIsTeamLead();
                } catch (TeamLeadUpsetException e) {
                    throw new NoLeaveGrantedException("Leave not sanctioned.", e);
                }
            }
    
            static void howIsTeamLead() throws TeamLeadUpsetException {
                try {
                    howIsManager();
                } catch (ManagerUpsetException e) {
                    throw new TeamLeadUpsetException(
                               "Team lead is not in good mood", e);
                }
            }
    
            static void howIsManager() throws ManagerUpsetException {
                try {
                    howIsGirlFriendOfManager();
                } catch (GirlFriendOfManagerUpsetException e) {
                    throw new ManagerUpsetException("Manager is in bad mood", e);
                }
    
            }
    
            static void howIsGirlFriendOfManager() 
               throws GirlFriendOfManagerUpsetException {
                throw new GirlFriendOfManagerUpsetException(
                  "Girl friend of manager is in bad mood");
            }
        }
    

    现在比较日志

    1.没有链接

        com.bskyb.svod.autoingest.GirlFriendOfManagerUpsetException: Girl friend of manager is in bad mood
            at com.bskyb.svod.autoingest.MainClass.howIsGirlFriendOfManager(MainClass.java:61)
            at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:52)
            at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:43)
            at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
            at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
        com.bskyb.svod.autoingest.ManagerUpsetException: Manager is in bad mood
            at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:55)
            at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:43)
            at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
            at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
        com.bskyb.svod.autoingest.TeamLeadUpsetException: Team lead is not in good mood
            at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:46)
            at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
            at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
        Exception in thread "main" com.bskyb.svod.autoingest.NoLeaveGrantedException: Leave not sanctioned.
            at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:37)
            at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
    

    2。链接

        Exception in thread "main" com.bskyb.svod.autoingest.NoLeaveGrantedException: Leave not sanctioned.
            at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:36)
            at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
        Caused by: com.bskyb.svod.autoingest.TeamLeadUpsetException: Team lead is not in good mood
            at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:44)
            at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
            ... 1 more
        Caused by: com.bskyb.svod.autoingest.ManagerUpsetException: Manager is in bad mood
            at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:52)
            at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:42)
            ... 2 more
        Caused by: com.bskyb.svod.autoingest.GirlFriendOfManagerUpsetException: Girl friend of manager is in bad mood
            at com.bskyb.svod.autoingest.MainClass.howIsGirlFriendOfManager(MainClass.java:58)
            at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:50)
            ... 3 more
    

    【讨论】:

    • 喜欢您的示例演示链接的方式。笑得很开心
    【解决方案3】:

    我认为该示例的原因是作者希望创建一个不依赖于某些特定底层系统(如 SQL)的统一接口。因此,他将异常转换为更通用的形式,使实际实现对业务逻辑透明。

    但是,在某些情况下,可能需要传播 SQLException 之类的错误。这在需要根据抛出的异常采取不同操作的地方很有用。

    【讨论】:

      【解决方案4】:

      loadResource 的调用者不需要知道这些资源是如何加载的确切细节,或者至少不需要关心它失败的细节。 (请记住,loadResources 可能不是您写的,也可能是其他人需要使用 loadResources 方法)。

      调用 loadResource 时您应该关心的是它可能会抛出 ResourceLoadException。并不是因为 SQLException 而导致实现细节失败 - 这也可能随着时间而改变,稍后有人可能会决定从其他可能失败的地方加载资源。

      你只需要加载一些资源,如果它失败了你需要处理,而不是处理潜在的 MainframeHasCrashedException、FileNotFoundException 和其他十几个加载这些资源可能失败的原因。

      现在,当某些事情确实失败时,拥有导致失败的原始异常(例如 SQLException)会很方便 - 因此通过日志文件或类似文件搜索的人可以通过检查堆栈跟踪找出错误的原因

      如果 loadResources 也可以抛出其他异常,例如,您也不应该试图在这里捕获异常。 UnautorizedException,您可能不想在调用 loadResources 时处理它 - 您可能希望将该异常传播给可以处理 UnautorizedException 的其他调用者(并可能提示用户输入一些凭据)。 catch(Exception e) 会吞下你真的无法处理的 UnautorizedException。

      【讨论】:

      • 谢谢您的解释!
      【解决方案5】:

      优点是调用者只需处理ResourceLoadException 而不是SQLException。这样,如果您稍后将数据存储更改为文件,访问它可能会导致IOException。您不必返回并更改调用者处理的异常类型。这对您的调用者很有用,因为调用者将以相同的方式处理任一异常。

      【讨论】:

        【解决方案6】:

        第一个好处是封装。 ResourceLoader 可能是具有多个实现的接口(例如,一个从数据库加载资源,而另一个从文件系统加载它们),其中要使用的实现是在运行时选择的。然后调用者应该不知道加载资源失败的根本原因,但可能仍希望对资源加载失败做出反应。如果接口声明要抛出两个不同的异常,调用者可能希望以不同的方式响应(例如,重试可能成功的 TransientResourceLoadFailureException 和已知重试不成功的 PermanentResourceLoadFailureException),这将特别有用。

        异常链的第二个好处是链中的每个异常都可能有不同的消息,这允许包含用于调试的附加信息。在您的情况下,请注意 ResourceLoadException 的消息包含无法加载的资源的名称,不保证包含在 SQLException 中,但可能需要重现问题(某些数据库不完全了解特定错误消息)。

        这些好处是以必须编写和维护 catch 块为代价的。您应该根据具体情况决定收益是否与成本相符。

        【讨论】:

        • 有道理。谢谢你的解释!!
        【解决方案7】:

        链式异常用于调试目的。当您遇到一般异常时,您可以检查是否存在链式低级异常,并尝试了解发生该低级异常的原因。

        【讨论】:

          猜你喜欢
          • 2012-04-23
          • 2015-01-27
          • 2010-12-16
          • 1970-01-01
          • 2015-01-20
          • 1970-01-01
          • 2016-06-12
          • 2018-01-11
          • 1970-01-01
          相关资源
          最近更新 更多