我的问题更多是关于设计的,java编译器强迫我们捕获一个检查过的异常(例如FileNotFoundException),但是并不强迫我们捕获一个未经检查的异常(例如NullPointerException)。我想了解原因,为什么编译器是这样设计的?
解释未加限制的例外--争议表明,这是为了提高可读性。
不捕获运行时异常不是很大的成本吗?
编辑:我的意思是,在活动环境中获得运行时异常,而不是在编译时。如果这些异常是在编译过程中处理的,则不存在漏洞泄漏的可能性。修复错误的成本随着检测阶段的延迟而增加。
发布于 2014-05-14 10:37:55
设计是关于权衡的。设计一个处理每一种情况的系统是不可行的;我们在设计处理相当常见的情况的系统时有足够的困难。
在我看来,检查异常和未检查异常之间的权衡似乎是语言设计中的一个问题,这种权衡是编码的;API设计器、系统和应用程序都可以确定是否应该由编译器强制捕获给定的异常。
catch (Exception e) {},这样人们就可以指着他们尖叫了吗?在我看来,Java一直擅长的那种灵活性-- API设计人员可以选择。
发布于 2014-05-14 10:02:50
这一理由实际上载于Java语言规范第11.2节。以下是相关的努力:
11.2.异常的编译时检查() 未检查的异常类(§11.1.1)免于编译时检查。 在未检查的异常类中,错误类可以被豁免,因为它们可以在程序中的许多点发生,从它们恢复是困难的或不可能的。宣布这种例外的程序将是杂乱无章的,毫无意义。复杂的程序可能还希望捕捉并尝试从其中一些条件中恢复过来。 在未检查的异常类中,运行时异常类被豁免,因为根据Java编程语言的设计人员的判断,必须声明此类异常对建立程序的正确性没有明显的帮助。Java编程语言的许多操作和构造在运行时可能导致异常。Java编译器可用的信息和编译器所执行的分析级别通常不足以确定这样的运行时异常不会发生,即使这对程序员来说是显而易见的。要求声明这样的异常类对程序员来说只是个麻烦。 例如,某些代码可能实现一个循环数据结构,通过构造,它永远不会包含空引用;然后,程序员可以确定NullPointerException不会发生,但是Java编译器很难证明它。建立这样的数据结构全局属性所需的定理证明技术超出了本规范的范围。
作为开发人员,至少在某种程度上,我们可以控制我们编写的代码是否会引发未经检查的异常。例如,我们可以避免遇到NullPointerException,方法是在尝试导致异常的操作之前检查null,或者编写代码,以便该变量从一开始就不可能是null。类似地,我们可以通过实现一个健全检查来避免NumberFormatException,以确保我们的输入在尝试将其解析为一个数字之前肯定是一个数字,等等……
如果您一直明智地编写代码,那么您应该很少(如果有的话)遇到一个RuntimeException (或者它的子类),因此要求您在每一段可能抛出一段代码的代码周围放置try-catch块,会导致甚至简单类中数百个小try-catch块的混乱,或者一个巨大的捕获--所有这些块都不是特别可取的,并且在代码中添加了大量不必要的臃肿。
强迫捕获所有未经检查的异常将使诸如使用System.out.println()这样的“简单”操作更加冗长。
如果我们被迫捕获所有未经检查的异常(注意:这是一种最坏的情况,在API中只传播异常而不是处理异常),那么我们需要写什么才能将空行打印到控制台:
System.out.println();
// ^ Theoretically this could throw a NullPointerException所以,我们得解释一下:
try {
System.out.println();
} catch (NullPointerException e) {
// In practice this won't happen, but we're forced to deal with
// it all the same...
}我们还没完成呢。我们需要了解out是什么,以及println()是如何工作的,看看是否还有什么需要处理的。out实际上是一个PrintStream对象,那么我们可以了解它的println()方法吗?
这是println()
public void println() {
newLine();
}这意味着我们现在需要看看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;
}
}这里有更多的NullPointerException来源,但我们已经发现了。interrupt()可以(最终)抛出一个SecurityException,也可以导致一个InterruptedException,所以我们也必须处理这两个问题。
try {
System.out.println();
} catch (NullPointerException e) {
} catch (SecurityException e) {
} catch (InterruptedException e) {
}textOut.newLine()最终在处理char[]的Writer#write(String, int, int)中结束,因此我们立即也有了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) {
}它也叫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) {
}我们现在是这个方法调用中的六个单独的运行时异常,并且这不包括所有可能的异常,这些异常都可能从各种native方法调用中弹出(其中有几个),并且也不包括Error的任何子类。
如果我们真的被迫捕获所有异常,Java就会更糟。
发布于 2014-05-14 12:48:16
但并不强迫我们捕获未经检查的异常(例如,NullPointerException)。我想了解原因,为什么编译器是这样设计的?
因为如果Java不同的话,它作为一种语言将是完全不可用的。
您提到了最引人注目的例子:NullPointerException。这可以在访问实例字段或方法或访问数组时发生。这在Java代码的所有行中占很大的百分比。本质上,NullPointerException可以在任何地方发生,也可以发生在其他地方(ClassCastException,ArrayIndexOutOfBoundsException)。
如果您被迫在任何地方捕获代码,那么您如何编写代码呢?在catch块中你会做什么?日志记录即将结束,因为它需要一个方法调用,该方法调用可能抛出一个NullPointerException,您必须处理.
坦率地说,检查异常是语言设计中一个失败的实验。没有其他语言采用它们,Java社区的很大一部分人在可能的情况下避免使用它们。
https://stackoverflow.com/questions/23650854
复制相似问题