链式异常的优点是什么?

时间:2011-02-16 19:03:49

标签: java exception-handling

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

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

7 个答案:

答案 0 :(得分:15)

  

有人可以提供有关链式例外需求的信息吗?

文章说得很好:

  

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

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

因此,链接例外是一个很好的选择。你抛出一个“高级”异常,非常适合特定方法,但是将其链接到导致它的异常。

  

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

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

答案 1 :(得分:15)

为什么链异常?

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

以下列示例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

答案 2 :(得分:5)

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

答案 3 :(得分:5)

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

调用loadResource时你应该关心的是它可能抛出ResourceLoadException。并非由于SQLException而导致实现细节失败 - 这可能会随着时间的推移而改变,稍后某人可能会决定从其他可能失败的地方加载资源。

您只需要加载一些资源,如果失败则需要处理,并且不处理潜在的MainframeHasCrashedException,FileNotFoundException以及加载这些资源的其他十几个原因可能会失败。

现在,当某些内容失败时,使用导致失败的原始异常(例如SQLException)非常方便 - 因此搜索日志文件或类似内容的人可以通过检查stacktrace找出错误的原因

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

答案 4 :(得分:2)

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

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

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

答案 5 :(得分:1)

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

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

答案 6 :(得分:0)

链式异常是出于调试目的。当您获得一般性异常时,可以检查是否存在链式下层异常,并尝试了解为什么发生该下层异常。