将 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 子句中可能看起来很痛苦,但这是编写健壮的分布式应用程序所要付出的代价。
-- 安·沃拉特