为什么未经检查的异常未被java编译器检查?

时间:2014-05-14 09:36:02

标签: java exception

我的问题更多的是关于设计, java编译器强制我们捕获已检查的异常(例如FileNotFoundException),但不强制我们捕获未经检查的异常(例如NullPointerException)。我想了解原因,为什么编译器会以这种方式设计?。

解释Unchecked Exceptions — The Controversy表明这是为了提高可读性。

不捕获运行时异常不是一个很大的代价吗?

编辑: 按成本我的意思是,在实时环境中而不是在编译时本身获取运行时异常。 如果在编译期间处理此类异常,则不存在错误泄漏的可能性。修复错误的成本随着检测到的相位延迟而增加。

6 个答案:

答案 0 :(得分:10)

对此的理由实际上包含在第11.2节的Java Language Specification中。这是相关的exerpt:

  

<强> 11.2。编译时检查例外

     

未经检查的异常类(第11.1.1节)免于编译时检查。

     

在未经检查的异常类中,错误类是免除的,因为它们可以在程序中的许多点发生,并且很难或不可能从中恢复。宣布此类例外的程序将毫无意义地混乱。复杂的程序可能希望捕获并尝试从某些条件中恢复。

     

在未经检查的异常类中,运行时异常类是免除的,因为在Java编程语言的设计者的判断中,必须声明此类异常不会有助于建立程序的正确性。 Java编程语言的许多操作和构造都可能在运行时导致异常。 Java编译器可用的信息以及编译器执行的分析级别通常不足以确定不会发生此类运行时异常,即使这对程序员来说可能是显而易见的。要求声明这样的异常类只会让程序员感到恼火。

     

例如,某些代码可能会实现一个循环数据结构,通过构造,它永远不会涉及空引用;程序员可以确定不会发生NullPointerException,但Java编译器很难证明它。建立数据结构的这种全局属性所需的定理证明技术超出了本规范的范围。

作为开发人员,我们至少可以在一定程度上控制我们编写的代码是否可以抛出未经检查的异常。例如,我们可以通过在尝试导致异常的操作之前检查NullPointerException来避免遇到null,或者我们可以编写我们的代码使得该变量永远不会 null首先。同样,我们可以通过执行完整性检查来避免NumberFormatException,以确保我们的输入肯定是一个数字,然后我们尝试将其解析为一个等等...

如果您已经明智地编写了代码,那么您应该很少(如果有的话)遇到RuntimeException(或其子类),因此要求您在每一段代码周围放置try-catch可能抛出一个会导致数百个小try-catch块甚至简单的类中的一个可怕的混乱,或者一个巨大的catch-everything块,这两者都不是特别理想的,并且添加了很多你的代码很大程度上是不必要的臃肿。

被迫捕获所有未经检查的异常会使“{简单”操作变得更加冗长。

如果我们被迫捕获所有未经检查的异常,那么让我们看看我们必须编写什么来打印空白行到控制台(注意:这是最糟糕的情况,其中例外简单地传播而不是在API中处理):

System.out.println()

所以,我们必须考虑到这一点:

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()结尾处理Writer#write(String, int, int),因此我们也会立即获得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) { } ,它可以抛出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的任何子类。

如果我们真的被迫抓住所有例外,那么Java会更糟糕。

答案 1 :(得分:3)

设计就是权衡。设计一个处理各种情况的系统是不可行的;我们在设计处理相当常见情况的系统时遇到了麻烦。

在我看来,经过检查和未经检查的例外情况之间的权衡是一个语言设计问题,在这种问题上,这种权衡被编成法典;允许API设计者,系统和应用程序两者确定是否应该由编译器强制捕获给定的异常。

  • 异常是否反映了调用者的重要性 不应该允许这种方法只是&#34;跳过它&#34;并让 程序崩溃?
  • 是应用程序的异常 程序员可能会忘记做,因此应该 被迫至少写catch (Exception e) {}所以人们可以指出 在他们和尖叫?
  • 异常是否反映了应用程序的内容 程序员可能认为这是一个由电话处理的情况, 但事实上并非如此?

在我看来,Java一直很好的灵活性 - 允许API设计师选择。

答案 2 :(得分:1)

  

但不会强迫我们捕获未经检查的异常(例如NullPointerException)。我想了解原因,为什么编译器会以这种方式设计?。

因为如果语言不同,Java将完全无法使用

你提到了最明显的例子:NullPointerException。每次访问实例字段或方法或访问数组时都会发生这种情况。所有Java代码行中占很大比例。从本质上讲,NullPointerException可以发生到处,还有其他几个(ClassCastExceptionArrayIndexOutOfBoundsException)。

如果你被迫到处抓住它们,你甚至可以编写代码?你会在catch块中做些什么?记录是正确的,因为它需要一个方法调用,这可能会抛出NullPointerException,你必须处理...

坦率地说,检查异常是语言设计中失败的实验。没有其他语言采用它们,Java社区的大部分都尽可能避免使用它们。

答案 3 :(得分:1)

运行时异常由于,

而发生
  1. 程序员监督
  2. 错误数据
  3. 它们都可以在代码本身中得到纠正。 无意发生。与Checked异常预期不同。

    在下面的方法中,由于运行时输入错误而发生异常。在调用函数之前应该纠正哪个(错误数据)。

    Character.toChars(-2);
    
     public static char[] toChars(int codePoint) {
            if (isBmpCodePoint(codePoint)) {
                return new char[] { (char) codePoint };
            } else if (isValidCodePoint(codePoint)) {
                char[] result = new char[2];
                toSurrogates(codePoint, result, 0);
                return result;
            } else {
                throw new IllegalArgumentException();
            }
        }
    

答案 4 :(得分:0)

该程序无法编译,因为有未声明的异常,它是FileNotFoundException。为了使程序正常运行而不会崩溃,必须捕获该异常或声明该异常被抛出。

答案 5 :(得分:-1)

由于未经检查的异常不需要被捕获,因此需要处理已检查的异常。因此,编译器&#34;强制&#34;您可以捕获已检查的例外情况,并让未经检查的例外处于未捕获状态。

此处有更多解释:Checked vs Unchecked Exceptions in Java