为什么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
块中catch
exception。
这是两个不同的东西,一个是投掷 ,另一个是处理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表。
- javax.persistence.NoResultException:getSingleResult()没有检索任何实体
- SQLException:ResultSet关闭后不允许操作
- Android java.net.SocketTimeoutException:连接超时
- 无法在IntelliJ Idea中启动spring-boot应用程序
- Java Thread:Run方法不能抛出已检查的exception
- Java SDK for REST API服务的error handling
- 从日志文件中提取java堆栈跟踪的工具
- 计算字符串中出现的字符数
- Servlet:SEVERE:为servlet分配exception