使用永远不会在方法主体中抛出的 throws 子句声明已检查的异常

Declaring checked exception using throws clause that is never thrown in the body of the method

我了解到在相应的try块中捕获从未抛出的已检查异常是无效的。因为如果发生异常,编译器本身会强制程序员处理异常。

例如这个代码片段-

try
{
}
catch(IOException e)
{
}

无效

但是为什么编译器对抛出方法主体中从未抛出的已检查异常的方法不以相同的方式工作?

例如这个代码片段-

void test() throws IOException
{
}

出人意料地有效

请说明原因。 TIA.

指定表示该方法可以抛出异常或不能抛出异常。但是编译器不检查它,因为实际上他不能。所以尝试块只检查指定。这 重点是编译器看不到实际调用异常和方法指定之间的差异。如果您在 try 块中调用 test(),它将有效。对不起我的英语)

It is a compile-time error if a catch clause can catch checked exception class E1 and it is not the case that the try block corresponding to the catch clause can throw a checked exception class that is a subclass or superclass of E1, unless E1 is Exception or a superclass of Exception.

这告诉你所有这些都是有效的:

try { }
catch(Exception e){}

--

try{ }
catch(NullPointerException  e) {}

--

try{ }
catch(ArrayIndexOutOfBoundsException  e) {}

--

try{ }
catch(RuntimeException e) {}

--

try{ }
catch(Error e) {}

--

try{ }
catch(Throwable e){ }

它与空块无关。它与您检查的异常子类的不可能路径有关。


Exception 的子类会怎样?例如IOexception:那些是无法访问的catch块。 try 块中的任何内容都不会导致该异常,因此编译器只是告诉您:该块永远不会执行,因为它永远不会捕获该子异常。

throws的区别:在此上下文中不存在不可访问代码的概念。方法抛出异常的可能性并不会在方法的定义中结束。例如:

abstract void readFile(String path) throws IOException;

这个方法甚至没有块,因为它是一个抽象方法。很容易猜到这一行永远不会抛出任何 IOException。但它为将实现它的扩展定义了一种行为。为了覆盖它,您的方法必须抛出 IOException.

同理,如果有人覆盖了你的测试方法:

@Override
void test() throws IOException
{
   readFile(file);
}

与您的第一个 try-catch 块相反,这并非不可能发生。

Java 遵循“处理或声明”规则。

如果您的代码中声明了检查异常,您必须处理它(在 try/catch 块中)或声明它(在方法签名上添加 'throws')。

当您说您的方法可能会抛出异常时,使用您的方法的每个人都必须处理或声明该异常。如果你处理它,你正在使你的代码能够恢复。如果声明它,则将问题传递给调用者。

如果你声明 'throws',编译器就没问题,他知道当你的方法被调用时要做什么。

如果您声明了一个实际上不存在的异常,您就是在让代码的用户知道,在未来的版本中,您可能会添加该异常。用户将为您添加该例外的那一天做好准备。

因为你可能希望允许子类抛出这个异常。

public abstract class Parent {
  public void doStuff() {}
}
public class Child {
  // this is illegal, because you throw more exceptions than the overridden method
  public void doStuff() throws IOException {}
}

这对于接口来说更直观,接口的(非default)方法永远不会抛出异常但可以声明它。