为什么Java编译器允许在throws部分中列出exception,该方法无法抛出exception

如果有一些代码显然无法抛出exception,那么Java编译器似乎不一致,并且您编写了声明代码可以抛出该exception的周围代码。

请考虑这些代码段。

片段1

从未抛出的exceptioncatch

 public void g(){ try { } catch (FileNotFoundException e) {//any checked exception } } 

消息是编译错误

 Unreachable catch block for FileNotFoundException. This exception is never thrown from the try statement body 

Snippet2

throws声明,指示从不抛出的exception。

 public void g() throws FileNotFoundException{ } 

它汇编很好。

因此,第一个代码段的结果显示编译器可以计算方法是否可以抛出throws列表中列出的throws 。 因此,似乎编译器故意不报告第二个片段的错误。 但为什么? 为什么编译器允许您在throws部分编写exception,即使编译器知道这些exception不能被抛出?

编译器允许这样做,因为方法的throws子句是方法签名的一部分,而不是其实现的一部分。 实施可能会在某些时候发生变化,同时保持签名相同 。 旧的实现可能抛出了已检查的exception,但新的exception可能没有。 或者签名的设计者可能希望赋予实现者在不总是必要时抛出已检查exception的灵活性。

试一试!!

对于第二种情况,将来某些其他类可能会覆盖此方法,并且会在可能引发此exception的内部编写代码。

既然你想要其他观点 –

考虑两个不同子类中相同签名的两个实现。

例如,(一个组成的例子),

 public class StudentLoader { public abstract Student readStudentData() throws SQLException, IOException; public static void main(String args[]) { StudentLoader loader = getStudentLoader (); //may return any subclass instance try { Student s = loader.readStudentData(); } catch(IOException e) { //do something } catch(SQLException e) { //do something } } } public class StudentFileReader extends StudentLoader { public Student readStudentData() throws IOException { //read from a file } } public class StudentDBReader extends StudentLoader { public Student readStudentData() throws SQLException { //read from DB } } 

为什么编译器允许在throws部分写入exception,即使它不能被抛出?

即使子类实现StudentLoader没有抛出IOException ,父类StudentLoader仍然必须throws IOException因为StudentLoader其他实现可能会抛出它。 因此,即使方法不能抛出exception,您也可以使用StudentLoader引用(指向两个子类实例中的任何一个)向调用者指示调用者必须处理这些exception。

在显示方法g() 代码段1中 ,没有inheritance范围。 代码就在try块中。 如果try中的任何语句抛出一个已检查的Exception,则必须处理它。 如果是throws子句,则必须允许inheritance范围。 编译器无法决定在运行时调用哪个版本的readStudentData( )

我希望编译器在静态方法的情况下应该给出错误,如果不抛出throws子句中提到的Exception,因为静态方法不参与inheritance。 我不确定为什么静态方法中的throws子句可以包含从不在实现中抛出的exception。 它无论如何都不会被覆盖,为什么不在这里抛出错误? 我可能会遗漏一些东西。

 public class UnreachableCatchBlock { public static void main(String[] args) { UnreachableCatchBlock ucb = new UnreachableCatchBlock(); System.out.println(ucb.getClass().getName() + " started."); // ucb.method1(); ucb.method2(); } private void method1() { try { System.out.println(getClass().getName() + ".method1() started."); } catch (FileNotFoundException e) {//any checked exception } } private void method2() { System.out.println(getClass().getName() + ".method2() started."); } } 

上面的代码编译和运行,如@Raedwald回答中所述。

我不建议将其作为答案,只是发布我试图在评论中包含的代码的地方。

Throws不处理exception,它表示从调用方法向上抛出exception。 换句话说,它只会将exception传递给调用者。

虽然try...catch块处理exception,这就是为什么Java编译器会检查是否有任何exception来处理catch块中catchexception。

这是两个不同的东西,一个是投掷 ,另一个是处理exception,编译器会使他的鼻子只在第二个上倾斜……:p

来自JavaDoc :

exception处理程序不仅可以打印错误消息或停止程序。 他们可以进行错误恢复,提示用户做出决定,或者使用链式exception将错误传播到更高级别的处理程序。

因此,通过提供try...catch实现,您要求编译器除了打印exception之外还要做更多的事情。

另一个具体原因:

 public void testException() throws FileNotFoundException { File file = new File("test.txt"); System.out.println(file.exists()); Scanner scanner = new Scanner(file); } 

如果您将通过javap -c Test.class观察上述示例的编译代码,您将发现将创建一个Exception表。

  public static void testException(); Code: 0: new #2 // class java/io/File 3: dup 4: ldc #3 // String test.txt 6: invokespecial #4 // Method java/io/File."":(Ljava/lang/String;)V 9: astore_0 10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_0 14: invokevirtual #6 // Method java/io/File.exists:()Z 17: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V 20: new #8 // class java/util/Scanner 23: dup 24: aload_0 25: invokespecial #9 // Method java/util/Scanner."":(Ljava/io/File;)V 28: astore_1 29: goto 37 32: astore_1 33: aload_1 34: invokevirtual #11 // Method java/io/FileNotFoundException.printStackTrace:()V 37: return Exception table: from to target type 20 29 32 Class java/io/FileNotFoundException 

因此,当编译器找不到任何在try块中没有抛出exception的代码时,编译时错误就会出现。

抛出时不会生成exception表。