我的问题更多的是关于设计,java编译器强制我们捕获一个已检查的异常(例如FileNotFoundException)但不强制我们捕获未经检查的异常(例如NullPointerException).我想了解原因,为什么编译器是这样设计的?
解释未经检查的例外 - 争议表明它是为提高可读性而完成的.
不捕获运行时异常不是一个很大的代价吗?
编辑:按成本我的意思是,在实时环境中而不是在编译时本身获取运行时异常.如果在编译期间处理此类异常,则不存在错误泄漏的可能性.修复错误的成本随着检测到的相位延迟而增加.
Jon*_*onK 11
对此的理由实际上包含在第11.2节中的Java语言规范中.这是相关的exerpt:
11.2.编译时检查异常
未经检查的异常类(第11.1.1节)免于编译时检查.
在未经检查的异常类中,错误类是免除的,因为它们可以在程序中的许多点发生,并且很难或不可能从它们中恢复.宣布此类例外的程序将毫无意义地混乱.复杂的程序可能希望捕获并试图从某些条件中恢复.
在未经检查的异常类中,运行时异常类是免除的,因为在Java编程语言的设计者的判断中,必须声明这样的异常不会有助于建立程序的正确性.Java编程语言的许多操作和构造都可能在运行时导致异常.Java编译器可用的信息以及编译器执行的分析级别通常不足以确定不会发生此类运行时异常,即使这对程序员来说可能是显而易见的.要求声明这样的异常类只会让程序员感到恼火.
例如,某些代码可能实现一个循环数据结构,通过构造,它永远不会涉及空引用; 程序员可以确定不会发生NullPointerException,但Java编译器很难证明它.建立数据结构的这种全局属性所需的定理证明技术超出了本规范的范围.
作为开发人员,我们至少在某种程度上可以控制是否可以通过我们编写的代码抛出未经检查的异常.例如,我们可以避免在尝试操作之前NullPointerException检查是否null会导致异常,或者我们可以编写代码使得该变量永远不会出现 null在第一位.同样,我们可以避免NumberFormatException通过实施健全性检查来确保我们的输入肯定是一个数字,然后我们尝试将其解析为一个等等...
如果您已经明智地编写了代码,那么您应该很少(如果有的话)遇到一个RuntimeException(或者它的子类),因此要求您try-catch在每个可能抛出一个代码的代码周围放置块会导致数百个小的可怕的混乱try-catch甚至简单类中的块,或者一个巨大的catch-everything块,这两者都不是特别理想的,并且在代码中添加了大量不必要的膨胀.
被迫捕获所有未经检查的异常将使"简单"操作System.out.println()变得更加冗长.
让我们看看如果我们被迫捕获所有未经检查的异常,我们必须编写什么来打印空白行到控制台(注意:这是最糟糕的情况,其中异常只是传播而不是在API中处理) :
System.out.println();
// ^ Theoretically this could throw a NullPointerException
Run Code Online (Sandbox Code Playgroud)
所以,我们必须考虑到这一点:
try {
System.out.println();
} catch (NullPointerException e) {
// In practice this won't happen, but we're forced to deal with
// it all the same...
}
Run Code Online (Sandbox Code Playgroud)
我们还没有完成.我们需要看看是什么out,以及如何println()确定我们是否需要处理其他任何事情.out实际上是一个PrintStream对象,那么我们可以告诉它的println()方法呢?
这是println():
public void println() {
newLine();
}
Run Code Online (Sandbox Code Playgroud)
这意味着我们现在需要看看有什么newLine()...
private void newLine() {
try {
synchronized (this) {
ensureOpen();
textOut.newLine();
textOut.flushBuffer();
charOut.flushBuffer();
if (autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
Run Code Online (Sandbox Code Playgroud)
NullPointerException这里有更多的s 来源,但我们已经抓住了.interrupt()可以(最终)抛出一个SecurityException,也可以造成一个InterruptedException,所以我们也必须处理这两个.
try {
System.out.println();
} catch (NullPointerException e) {
} catch (SecurityException e) {
} catch (InterruptedException e) {
}
Run Code Online (Sandbox Code Playgroud)
textOut.newLine()最终结束Writer#write(String, int, int),处理a char[],所以我们立即有一个来源ArrayIndexOutOfBoundsException.它也会调用String#getChars(int, int, char[], int),它本身可以抛出一个StringIndexOutOfBoundsException.还得处理......
try {
System.out.println();
} catch (NullPointerException e) {
} catch (SecurityException e) {
} catch (InterruptedException e) {
} catch (ArrayIndexOutOfBoundsException e) {
} catch (StringIndexOutOfBoundsException e) {
}
Run Code Online (Sandbox Code Playgroud)
它也叫BufferedWriter#write(char[], int, int),可以扔IndexOutOfBoundsException...
try {
System.out.println();
} catch (NullPointerException e) {
} catch (SecurityException e) {
} catch (InterruptedException e) {
} catch (ArrayIndexOutOfBoundsException e) {
} catch (StringIndexOutOfBoundsException e) {
} catch (IndexOutOfBoundsException e) {
}
Run Code Online (Sandbox Code Playgroud)
我们现在是来自这个方法调用的六个独立的运行时异常,并且这不包括可能从native一路上的各种方法调用(其中有几个)弹出的所有潜在异常,并且不包括包括任何子类Error.
如果我们真的被迫捕获所有异常,那么Java会更糟糕.