【问题标题】:Rethrowing an Exception: Why does the method compile without a throws clause?重新抛出异常:为什么方法在没有 throws 子句的情况下编译?
【发布时间】:2013-11-23 16:42:41
【问题描述】:

在下面的源代码中,我将重新抛出 Exception
为什么不需要在方法的签名上加上throws关键字?

public void throwsOrNotThrowsThatsTheQuestion() {
    try {

        // Any processing

    } catch (Exception e) {
        throw e;
    }
}

【问题讨论】:

  • 您确定可以为已检查的异常执行此操作吗??
  • 这个源可能不会编译
  • Eclipse 中似乎有一个错误(Juno Service Release 1 Build id: 20121004-1855),因为它在您的示例中没有显示任何错误。 IntelliJ 显示预期行为(未处理的异常错误)。也许这就是你所看到的
  • 一篇关于已检查与未检查异常的好帖子:stackoverflow.com/questions/6115896/…
  • 你甚至可以捕捉并重新抛出Throwable。有趣的是,java7 还没有被广泛采用,它的特性也不是很为人所知。但人们可能会很快跳上 java8。

标签: java


【解决方案1】:

这种行为似乎只发生在 Java 1.7 上。使用 1.6 编译时,我收到以下编译器错误消息:

c:\dev\src\misc>javac -source 1.6 Main.java
warning: [options] bootstrap class path not set in conjunction with -source 1.6
Main.java:22: error: unreported exception Exception; must be caught or declared
to be thrown
        throw e;
        ^
1 error
1 warning

但是对于 Java 1.7,它可以编译。

c:\dev\src\misc>javac -source 1.7 Main.java

c:\dev\src\misc>

...直到我在try 块中实际抛出Exception

