如何知道一个方法可能抛出的所有异常?

3
有没有方法可以获取有关Java标准类异常安全方面的详细信息?由于主要使用C ++和C#,我对Java异常规范感到困惑,因此需要了解正确处理异常的方法。
更具体地说,让我们考虑ServerSocket。它一旦构造对象就开始侦听传入的连接。然后,您应该使用accept()接受连接(如果有人尝试连接)。
如果之前使用setSoTimeout()配置了服务器套接字,则有可能accept()会引发SocketTimeoutException,因为在指定的时间段内没有人尝试连接。那很好,服务器套接字仍然可用,因此您只需再次调用accept()。
但是SocketTimeoutException并不是accept()可能引发的唯一异常。所有其他异常都是什么意思?如果在2个catch子句中包装对accept()的调用:对于SocketTimeoutException和IOException,我是否仍然可以在进入IOException子句后安全地使用相关的ServerSocket实例?
我真的很感激Java范围和ServerSocket特定答案。

1
你需要深入研究Java文档才能了解其他异常的含义。一些良好记录的文档会解释发生了什么。在Java范围内,没有一个标准答案。异常发生后对象仍然存在,但该对象的状态取决于特定事物的实现方式。 - John Gardner
@John Gardner:已经花了太多时间阅读文档,没有明确的答案。 "I/O错误"代表什么?这是我的LAN卡死了还是只是网络延迟了几秒钟?虽然第一个是关键的,但第二个并不那么明显是“无法恢复”的。 - Andrey Agibalov
6个回答

3
重复使用对象是不安全的。对于这样的问题,我总是会查看源代码,这也是它开源的原因。
如果你查看这个链接:http://kickjava.com/src/java/net/ServerSocket.java.htm,你会发现在accept()中,如果套接字已关闭或未绑定,则会抛出SocketException(继承自IOException)。这两种状态都表示ServerSocket不再有效。
因此,对于这个类,通常情况下,如果出现异常,始终尝试在finally块中优雅地关闭ServerSocket,然后重新创建它。
另外,在Java范围内,始终查看源代码并了解接口正在执行的操作。如果这是使命关键的,请编写测试以重现行为(使用这种低级API应该不容易)。
最后——Java是否一直以这种方式进行?并非如此。有些类是无状态的,有些不是(例如ServerSocket),有些是线程安全的,有些不是。您需要了解它们构建的状态,以便在异常将您从主路径中移除时知道该怎么做,无论是从文档还是(大多数情况下)从代码中了解。
大多数人都诅咒那些检查的Java异常,因为它们中的大多数(与大多数IOException一样)实际上无法以有意义的方式恢复。他们争辩说,大多数情况下,您无法理解每个细节。这就是为什么许多复杂的框架可能会尝试两次或三次,如果它们认为在这种情况下它们可能会失败,但最终会将RuntimeException抛到顶层框架层。在那里,他们会从中提供有意义的错误(提供上下文)并记录所有详细信息,这通常是一个巨大的堆栈跟踪。这是许多开发人员害怕的知识资源。
因此,如果您无法从未经测试的边缘情况问题中恢复,请使用最有意义的堆栈跟踪(可能是RuntimeException的某个子类)注释所有上下文。设置监视。如果遇到频繁的问题,请修复它。

1
例如,ServerSocket的Javadoc并没有告诉你如何恢复以及可能发生哪些IOExceptions。但从我的角度来看,如果我正在使用设备,了解设备细节以及如何处理它们是必要的。在我的公司中,我们一直说:“所有内容都记录在代码中 :-)”。 - Stefan Schubert-Peters
IOException通常在设备读写失败时发生。就我个人而言,我认为没有必要了解设备的信息。 - Dimitri
ServerSocket异常的文档实在是太不充分了,无法真正确定实际的错误条件。源代码和堆栈跟踪是处理它们的唯一学习工具。 - Perception
@Perception - Java中I/O异常抛出的javadoc模糊不清有很好的原因(一般来说,以及在这个特定情况下)。如果它是具体的,那么它会创造一个期望,即Java可以区分所有实现平台上的不同情况。实际上它不能,因为通常从syscall ABI中无法获取所需的信息。 - Stephen C
@Stephen C - 你可能是对的。但事实仍然是,阅读ServerSocket的Javadoc足以告诉您发生的异常类型,但并不能真正提供关于它们发生的原因的见解。这就是OP感到好奇的地方。 - Perception
显示剩余2条评论

