【问题标题】:Where do Java exceptions came from?Java 异常从何而来?
【发布时间】:2017-05-16 10:26:02
【问题描述】:

到目前为止,我认为每个 Java 异常都必须由构造函数在某处创建,因为我可以自己创建自定义异常:

throw new Exception();

但现在看来我必须处理来自 JavaMail 的一些异常 - MessagingException。它来自方法Store.close(继承自Service类)。

我去了那里(我需要检查什么时候抛出这个异常,这样我才能知道哪里出了问题),我看到这个方法调用了另外两个方法——它们都没有抛出异常!

public synchronized void close() throws MessagingException {
    setConnected(false);
    notifyConnectionListeners(ConnectionEvent.CLOSED);
}

据我了解,这是已检查异常(既不是错误也不是 RuntimeException),那么close 方法命令使用的任何一个都不必声明它怎么可能?它也不是在这里创建的,在这个方法中。

【问题讨论】:

  • 该特定实现不会抛出MessagingException,但覆盖它的子类可以。
  • @Jon Skeet 可以,但不必...你不觉得现在我必须捕获/抛出不可能发生的异常有点愚蠢吗?而且,如果他们可以抛出这些异常,为什么需要在这个 Service 类中声明呢?留下它并选择性地在覆盖方法中声明不是更好吗?
  • 在什么方面是不可能的?是什么让您如此确定您将使用一个不会覆盖它的子类,这意味着异常可以被抛出?所以不,我不认为这很愚蠢。
  • @JonSkeet 我只使用一个子类 - Store。而且我不知道这个 close 方法应该如何为我抛出异常。尽管如此,我还是必须处理这个 Store 类的幻像异常。
  • 不,这些方法都不应该抛出该异常。 这个实现不应该抛出那个异常。该异常被声明为允许 子类抛出它。请注意,您也不能直接使用Store,因为它仍然是一个子类。那么,您对您正在使用Store 的哪个子类以及它不会以可能涉及MessagingException 被抛出的方式覆盖close 有多大把握?

标签: java exception jakarta-mail checked-exceptions


【解决方案1】:

声明的异常与那个实现可以抛出什么无关——它是关于那个实现或子类中的实现可以抛出什么。 Service 是一个抽象类——JavaMail 实现的两个直接子类(TransportStore)也是如此。即使那些都没有覆盖close(),你使用的具体实现仍然完全有可能覆盖close()并且它的实现可能抛出MessagingException

从 API 设计的角度来看是否有意义?我必须更仔细地研究 JavaMail 才能对此做出判断,谢天谢地,我已经很长时间没有使用 JavaMail。

从语言的角度来看是否有意义?绝对地。有一个不抛出特定检查异常但预期具体子类可能需要的实现是完全合理的。

【讨论】:

    【解决方案2】:

    关于method declaration的JLS说

    声明检查异常的要求允许 Java 编译器确保包含用于处理此类错误条件的代码。如果方法或构造函数无法处理在其主体中作为已检查异常抛出的异常条件,则如果它们的 throws 子句中缺少适当的异常类型,则通常会导致编译时错误。因此,Java 编程语言鼓励一种以这种方式记录罕见和真正异常情况的编程风格。

    基本上,如果不处理异常,您可以确定代码不会编译。所以即使它没有在这个实现中抛出,它也可能在一个子类中。

    阅读完整页面了解更多详细信息。

    public class Mother{
        public foo() throws Exception{
             system.out.println("I am mother");
        }
    }
    
    public class Daughter extends Mother{
    
        @Override
        public foo() throws Exception{
             throws new Exception("I am a teenager !!");
        }
    }
    

    因为这是允许的

    Mother m = new Daughter();
    

    你失去了实例的真实类型,所以幸运的是,如果你这样做了编译器会尖叫

    m.foo(); //will not compile
    

    请注意,如果子类中的重写方法需要 throws 某些东西,则不能在没有任何 throws 声明的情况下使用来自母亲的方法。禁止,不能在子类throws声明中添加或使用异常的超类

    【讨论】:

    • 但是这个方法在子类中甚至没有被覆盖。而且我没有创建任何子类,我只是使用其中一个。想象一下,你有 Mother 类抛出异常和 Son 类没有抛出异常......就是这种情况。我正在使用 Son,如果我理解正确,我必须以某种方式捕获不会发生的异常......因为 Son 类没有 foo 方法。而且Store是抽象类,你确定我可以这样声明吗?
    • @Line 还没有......但如果将来有人决定覆盖它,它将与现有源兼容。所以你的Son不会抛出一个方法,就像我的Daughter一样工作,因为你已经抓住了Exception。现在,您想知道这些情况何时会发生,有时您不能,请检查 javadoc,如果没有描述,请为最坏的情况做好准备;)
    • @Line 如果一个抽象方法声明它可以抛出一个检查异常,这是该方法的合同的一部分。您正在根据合同进行编码,而不是特定的实现。由于使用的实现可以在运行时确定,因此您永远无法确定实现是否可以抛出异常。因此,您必须通过声明向上委托或捕获它来相应地处理它。
    • @Axel:我现在正在使用这个类。如果将来有人改变这个类,我有什么信心相信代码的其他部分会正常工作?这种想法有些疯狂。当我有实际代码并且它似乎否认存在此类异常时,为什么我需要描述?我不明白这一点 - 未声明的方法如何抛出已检查的异常......而且它是声明异常并且不提供抛出它的方法的抽象方法。
    • @Line 正如 Jon Skeet 在他的回答中提到的那样。关闭方法通常可能需要某些资源清理,例如显式关闭连接、处理一些不是垃圾收集的底层本机接口等。事情可能会失败,因此该方法通过声明 MessagingException 来预见这一点,这是一个包罗万象的包装器任何潜在的问题。这 can 是否真的发生完全取决于实现。这是否让不得不在某个地方强制处理所有这些异常变得很痛苦?是的,它确实。检查异常当然不是没有争议的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-28
    • 1970-01-01
    • 2015-03-23
    相关资源
    最近更新 更多