【问题标题】:How do you know all the exceptions a method can throw你怎么知道一个方法可以抛出的所有异常
【发布时间】:2011-08-12 23:20:58
【问题描述】:

有没有办法获得有关 Java 标准类的异常安全方面的一些详细信息?主要使用 C++ 和 C#,我对 Java 异常规范感到困惑,所以我需要了解处理异常的正确方法。

更具体地说,让我们考虑ServerSocket。一旦构建了它的对象,它就开始监听传入的连接。然后,您应该使用accept() 接受连接(如果有人尝试连接)。

如果您之前使用setSoTimeout() 配置了您的服务器套接字,则accept() 将抛出SocketTimeoutException 的更改,因为在指定的时间段内没有人尝试连接。没关系,服务器套接字仍然可用,所以你只需再次调用accept()

但是SocketTimeoutException 并不是accept() 可能抛出的唯一东西。所有其他例外是​​什么意思?如果我使用 2 个 catch 子句对accept() 的调用进行包装:对于SocketTimeoutExceptionIOException,在我进入IOException 子句后,我还能安全使用相关的ServerSocket 实例吗?

我非常感谢 Java 范围和 ServerSocket 特定的答案。

【问题讨论】:

  • 您必须深入研究 javadocs 才能了解所有其他异常的含义。有据可查的东西可以解释发生了什么。在 Java 范围内,没有答案。异常发生后该对象仍然存在,但该对象所处的状态取决于该特定事物的实现。
  • @John Gardner:已经花太多时间阅读文档了。没有明确的答案。 “发生 I/O 错误”代表什么?这是否意味着我的 LAN 卡已经死了,或者只是网络延迟了几秒钟?虽然第一个很关键,但第二个并不是那么明显“不可恢复”。

标签: java exception-handling exception-safe exception-safety


【解决方案1】:

重复使用对象是不安全的。对于这样的问题,我总是会寻找来源,这就是它开放的原因。

因此,如果您查看那个:http://kickjava.com/src/java/net/ServerSocket.java.htm,您会注意到在accept() 中,如果套接字已关闭或不再绑定,则会引发 SocketException(从 IOException 继承)。这两种状态都表明 ServerSocket 不再有效。

所以对于这个类,一般来说,如果你因异常而失败,总是尝试在 finally 块中优雅地关闭 ServerSocket,然后重新创建它。

另外,关于 Java 范围的问题范围:始终查看源代码并了解接口在做什么。如果是重现行为的任务关键型编写测试(使用如此低级的 api 根本不应该容易)。

最后 - Java 是否一直以这种方式做这些事情?不,有些类是无状态的,有些则不是(如 ServerSocket),有些是线程安全的,有些则不是。而且您需要从文档或(主要)从代码中了解它们构建的状态,以便了解当异常将您从主路径中击退时该怎么做。

大多数人诅咒那些检查过的 Java 异常,因为它们中的大多数(与大多数 IOException 一样)并不能真正以有意义的方式恢复。大多数时候,他们争辩说,你无法理解每一个优秀的极端案例。这就是为什么许多复杂的框架可能会重试两次或三次,如果他们认为在这种情况下可能会重试,但最终会向顶层框架层抛出 RuntimeException。他们在那里做了一些有用的事情(提供上下文的有意义的错误)并记录他们拥有的所有细节,这在大多数情况下是一个巨大的堆栈跟踪。大量的知识资源,许多开发人员都害怕。

如果您无法从未经测试的极端情况问题中恢复过来,您该怎么办?抛出(可能带有 RuntimeException 的某个子类)最有意义的堆栈跟踪,并用您拥有的所有上下文进行注释。设置监控。如果您经常遇到问题,请修复它。

【讨论】:

  • 当 javadoc 可用且清晰时,为什么要查看源代码?
  • ServerSocket 上的 Javadoc 例如并没有告诉您是否以及如何恢复以及可能发生哪些类型的 IOException。但从我的角度来看,有必要了解设备详细信息以及在使用设备时如何处理它们。在我的公司里,我们一直说:这一切都记录在代码中:-)
  • IOException 主要发生在设备上的读/写失败时。就我的观点而言,我认为没有必要提供有关设备的信息
  • ServerSocket 异常的文档在实际确定实际错误条件方面严重不足。源代码和堆栈跟踪是处理它们的唯一学习工具。
  • @Perception - 抛出的 I/O 异常的 javadoc 含糊不清是有充分理由的(通常,在这种特定情况下)。如果它是具体的,它将产生一种期望,即 Java 可以区分所有实现平台的不同情况。实际上它不能,因为执行此操作所需的信息通常无法从系统调用 ABI 中获得。
