问题描述
我了解捕获从未在相应 try 块中抛出的已检查异常是无效的。因为如果异常发生,编译器本身会强迫程序员处理异常。
例如,这个代码片段 -
try
{
}
catch(IOException e)
{
}
无效。
但是为什么对于抛出从未在方法体中抛出过的已检查异常的方法,编译器的工作方式不一样?
例如,这个代码片段 -
void test() throws IOException
{
}
出人意料地有效。
请解释其背后的原因。 TIA。
解决方法
因为您可能希望允许子类抛出此异常。
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
)方法永远不会抛出异常但可以声明它。
It is a compile-time error 如果 catch 子句可以 catch 检查 异常类 E1 并且 try 块不是这种情况 对应catch子句可以抛出checked异常类 是 E1 的子类或超类,除非 E1 是 Exception 或 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 子类的不可能路径有关。
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',编译器就很好,他知道当你的方法被调用时要做什么。
如果您声明了一个实际上并不存在的异常,您就是让代码的用户意识到,在未来的版本中,您可能会添加该异常。用户将为您添加该例外的那一天做好准备。
,指定表示该方法可以抛出异常或不能抛出。但是编译器不检查它,因为他实际上不能。所以try块只检查指定。这 重点是编译器看不到实际调用异常和方法指定之间的区别。如果您在 try 块中调用 test() ,它将有效。对不起我的英语)