Java异常层次结构,重点是什么?

Java异常层次结构,重点是什么?,java,Java,我发现自己正忙于重构我的宠物项目,并消除与异常抛出声明相关的所有杂音。基本上,所有违反条件的行为都是断言违反,或者正式地说,是断言错误,在Java中,它被慷慨地允许从方法的签名中省略。我的问题是:拥有异常层次结构有什么意义?我的经验是,每个例外都是唯一的,没有正式的标准来确定一组例外是另一组例外的子集。即使检查异常和未检查异常之间的区别是模糊的,例如,为什么我会坚持客户端代码捕获异常,而懒惰(或不耐烦)的程序员可以轻松地将其包装成RuntimeException并在我身上重新显示它?理论上,Ja

我发现自己正忙于重构我的宠物项目,并消除与异常抛出声明相关的所有杂音。基本上,所有违反条件的行为都是断言违反,或者正式地说,是断言错误,在Java中,它被慷慨地允许从方法的签名中省略。我的问题是:拥有异常层次结构有什么意义?我的经验是,每个例外都是唯一的,没有正式的标准来确定一组例外是另一组例外的子集。即使检查异常和未检查异常之间的区别是模糊的,例如,为什么我会坚持客户端代码捕获异常,而懒惰(或不耐烦)的程序员可以轻松地将其包装成RuntimeException并在我身上重新显示它?

理论上,Java异常层次结构具有一定的意义:

Throwable*
 -> Error (OutOfMemoryError, etc.)
 -> Exception (IOException, SQLException, etc.)
     -> RuntimeException (IndexOutOfBoundsException, NullPointer, etc.)
现在这些背后的理论实际上有一定的道理。(遗憾的是,由于积攒的积垢,实际实施过程中还存在一些不尽如人意的地方。)

错误
-下降的
可丢弃的
对象是程序无法恢复的严重错误。(换句话说,您通常不会发现这些问题。)其中一个突然出现的问题是一个严重的整体系统问题。例如,当内存耗尽时,这表示某个地方出现了严重的故障,因为从理论上讲,GC现在已经拼命地尝试为您释放空间。抓住这一点毫无意义

异常
-下降的
可丢弃的
对象是程序在正常操作过程中可能遇到的所有错误;网络错误、文件系统错误等。事实上,除了那些源于
RuntimeException
的错误之外,程序必须显式地处理这些错误——它们就是所谓的“检查异常”。(当然,差劲的程序员会通过剔除它们来“处理”这些问题,但这是程序员的问题,而不是系统的问题。)

RuntimeException
-下降的
Throwable
对象略有不同。这些错误不一定是预期的,但当它们发生时,程序可以合理地从中恢复。因此,它们不会被检查(程序没有义务处理这些问题),但如果有合理的方法来处理这种情况,它们可能会被检查。通常,这些异常表示某种编程错误(与前一类相反,前一类是正常操作中出现的预期错误)

那么这种层次结构有意义吗?嗯,在某种程度上似乎是这样<代码>错误由系统抛出,表示可能会导致程序死机的重大故障<代码>运行时异常,如果使用得当,会由系统库(或偶尔由您自己的程序)引发,通常意味着有人在某个地方搞砸了,但这没关系,因为您可能可以从中恢复。其他
异常
对象是预期的错误,它们实际上是对象所述接口的一部分

但是

最后一项就是问题所在。直截了当地说,检查过的例外情况是躯干解剖结构下游的严重疼痛。在我看来(以及许多其他人的我可能会补充!),它们不必要地用异常处理样板文件将代码弄得杂乱无章,从而呈现出异常的全部意义:错误检测和错误处理的分离。通过强制链中的每个方法处理异常,即使只是重新包装并传递异常-代码被错误处理的细节弄得乱七八糟,以至于与返回状态代码并在每次方法调用后处理它们相比,它没有什么好处


如果Java是一种更智能的编程语言,那么检查的异常将在编译/链接时进行检查,以查看它们是否在系统范围内得到了正确处理,而不是在每个类文件中的每个方法调用中。不幸的是,Java的整个体系结构不允许进行这种级别的整个程序分析,其结果也是,在我看来(但也是许多人所共有的),实际上,它是异常处理和错误返回两个世界中最糟糕的一个混合体:你得到了大多数显式错误返回的样板框架,但是你也得到了来自类似异常的行为的

我不认为层次结构是坏的,它只是没有太大的用处。99%的时候,一个异常信号表明发生了非常糟糕的事情,我的选择很少。我们基本上死了。唯一的选择是选择要向用户显示的适当错误消息

我是那些懒惰的程序员之一。如果我从代码内部调用'processFile()'方法,它抛出一个异常,我可能会生成一条“您的文件无法处理”消息,并重新抛出一个RuntimeException。我们无法恢复。我们是一个退伍军人项目。我们不在了。通过在调用堆栈中的每个方法上添加一个已检查的异常来废弃代码是没有任何好处的

我总是这样编码:

try {
    processAFile();
} catch (Exception e) { // Just catch them all.
    logger.error(e);
    logger.error("log any important information here.");
    throw new RuntimeException("We were unable to process your file.");
}
现在RuntimeException一直响到主方法,并且得到了负责任的处理

在代码的顶部,我捕获所有异常,根据需要记录,通常回滚事务,并执行显示客户友好的错误消息所需的操作

我喜欢检查异常和未检查异常之间的区别,尽管我想不出原因。IntelliJ将自动填充几个异常捕获块。我认为“嗯,这很有趣”,并用一个
catch(异常e)
替换它们,因为恢复总是一样的。在上面的例子中,我必须捕捉到checked和unchecked,然后我就死了,错误消息也是一样的,所以谁在乎呢。为什么要抛出选中的异常。死就是死

唯一一次我能想到我在哪里处理