为什么NullPointerException是运行时异常而RemoteException不是?

14

NullPointerException是一个运行时异常的可能原因是每个方法都可能抛出它,所以每个方法都需要有"throws NullPointerException",这样很丑陋。但是远程异常(RemoteException)就是这种情况。

而远程异常不是运行时异常的可能原因是告诉客户端如何处理该异常。但在远程环境中,每个方法都需要抛出它,所以和抛出NullPointerException没有区别。

猜测?我表达清楚了吗?


1
人们如何在没有检查异常概念的语言中处理?有什么事情是在其他语言中无法干净地完成的?问题在于人们认为“失败”是一种特殊情况,而不是意识到失败是常态。这些人喜欢像检查异常一样的大型GOTO语句。状态测试方法?超时?那不行。如果出了问题,就用大型GOTO语句。这基本上是Java的一个特性,它肯定不能团结整个Java社区(例如Spring框架非常讨厌它们)。 - SyntaxT3rr0r
5
Webinator,那个人问了一个非常合理的问题。没有必要发表激烈言论。 - DJClayworth
4个回答

21

我不会讨论这个决定,我只引用Ann Wollrath(领导Java RMI的设计和实现)对此决定的解释。这是从RMI-USERS档案中摘取的信息(来自1999年1月的消息):

将RemoteException作为已检查异常并要求远程方法在其throws子句中列出该异常的决定并不是一个涉及信仰的决定。而是基于如何使分布式计算可靠的考虑。这个问题偶尔会在我们用户列表上出现。我有一个详细的回复,我曾经发表过。如果你感兴趣,下面是我的回复。我在rmi-users档案中找不到它,因此我在下面附上了它。

祝好!

-- Ann


我想解释一下将RemoteException作为Checked Exception而不是RuntimeException的基本原理。

1)网络不可靠

很遗憾,每个网络都会出现短暂的故障,即使您可以建立网络冗余,大多数网络也没有这样做。因特网和内部网络都有短暂的故障,所以每个RPC调用都可能失败。故障类型可能与“网络”本身无关;如果服务器没有文件描述符了,客户端将获得连接异常。这不是网络故障,而是服务器处于资源匮乏的瞬态状态。

RMI并非仅设计处理整个网络在单台计算机崩溃时的情况。这样的网络被认为是可靠的,要么所有内容正常运行,要么全部停止--不存在部分故障。RMI的目标是面向更广泛的受众。

2)RPC失败不能对客户端隐藏

部分故障是分布式编程的事实,这些故障无法隐藏到程序中。失败会在客户端中显示,无论是已检查的异常还是未检查的异常,它仍然会显示在客户端。那么,应该如何向客户端指示此类故障?

3)已检查的异常促进了更健壮的程序

曾经的Java版本没有已检查的异常,而异常处理是建议性的,这是一个不安全的世界。Jim Waldo和我特别建议编译器检查异常。Jim在他的论点中非常有说服力,讲述了健壮代码将统治的世界。在考虑一番后,Java被重新设计为拥有已检查的异常。只有那些没有恢复或反映应用程序错误的异常将不被检查(例如OutOfMemoryError、NullPointerException)。世界再次变得安全。

当Java API和编译器中许多异常从未检查过改为已检查时,Java工程师惊讶地发现,编译器强制执行区分时,他们发现了实现中的漏洞!因此,处理错误条件的最佳努力,无论意图多么良好,都不够好。编译器对某些事情很有用:-)

4)RemoteException应该是已检查的异常

既然远程调用中存在RemoteException(请参见#1,#2),而已检查的异常可以强制您编写安全代码(#3),因此我们认为将RemoteException作为已检查的异常是一个好主意。编写健壮的分布式程序已经足够困难,无需编译器来帮助您处理异常。

因此,有人可能会认为RemoteException类似于OutOfMemoryError;如果远程调用失败,则您的程序应该崩溃死亡。我不同意这一点。是的,在某些情况下,没有从RemoteException中恢复;但是,如果您正在编写可靠的分布式程序,则客户端需要捕获故障并适当地重试。也许您需要与另一个服务器联系或终止某种事务。如果未处理RemoteException,它将上升并崩溃客户端(呕吐)。

其他人曾经说过,有些远程接口在本地情况和远程情况下都被使用,客户端不应该在本地情况下处理异常,因此不需要在throws子句中包含RemoteException并且还不需要强制处理它。现在

-- Ann Wollrath


1
她看起来非常确信,人们没有用连“检查异常”概念都没有的语言编写“强大的分布式应用程序”[sic]。我想尝一下她上个世纪吸的东西,听起来很猛 :) - SyntaxT3rr0r
8
@Downvoter 我真的很想知道为什么这个答案被踩了。即使你不同意作者的观点,我只是在发布参考资料,而不是个人意见。情绪化的踩票是荒谬的。 - Pascal Thivent
1
我无法想象为什么会有人对此进行负投票,因为无论一个人对已检查异常的感受如何,这显然是你可能得到的最正确的答案。 - ColinD
3
@Webinator 的观点不在于人们能否使用没有受检异常的语言编写稳健的分布式应用程序,而在于使用受检异常的语言更易于实现。我总是会选择事情变得容易的语言而不是仅仅可能的语言。 - DJClayworth
2
+1 - 绝对是对所问问题的权威回答,也是一篇有趣的阅读。 - Bill the Lizard
+1 - 即使我在某种程度上不同意,这看起来是完美的答案 - 做得好! - Bruno Bossola

4

NullPointerExceptionRemoteException更有潜在的风险。 几乎任何调用对象方法的Java代码都可能抛出NullPointerException。 只有RMI代码才能抛出RemoteException,这只是“所有代码”的微小子集。

在编写RMI库时,设计人员决定让客户端代码期望处理这些异常。考虑到远程代码执行的本质,我认为这是合理的。


3
我理解的方式是:
  • 运行时异常是为了表示可以避免的事情而抛出的。
  • 异常是为了表示不可避免但可恢复的事情而抛出的。
  • 错误是为了表示不可避免且无法恢复的事情而抛出的。
例如,NullPointerException总是可以避免的,因此是未经检查的异常。当存在网络故障并且在方法调用之前不能合理地防止时,可能会发生RemoteException,因此需要经过检查。

1
我认为您在列表中颠倒了“Exceptions”和“RuntimeExceptions”的顺序。NullPointerException是一个RuntimeException - Matthew

0
除了RemoteException仅适用于来自java.rmijavax.rmi包(及其子包)的代码之外,RemoteExceptionIOException的一种类型,就像SocketException一样...而且所有的IOException都是已检查异常。

我不会给你点踩,但是这个答案并不能成为RuntimeException的不可能原因。RemoteException只是Exception的一种类型,而不是IOExeption。成为IOException是在决定成为checked exception之后做出的决定。 - The Student
Java 中所有与通信相关的异常都是 IOException 的子类。IOException (以及任何其他从 Exception 而不是 RuntimeException 继承的类)是一个受检查的异常,因此任何继承自它的类也是受检查异常。 - Powerlord

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接