我想知道为什么Java编译器在方法永远不会抛出Exception时允许方法声明中的抛出。因为“引发”是一种处理异常的方法(告诉调用方对其进行处理)。
因为有两种处理异常的方法(引发和try / catch)。在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
}
答案 0 :(得分:25)
throws
子句是方法合同的一部分。它要求方法的调用者的行为就像方法可能抛出指定的异常一样(即捕获异常或声明自己的throws
子句)。
方法的初始版本可能不会抛出throws
子句中指定的异常,但是将来的版本可能会抛出该异常而不破坏API(即,任何调用该方法的现有代码仍会通过编译)。
相反也可能。如果用于抛出throws
子句中指定的异常的方法,但以后的版本不再抛出该异常,则应保留throws
子句,以免破坏使用该子句的现有代码。您的方法。
第一个示例:
假设您有使用methodB
的代码:
private static void methodA() {
methodB(); // doesn't have throws IOException clause yet
}
如果以后您想更改methodB
以抛出IOException
,则methodA
将停止传递编译。
第二个示例:
假设您有使用methodB
的代码:
private static void methodA() {
try {
methodB(); // throws IOException
}
catch (IOException ex) {
}
}
如果您从throws
的未来版本中删除methodB
子句,则methodA
将不再通过编译。
当methodA
为private
时,此示例不是很有趣,因为它只能在本地使用(在同一类中,很容易修改所有调用它的方法)。
但是,如果它变成public
,则您不知道谁使用(或将使用)您的方法,因此您无法控制所有可能由于添加或删除{ {1}}子句。
如果它是一个实例方法,那么即使您不抛出异常,还有一个允许throws
子句的原因-该方法可以被覆盖,并且即使基数为基,该覆盖方法也可能会抛出异常类实现没有。
答案 1 :(得分:8)
因为签名定义了方法的约定。即使该方法现在不抛出IOException,也许将来也会发生,并且您想为此做准备。
假设您现在只为该方法提供一个虚拟实现,但是您知道以后,实际实现可能会引发IOException。 如果编译器阻止您添加此throws子句,则在您提供该方法的实际实现后,将被迫(递归地)重新处理对该方法的所有调用。
答案 2 :(得分:0)
关键字引发告诉程序员方法中可能发生IOException。现在,如果您未指定try / catch,则意味着抛出异常时程序将停止工作,而在try / catch中,如果抛出异常,则通过执行其他操作来处理它。
使用throw来提高可读性并指定异常的可能性,并使用try / catch告诉程序在发生异常的情况下该怎么做。
答案 3 :(得分:-1)
方法B引发IOException,因此调用方法B的方法负责捕获将由方法B引发的异常。尝试从其他方法调用methodB,它将要求您捕获它或重新抛出IOException。您将必须在某个地方捕获链中的IOException(在try / catch块中)。 因此您不会遇到编译时错误。
private void sampleMethod(){ 尝试{ methodB(); } catch(IOException e){ // TODO自动生成的catch块 e.printStackTrace(); } }
try / catch无疑吞下了异常,这意味着methodA负责在try / catch块中捕获异常。 “ 任何Java程序中的每个语句都必须可访问,即每个语句必须至少可执行一次” 因此,由于try块没有任何导致IOException的代码,您将得到编译器错误。