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

2022-09-04 07:42:36

由于 NullPointerException 是运行时异常,一个可能的原因是因为每个方法都可以抛出它,所以每个方法都需要有一个“throws NullPointerException”,并且会很丑陋。但是这在RemoteException中发生。

而一个可能的原因是,因为 RemoteException 不是运行时异常,而是告诉它客户端处理异常。但是远程环境中的每个方法都需要抛出它,因此抛出NullPointerException没有区别。

猜测?我清楚了吗?


答案 1

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

将 RemoteException 设置为已检查异常并要求远程方法在其 throws 子句中列出异常的决定不是宗教性的。该决策基于如何使分布式计算可靠。这个问题每隔一段时间就会出现在我们的用户列表中。我有一个详细的回复,我不久前发布。如果您有兴趣,请在这里。我在rmi-users存档中找不到它,所以我把它包括在下面。

干杯

-- 安


我想解决使 RemoteException 成为已检查异常而不是 RuntimeException 的基本原理。

1)网络不可靠

我希望他们是,但事实上,他们不是。每个网络都有暂时性故障。您可以构建网络冗余,但事实是大多数网络都没有网络冗余。内部网有暂时性故障,互联网也是如此。因此,所做的每个RPC都会失败。故障类型可能与“网络”本身没有任何关系;如果服务器用完了文件描述符,则客户端将收到连接异常。这不是网络故障,从某种意义上说,网络被破坏了;您的服务器处于资源匮乏的短暂状态。

RMI 并非仅设计用于处理当单个计算机崩溃时整个网络崩溃的有限情况。这样的网络被认为是可靠的,要么一切都在上升,要么一切都在下降 - 没有部分故障。RMI的目标是更一般的受众。

2) 无法对客户端隐藏 RPC 故障

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

3)检查异常促进更强大的程序

曾经有一段时间,Oak和Java的最早版本没有检查异常。异常处理是建议性的,这是一个不安全的世界。这是我们的团队(特别是吉姆·沃尔多和我:-)建议编译器检查异常。Jim的论点非常有说服力,讲述了一个健壮代码将统治的世界。经过一些考虑,Java被重新配置为检查异常。只有那些没有恢复或反映应用程序错误的异常才会被取消选中(例如,OutOfMemoryError,NullPointerException)。世界又安全了。

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

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

好了,回到正轨吧。由于 RemoteException 是 RPC 调用中的实际情况(参见 #1, #2),并且检查的异常会强制您编写安全代码 (#3),因此我们认为将 RemoteException 作为已检查的异常是一个好主意。编写健壮的分布式程序已经足够困难了,没有编译器来帮助你解决异常问题。

因此,有些人可能会争辩说,RemoteException就像OutOfMemoryError一样。如果远程调用失败,您的程序应该会失效。我不同意这一点。是的,在某些情况下,无法从 RemoteException 中恢复;但是,如果您正在编写可靠的分布式程序,则客户端需要捕获故障并相应地重试。也许您需要联系另一台服务器,或者中止某种事务。如果未处理 RemoteException,它将渗透并导致客户端崩溃 (yuk)。

其他人则表示,有一些远程接口同时用于本地情况和远程情况,客户端不必处理本地情况下的异常,因此 RemoteException 不必在 throws 子句中,并且处理它不应该是强制性的。现在,如果我们允许远程接口方法省略 RemoteException,并且有一个“rmic”开关来生成存根,该存根将引发未经检查的 RemoteException,则客户端在此事上别无选择。异常处理的决定应由客户端保留。如果定义一个仅引发未经检查的异常的接口,则永远无法编写希望编译器帮助处理这些异常的客户端。我们已经从上面的示例中看到,检查异常可以培养健壮的代码。

另一个时不时出现的问题是,开发人员需要简单地翻译本地接口并将其用作远程接口。这可能适用于一小部分情况,但如果接口在设计时未考虑并发性和部分故障以及调用延迟,则接口捕获的协议可能不适合在分布式情况下使用。在这些操作中传递的信息是否足以使操作幂等?也许吧,但很可能不是。

将 RemoteException 放在每个 throws 子句中可能看起来很痛苦,但这是编写健壮的分布式应用程序所要付出的代价。

-- 安·沃拉特


答案 2

比 有比 的潜力要大得多。任何在对象上调用方法的代码(实际上意味着任何Java代码)都可能引发.只有 RMI 代码可以抛出 .这是“所有代码”的一个很小的子集。NullPointerExceptionRemoteExceptionNullPointerExceptionRemoteException

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