Java 当方法永远不会抛出异常时,为什么编译器允许抛出

Java 当方法永远不会抛出异常时,为什么编译器允许抛出,java,checked-exceptions,Java,Checked Exceptions,我想知道为什么java编译器允许抛出方法声明,而方法永远不会抛出异常。因为“throws”是处理异常的一种方式(告诉调用方处理它) 因为有两种处理异常的方法(抛出和尝试/捕获)。在try/catch中,它不允许捕获未在try块中抛出的异常,但它允许在不可能抛出异常的方法中抛出异常 private static void methodA() { try { // Do something // No IO operation here } catch

我想知道为什么java编译器允许抛出方法声明,而方法永远不会抛出异常。因为“throws”是处理异常的一种方式(告诉调用方处理它)

因为有两种处理异常的方法(抛出和尝试/捕获)。在try/catch中,它不允许捕获未在try块中抛出的异常,但它允许在不可能抛出异常的方法中抛出异常

private static void methodA() {
    try {
        // Do something
        // No IO operation here
    } catch (IOException ex) {  //This line does not compile because
                              //exception is never thrown from try
        // Handle   
    }
}

private static void methodB() throws IOException { //Why does this //compile when excetion is never thrown in function body
    //Do Something 
    //No IO operation
}

抛出
子句是方法契约的一部分。它要求方法的调用方的行为就像指定的异常可能被方法抛出一样(即捕获异常或声明自己的
throws
子句)

方法的初始版本可能不会引发
throws
子句中指定的异常,但未来版本可以在不破坏API的情况下引发异常(即,调用该方法的任何现有代码仍将通过编译)

反之亦然。如果用于抛出
throws
子句中指定的异常的方法不再抛出该异常,但该方法的未来版本不再抛出该异常,则应保留
throws
子句,以免破坏使用该方法的现有代码

第一个例子:

假设您有使用
方法b
的代码:

private static void methodA() {
    methodB(); // doesn't have throws IOException clause yet
}
private static void methodA() {
    try {
        methodB(); // throws IOException
    }
    catch (IOException ex) {

    }
}
如果以后要将
methodB
更改为抛出
IOException
methodA
将停止传递编译

第二个例子:

假设您有使用
方法b
的代码:

private static void methodA() {
    methodB(); // doesn't have throws IOException clause yet
}
private static void methodA() {
    try {
        methodB(); // throws IOException
    }
    catch (IOException ex) {

    }
}
如果从未来版本的
methodB
中删除
throws
子句,
methodA
将不再通过编译

methodA
private
时,这个例子就不太有趣了,因为它只能在本地使用(在同一个类中,很容易修改调用它的所有方法)

但是,如果它成为
公共
,您不知道谁使用(或将使用)您的方法,因此您无法控制所有可能因添加或删除
抛出
子句而中断的代码


如果它是一个实例方法,那么即使您没有抛出异常,也允许使用
throws
子句还有另一个原因-该方法可以被重写,并且重写方法可以抛出异常,即使基类实现没有抛出异常。

,因为签名定义了该方法的契约。即使该方法现在不抛出IOException,将来也可能抛出,您需要为这种可能性做好准备

假设您现在只为该方法提供了一个虚拟实现,但您知道,稍后,实际实现可能会抛出IOException。
如果编译器阻止您添加这个throws子句,那么一旦您提供了该方法的实际实现,您将被迫(递归地)重做对该方法的所有调用

关键字抛出告诉程序员方法中可能发生IOException。现在,若并没有指定try/catch,这意味着当抛出异常时,程序将停止工作,而在try/catch中,若抛出异常,则通过执行其他操作来处理它

private static void methodA() {
    try {
        // Do something
        // No IO operation here
    } catch (IOException ex) {  //This line does not compile because
                              //exception is never thrown from try
        // Handle   
    }
}

private static void methodB() throws IOException { //Why does this //compile when excetion is never thrown in function body
    //Do Something 
    //No IO operation
}
为了可读性和指定异常的可能性,使用throws,并使用try/catch告诉程序在发生异常时应该做什么

private static void methodA() {
    try {
        // Do something
        // No IO operation here
    } catch (IOException ex) {  //This line does not compile because
                              //exception is never thrown from try
        // Handle   
    }
}

private static void methodB() throws IOException { //Why does this //compile when excetion is never thrown in function body
    //Do Something 
    //No IO operation
}
  • methodB抛出IOException,因此调用methodB的方法负责捕获methodB将抛出的异常。尝试从其他方法调用methodB,它将要求您捕获它或重新抛出IOException。在某个地方,您必须捕获链中的IOException(在try/catch块中)这样就不会出现编译时错误

    私有void sampleMethod(){ 试一试{ 方法b(); }捕获(IOE异常){ //TODO自动生成的捕捉块 e、 printStackTrace(); } }

  • methodA中的try/catch承认吞并了异常,这意味着methodA负责捕获try/catch块中的异常。 “任何java程序中的每条语句都必须是可访问的,即每条语句必须至少可执行一次” 所以,您将得到编译器错误,因为您的try块没有任何代码导致IOException


  • 一个私有方法是否可以说有一个契约是有争议的。它为类中的所有其他方法定义了一个契约。反过来,这些方法可能想要声明IOException,因为它们依赖于它们调用的私有方法的约定。您还可以决定更改私有方法的返回值。这也是“合同”的一部分,不是吗?然而,当这种情况发生时,您要做的是修复调用它的代码。“throws”也可以这样做。我不明白这与问题有什么关系,问题是:为什么编译器允许对方法未抛出的异常使用throws子句。你的意思是什么?编译器还允许您使用
    long
    作为返回类型,即使您只返回适合整数的常量值。为什么?出于同样的原因:你定义了契约,即该方法现在或以后可能会返回一个长值。TLDR:这是一个设计选择,这里没有对错。因为我们不是语言的设计者,“为什么”问题除了推测之外很难回答,基于意见的答案。我还发现
    这个词会引起误解。我现在把它解析为
    可以在我的脑海中抛出
    ,它似乎更适合它的行为。关于覆盖的最后一点应该给予更多的强调,IMHO。与“即使父函数没有理由抛出特定的异常,即使今天的子函数版本也可能会出现异常”的概念相比,“如果函数发生变化会怎样”的概念更不具体