public static void throwsOrNotThrowsThatsTheQuestion() {
try {

    // Any processing
    throw new IOException("Fake!");

} catch (Exception e) {
    throw e;
}

正在编译...

c:\dev\src\misc>javac -source 1.7 Main.java
Main.java:22: error: unreported exception IOException; must be caught or declare
d to be thrown
        throw e;
        ^
1 error

看起来 Java 1.7 变得足够聪明,可以通过分析 try 块代码来检测可能抛出的 Exception(s) 类型,而 1.6 刚刚看到 Exception 类型的 throw e; 并给出一个错误。

将其更改为抛出 RuntimeException 使其按预期编译,因为与往常一样,未选中的 Exceptions 不需要 throws 子句:

public static void throwsOrNotThrowsThatsTheQuestion() {
try {

    // Any processing
    throw new RuntimeException("Fake!");

} catch (Exception e) {
    throw e;
}

正在编译...

c:\dev\src\misc>javac -source 1.7 Main.java

c:\dev\src\misc>

解释

这是怎么回事:

Java 7 引入了more inclusive type checking。引用...

考虑以下示例:

static class FirstException extends Exception { }
static class SecondException extends Exception { }

public void rethrowException(String exceptionName) throws Exception {
  try {
    if (exceptionName.equals("First")) {
      throw new FirstException();
    } else {
      throw new SecondException();
    }
  } catch (Exception e) {
    throw e;
  }
}

此示例的 try 块可能会抛出 FirstException 或 SecondException。假设您要在 rethrowException 方法声明的 throws 子句中指定这些异常类型。在 Java SE 7 之前的版本中,您不能这样做。因为catch子句的异常参数e是Exception类型,而catch块重新抛出异常参数e,所以只能在rethrowException方法声明的throws子句中指定异常类型Exception。

但是,在 Java SE 7 中,您可以在 rethrowException 方法声明的 throws 子句中指定异常类型 FirstException 和 SecondException。 Java SE 7 编译器可以确定语句 throw e 抛出的异常一定来自 try 块,而 try 块抛出的异常只能是 FirstException 和 SecondException。即使 catch 子句的异常参数 e 是 Exception 类型,编译器也可以确定它是 FirstException 或 SecondException 的实例:

(强调我的)

public void rethrowException(String exceptionName)
throws FirstException, SecondException {
  try {
    // ...
  }
  catch (Exception e) {
    throw e;
  }
}

【讨论】:

    【解决方案2】:

    java.lang.Exception 是一个经过检查的异常,所以它不会工作甚至编译。它可以与 unkeched (java.lang.RuntimeException) 一起使用。 是否在 catch 块中抛出异常绝对没有区别。

    编译器错误看起来像这样(取决于编译器):

    java: 未报告的异常 java.lang.Exception;必须被抓住或宣布被扔掉

    编辑:如果你从未真正抛出异常,Java 7 可以处理这种情况

    【讨论】:

    • 我尝试使用 java 7 并实际编译。
    【解决方案3】:

    如果你抛出一个检查异常,你需要把它放在抛出列表中

    public void retrhowChecked() throws Exception {
        try {
            throw new IOException();
        } catch(Exception e) {
            throw e;
        }
    }
    

    如果你抛出一个未经检查的异常,你不需要将它放在抛出列表中,你可以使用它来将一个检查的异常打包到一个未经检查的异常中,以避免在你更改有问题的方法时破坏使用此方法的代码以这样一种方式,它在更改之后可能会产生一个检查异常。但你必须小心,检查异常是否有待处理!

    public void retrhowUnchecked() {
        try {
            throw new IOException();
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }
    

    阅读有关异常的更多信息here

    【讨论】:

      【解决方案4】:

      为什么不需要将 throws 关键字放在方法的 签名?

      你可以这样说,因为你的// Any processing 没有抛出任何检查异常

      例子:

      这个编译没问题。

      public void throwsOrNotThrowsThatsTheQuestion() {
          try {
      
              throw new RuntimeException();
      
          } catch (Exception e) {
              throw e;
          }
      

      这不会编译,你需要添加throws子句。

      public void throwsOrNotThrowsThatsTheQuestion() {
          try {
              throw new Exception();
          } catch (Exception e) {
              //do something like log and rethrow
              throw e;
          }
      }
      

      这是从 java 7 开始工作的。 在以前的版本中会引发异常。更多信息rethrow in java 7

      【讨论】:

      • 是的,使用 Java 7,我在 netbeans 中进行了尝试,当我在 3 个月前发现这个时我很惊讶,我不知道在以前的版本中是否可以使用,我和你的想法一样@hgoebl
      • 远距离堆栈的最佳答案
      • 好的,我相信你。 Java 变得越来越智能!
      • @nachokk 相同的代码抛出异常 JAVA 6 :-).,看起来 JAVA 7 中发生了一些变化。非常令人惊讶。 mindview.net/Etc/Discussions/CheckedExceptions
      • @SathishJayapal 是的,这个功能是从 java7 开始的
      【解决方案5】:

      当你对方法使用 throws 时,这意味着将调用该方法的语句必须用 try catch 块包围。

      但如果该方法已经包含 try catch 块,则不需要 thorws 声明,因为该方法抛出的异常仅在此处处理。

      调用该方法的语句不需要用try catch块包围。

      希望这能消除您的疑虑。

      【讨论】:

      • 异常不是“只在那里处理”,因为它重新抛出它。
      • 我认为你误读了我所说的@ajb。请再看一遍,理解我写的每一个字。然后做出决定。
      • 好的,我做到了。我的决定是您写的内容很模糊-当您使用“方法”一词时,我不知道您是指帖子中的方法,该方法调用的方法还是调用该方法的方法。无论如何,我们已经确定 throwsOrNotThrowsThatsTheQuestion 可能需要也可能不需要 throws 声明,具体取决于“任何处理”代码中的内容。
      • 按照您的说法,您似乎明白我的意思以及帖子中包含的内容。我只需要同样的。谢谢:)
      【解决方案6】:

      throw new Exception(); 是你永远不应该在 catch 中做的事情 块,但您可能不得不或想要做throw new SomeException(throwable);(保留完整的堆栈跟踪) 的 throw throwable; 以符合您方法的 API, 例如当它声明抛出 SomeException 但你正在调用代码时 这可能会引发您不想添加到您的IOException 方法的 throws 子句。

      可能最常见的情况是 new RuntimeException(throwable);到 完全避免使用 throws 子句。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多