1

是的。对象仍然存在 - 可能处于错误状态,在这种情况下,它将抛出其他异常,直到被纠正。


因此,在一般情况下,调用对象的方法引发异常后无法确定对象是否正常? - Andrey Agibalov
1
这并不真正回答问题。 - Paul Bellora
1
这并不真正回答问题,因为它无法真正回答。这取决于异常! - John Gardner
@John Gardner:问题在于,根据异常,我怎么知道呢? - Andrey Agibalov

1

如果您阅读ServerSocket的规范,您会发现它在“等待连接时发生I/O错误”时抛出IOException。再次在套接字上执行accept()是安全的吗?是的。您是否会再次收到相同的异常?很可能如此。


你的意思是,accept() 只是一个告诉你有问题的人,但实际上它并不是真正失败的人? - Andrey Agibalov
“失败”是什么意思?它将尝试建立连接。如果无法建立连接,它将抛出4个异常之一,告诉您发生了什么。在这种情况下,IOException是一个通用异常(通常如此)。如果其他3个异常之一不能解释问题,它将被抛出。 - Konstantin Tarashchanskiy
ServerSocket有一个客户端连接队列。调用accept()会从队列中取出第一个客户端并尝试建立连接。但是,在此操作过程的某个位置,客户端的ISP断开了连接,因此我们未能与该客户端建立连接。这会影响队列中的其他客户端吗?accept()会为下一个客户端工作吗? - Andrey Agibalov
不会影响队列中的其他客户端。在这种情况下,接受将起作用,因为服务器和客户端之间的每个连接都应该在一个单独的线程中处理。所以,没有问题。 - Dimitri

1

我还没找到一种简单的方法来判断一个对象是否仍处于可用状态。每个对象都有自己的规则。

针对ServerSocket的特殊情况,我会尝试以下两种不同的方法:

  • 运行netstat或其他工具来查看操作系统认为socket正在执行的操作。如果操作系统认为它没有在监听,那么就发生了某些情况。

  • 编写一个测试程序来抛出异常并查看其结果。我经常这样做(尤其是在专有软件中)。你选择的ServerSocket情况会更困难,因为我能想到的所有场景(如地址被占用、权限不足等)都不会导致对象仍然有效。


这些解决方案太依赖于平台了。我在家用的是Linux,在办公室用的是Windows 7,如果有差异怎么办? - Andrey Agibalov
你可能也需要在你的真实程序中考虑这个问题。 - Dusty
另一个想法:自己连接到ServerSocket,看看它是否正常工作。 - maaartinus

1
但是SocketTimeoutException并不是accept()可能抛出的唯一异常。其他所有异常都意味着什么?根据javadoc,它声明的异常包括IOException、SecurityException、SocketTimeoutException和IllegalBlockingModeException。SecurityException和IllegalBlockingModeException仅在特定上下文中发生,您不应尝试捕获和处理它们。(它们不是您想要尝试从中恢复的问题!)当出现“某些其他I/O错误”时,会发生IOException情况。Javadoc没有指定这些I/O错误可能是什么,但可能的情况包括:绑定到的地址无效、传输协议错误以及操作系统协议堆栈中发生了某些错误(资源问题、错误等)。

事实上,javadoc没有说明可能抛出哪些IOException子类,这暗示你不应该尝试聪明地恢复。如果你这样做,你的代码很可能会依赖于平台。

如果我用两个catch子句包装accept()调用:对于SocketTimeoutException和IOException,当我进入IOException子句后,我仍然可以安全地使用相关的ServerSocket实例吗?

是和否。从某种意义上说,它是安全的,因为你不会使你的应用程序比它已经处于的更糟糕的状态。另一方面,不能保证下一个accept调用不会出现相同的问题。

如果你的应用程序旨在作为无人值守的服务器运行,我认为你别无选择,只能记录IOException并重试...希望问题是暂时的。


0

你可以在setsoTimeout的javadoc中找到答案,它说:

使用指定的超时时间(以毫秒为单位)启用/禁用SO_TIMEOUT。 将此选项设置为非零超时后,对于此ServerSocket的accept()调用将仅阻塞此时间量。 如果超时到期,则会引发java.net.SocketTimeoutException,尽管ServerSocket仍然有效。 在进入阻塞操作之前必须启用该选项才能生效。 超时必须大于0。 超时为零将解释为无限超时。


这只涉及到 SocketTimeoutException。其他的呢? - Andrey Agibalov

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