重新抛出exception:为什么方法在没有throws子句的情况下编译?

在下面的源代码中,我正在重新抛出Exception
为什么没有必要将throws关键字放在方法的签名上?

 public void throwsOrNotThrowsThatsTheQuestion() { try { // Any processing } catch (Exception e) { throw e; } } 

此行为似乎仅在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类型,其中1.6只看到了throw e; Exception类型,并为此提供了一个错误。

将其更改为抛出RuntimeException使其按预期编译,因为一如既往,未经检查的Exception不需要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引入了更具包容性的类型检查 。 引述…

请考虑以下示例:

 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子句中指定这些exception类型。 在Java SE 7之前的版本中,您不能这样做。 因为catch子句的exception参数e是类型Exception,并且catch块重新抛出exception参数e,所以只能在rethrowException方法声明的throws子句中指定exception类型Exception。

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

(强调我的)

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

java.lang.Exception是一个已检查的exception,因此无法工作甚至编译。 它可以使用unckeched(java.lang.RuntimeException)。 无论是否在catch块中抛出exception,绝对没有区别。

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

java:unreported exception java.lang.Exception; 必须被抓住或宣布被抛出

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

如果您检查了已检查的exception,则需要将其放在throws列表中

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

如果你抛出一个未经检查的exception,你不需要将它放在throws列表中,你可以使用它来在未经检查的exception中包装一个已检查的Exception,以避免破坏使用此方法的代码,如果你更改了这样的方法改变之后它可能产生一个检查exception的方式。 但是你必须要小心,检查Exception是否有待处理!

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

在此处阅读有关例外的更多信息。

为什么没有必要将throws关键字放在方法的签名上?

你可以把它放在你的// Any processing都没有抛出任何检查exception

例:

这编译好了。

 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开始工作的。在以前的版本中引发了exception。 更多信息在java 7中重新抛出

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

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

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

希望这能清除你的怀疑。

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

可能最常见的情况是新的RuntimeException(throwable); 避免完全抛出投掷条款。