【解决方案2】:

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

【讨论】:

  • 那么,在一般情况下,在调用其方法之一导致异常后,无法判断对象是否正常?
  • 这并不能真正回答问题
  • 它并没有真正回答这个问题,因为它不是真正可以回答的。这取决于例外情况!
  • @John Gardner:问题是 - 根据例外情况,我怎么知道?
【解决方案3】:

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

【讨论】:

  • 你的意思是,accept() 只是一个说有问题的人,但实际上并不是一个真正失败的人?
  • “失败”是什么意思?它将尝试建立连接。如果它不能,它会抛出 4 个异常之一来告诉你发生了什么。在这种情况下, IOException 是一个包罗万象的东西(通常是这样)。如果其他 3 个异常之一不能解释问题,则会被抛出。
  • ServerSocket 有一个想要连接的客户端队列。对accept() 的调用将第一个从队列中取出并尝试建立连接。在此操作的中间某处,客户端 ISP 出现故障,因此我们无法与该客户端建立连接。它会影响队列中的其他客户端吗? accept() 会为下一个客户工作吗?
  • 不,它不会影响队列中的其他客户端。在这种情况下接受将起作用,因为服务器和客户端之间的每个连接都应在单独的线程中处理。所以,没有问题
【解决方案4】:

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

在您使用 ServerSocket 的具体情况下,我会尝试两种不同的方法之一:

  • 运行 netstat 或其他一些实用程序来查看操作系统认为套接字在做什么。如果操作系统认为它没有在听,那么就发生了一些事情。 或

  • 编写一个将抛出异常的测试程序并查看它的作用。我一直这样做(尤其是使用专有软件)。在您选择的 ServerSocket 案例中会更难,因为我能想到的所有场景(例如正在使用的地址、权限不足等)都不会导致对象仍然有效。

【讨论】:

  • 嗯,这些解决方案过于依赖平台。我家有 Linux,办公室有 Windows 7,如果有差异怎么办?
  • 您可能也需要在实际程序中考虑这一点。
  • 另一个想法:自己连接到ServerSocket,看看它是否有效。
【解决方案5】:

但是 SocketTimeoutException 并不是 accept() 可能抛出的唯一东西。所有其他例外是​​什么意思?

根据javadoc,声明的异常有IOExceptionSecurityExceptionSocketTimeoutExceptionIllegalBlockingModeExceptionSecurityExceptionIllegalBlockingModeException 仅在特定上下文中出现,您不应尝试捕获和处理它们。 (它们不是您要尝试从中恢复的问题!)IOException 的情况发生在“一些其他 I/O 错误”发生时。 javadoc 没有指定这些 I/O 错误可能是什么,但可能性可能包括以下内容:

  • 您绑定的地址不再有效
  • 发生传输协议错误
  • 操作系统协议栈中出现了一些错误(资源问题、错误...)

(javadoc 没有说明可能抛出哪些IOException 子类的事实暗示您不应该尝试做聪明的事情来尝试恢复。如果这样做,您的代码很可能是平台依赖。)

如果我用 2 个 catch 子句包装对 accept() 的调用:对于 SocketTimeoutException 和 IOException,在我进入 IOException 子句后,我还能安全地使用相关的 ServerSocket 实例吗?

是的,不是的。从某种意义上说,它是安全的,您不会将应用程序置于比它已经处于的更糟糕的状态。另一方面,不能保证下一次 accept 调用不会因同样的问题而失败。

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

【讨论】:

    【解决方案6】:

    您可以在setsoTimeout 的 javadoc 中找到答案,上面写着:

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

    【讨论】:

    • 只与SocketTimeoutException有关。其他人呢?
    猜你喜欢
    • 1970-01-01
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多