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

cas*_*lin 23 java

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

public void throwsOrNotThrowsThatsTheQuestion() {
    try {

        // Any processing

    } catch (Exception e) {
        throw e;
    }
}
Run Code Online (Sandbox Code Playgroud)

rge*_*man 31

此行为似乎仅在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
Run Code Online (Sandbox Code Playgroud)

但是使用Java 1.7,它可以编译.

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

c:\dev\src\misc>
Run Code Online (Sandbox Code Playgroud)

......直到我真正抛出Exceptiontry块:

public static void throwsOrNotThrowsThatsTheQuestion() {
try {

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

} catch (Exception e) {
    throw e;
}
Run Code Online (Sandbox Code Playgroud)

编译...

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
Run Code Online (Sandbox Code Playgroud)

看起来Java 1.7已经足够聪明Exception,可以通过分析try块代码来检测可能抛出的那种类型,其中1.6只是看到throw e;了类型Exception而且只是为了给出了错误.

更改它以RuntimeException使其按预期编译,因为一如既往,未选中的Exceptions不需要throws子句:

public static void throwsOrNotThrowsThatsTheQuestion() {
try {

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

} catch (Exception e) {
    throw e;
}
Run Code Online (Sandbox Code Playgroud)

编译...

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

c:\dev\src\misc>
Run Code Online (Sandbox Code Playgroud)

说明

这是发生了什么:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

这个例子的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;
  }
}
Run Code Online (Sandbox Code Playgroud)


max*_*ann 6

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

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

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

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