因为 NullPointerException 是运行时异常,一个可能的原因是因为每个方法都可以抛出它,所以每个方法都需要有一个“抛出 NullPointerException”,而且会很丑陋。但这发生在 RemoteException 上。
由于 RemoteException 不是运行时异常,一个可能的原因是告诉它客户端处理异常。但是远程环境中的每个方法都需要抛出它,所以抛出 NullPointerException 没有区别。
猜测?我说清楚了吗?
因为 NullPointerException 是运行时异常,一个可能的原因是因为每个方法都可以抛出它,所以每个方法都需要有一个“抛出 NullPointerException”,而且会很丑陋。但这发生在 RemoteException 上。
由于 RemoteException 不是运行时异常,一个可能的原因是告诉它客户端处理异常。但是远程环境中的每个方法都需要抛出它,所以抛出 NullPointerException 没有区别。
猜测?我说清楚了吗?
我不会讨论这个决定,我只会引用 Ann Wollrath(领导 Java RMI 的设计和实现)对决定的解释。这是从RMI-USERS 档案中的这条消息中提取的(来自 1999 年 1 月的消息):
使 RemoteException 成为已检查异常并要求远程方法在其 throws 子句中列出异常的决定并不是一个宗教性的决定。该决定基于如何使分布式计算可靠。这个问题每隔一段时间就会出现在我们的用户列表中。我有一个详细的回复,我不久前发布了。如果你有兴趣,这里就是。我在 rmi-users 存档中找不到它,所以我将它包含在下面。
干杯,
——安
我想说明使 RemoteException 成为已检查异常而不是 RuntimeException 的基本原理。
1)网络不可靠
我希望他们是,但事实上,他们不是。每个网络都有短暂的故障。您可以构建网络冗余,但事实是大多数网络都没有。Intranet 有短暂的故障,Internet 也是如此。所以,每一个 RPC 都会失败。故障的类型可能与“网络”本身没有任何关系;如果您的服务器用完文件描述符,您的客户端将收到连接异常。从网络被破坏的意义上说,这不是网络故障;您的服务器处于资源匮乏的过渡状态。
RMI 并非旨在仅处理单个机器崩溃时整个网络崩溃的有限情况。这样的网络将被认为是可靠的,要么一切正常,要么一切都停止——没有部分故障。RMI 面向更广泛的受众。
2) 无法对客户端隐藏 RPC 失败
部分故障是分布式编程的一个事实;这些故障不能对程序隐藏。客户端显示失败,无论异常是检查异常还是未检查异常,它仍然显示。那么,应该如何向客户端指示此类故障呢?
3)检查异常促进更健壮的程序
曾经有一段时间,Oak 和 Java 的最早版本没有检查异常。异常处理是建议性的,这是一个不安全的世界。是我们的小组(尤其是 Jim Waldo 和我 :-) 建议编译器检查异常。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 子句中似乎很痛苦,但这是编写健壮的分布式应用程序所付出的代价。
——安·沃尔拉斯
比的潜力要NullPointerException
大得多RemoteException
。任何调用对象方法的代码(实际上意味着任何 Java 代码)都可能会抛出NullPointerException
. 只有 RMI 代码可以抛出RemoteException
. 这是“所有代码”的一小部分。
在编写 RMI 库时,设计人员决定让客户端代码能够处理这些异常。考虑到远程代码执行的性质,我认为这是合理的。
我理解的方式是:
例如,NullPointerExceptions 总是可以避免的,因此是未经检查的异常。当出现网络故障时,可能会发生 RemoteException,在方法调用之前无法合理防止,因此需要进行检查。
除了RemoteException
仅适用于java.rmi
和javax.rmi
包(及其子包)中的代码外,RemoteException
它是一种类型IOException
,很像SocketException
is... 并且所有IOException
s 都是检查异常。