异常传播指南(Java 中) [英] Guidelines on Exception propagation (in Java)

查看:26
本文介绍了异常传播指南(Java 中)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Java 中是否有关于异常传播的指南?

Are there any guidelines on exception propagation in Java?

什么时候给方法签名添加异常?例如:如果只有在缺少必要的程序资源时才抛出异常,并且只能在顶层处理,我是否将其传播到使用此异常的所有方法中,通过所有使用 erring 方法的方法?

When do you add an exception to the method signature? For example: if an exception is only thrown when an essential program resource is missing, and can only be handled at the top level, do I propagate it through all methods using this exception through all the methods using the erring method?

有什么好的做法吗?有什么不好的做法吗?

Are there any good practices? Any bad practices?

如果我含糊不清,我很抱歉,但我只是在寻找一些关于异常的编程风格的(一般)建议.

I'm sorry if I'm being vague, but I'm just looking for some (general) advice on programming style concerning exceptions.

推荐答案

过去帮助过我的指南包括:

Guidelines that have helped me in the past include:

  • 当方法无法处理异常时抛出异常,更重要的是应该由调用者处理.这方面的一个很好的例子恰好出现在 Servlet API 中 - doGet() 和 doPost() 在无法正确读取请求的某些情况下抛出 ServletException 或 IOException.这两种方法都无法处理异常,但容器可以(在大多数情况下会导致 50x 错误页面).
  • 如果方法无法处理异常则冒泡.这是上述的推论,但适用于必须捕获异常的方法.如果该方法无法正确处理捕获的异常,则最好将其冒泡.
  • 立即抛出异常.这听起来可能很模糊,但是如果遇到异常情况,那么最好抛出一个指示原始故障点的异常,而不是尝试通过错误代码处理故障,直到认为适合抛出异常的点.换句话说,尽量减少将异常处理与错误处理混为一谈.
  • 记录异常或冒泡,但不要同时进行.记录异常通常表明异常堆栈已经完全展开,表明没有进一步的异常冒泡发生.因此,不建议同时执行这两项操作,因为这通常会导致调试过程中令人沮丧的体验.
  • 使用 java.lang 的子类.Exception(检查异常),当你除调用者来处理异常时.如果调用者不处理异常,这将导致编译器抛出错误消息.但请注意,这通常会导致开发人员吞下"代码中的异常.
  • 使用 java.lang 的子类.RuntimeException(未经检查的异常)表示编程错误.此处推荐的异常类包括 IllegalStateException, IllegalArgumentException, UnsupportedOperationException 等等.同样,必须小心使用异常类,如 NullPointerException(扔一个几乎总是一种不好的做法).
  • 使用异常类层次结构在各个层级传达有关异常的信息.通过实现层次结构,您可以概括调用者中的异常处理行为.例如,您可以使用像 DomainException 这样的根异常,它有几个子类,如 InvalidCustomerException、InvalidProductException 等.这里需要注意的是,如果您将每个单独的异常场景表示为单独的异常,您的异常层次结构可能会很快爆炸.
  • 避免捕获无法处理的异常.很明显,但是很多开发人员试图捕获 java.lang.Exception 或 java.lang.Throwable.由于可以捕获所有子类异常,因此当捕获全局"异常类时,应用程序的运行时行为通常会很模糊.毕竟,我们不想捕获 OutOfMemoryError - 应该如何处理这样的异常?
  • 小心包装异常.重新抛出异常会重置异常堆栈.除非原始原因已提供给新异常对象,否则它将永远丢失.为了保留异常堆栈,必须将原始异常对象提供给新异常的构造函数.
  • 仅在需要时将已检查的异常转换为未检查的异常.包装异常时,可以包装已检查的异常并抛出未检查的异常.这在某些情况下很有用,尤其是当意图中止当前正在执行的线程时.但是,在其他情况下,这可能会导致一些痛苦,因为不会执行编译器检查.因此,将已检查的异常改编为未检查的异常并不是盲目的.
  • Throw exceptions when the method cannot handle the exception, and more importantly, should be handled by the caller. A good example of this happens to present in the Servlet API - doGet() and doPost() throw ServletException or IOException in certain circumstances where the request could not be read correctly. Neither of these methods are in a position to handle the exception, but the container is (which results in the 50x error page in most cases).
  • Bubble the exception if the method cannot handle it. This is a corollary of the above, but applicable to methods that must catch the exception. If the caught exception cannot be handled correctly by the method, then it is preferable to bubble it.
  • Throw the exception right away. This might sound vague, but if an exception scenario is encountered, then it is a good practice to throw an exception indicating the original point of failure, instead of attempting to handle the failure via error codes, until a point deemed suitable for throwing the exception. In other words, attempt to minimize mixing exception handling with error handling.
  • Either log the exception or bubble it, but don't do both. Logging an exception often indicates that the exception stack has been completely unwound, indicating that no further bubbling of the exception has occurred. Hence, it is not recommended to do both at the same time, as it often leads to a frustrating experience in debugging.
  • Use subclasses of java.lang.Exception (checked exceptions), when you except the caller to handle the exception. This results in the compiler throwing an error message if the caller does not handle the exception. Beware though, this usually results in developers "swallowing" exceptions in code.
  • Use subclasses of java.lang.RuntimeException (unchecked exceptions) to signal programming errors. The exception classes that are recommended here include IllegalStateException, IllegalArgumentException, UnsupportedOperationException etc. Again, one must be careful about using exception classes like NullPointerException (almost always a bad practice to throw one).
  • Use exception class hierarchies for communicating information about exceptions across various tiers. By implementing a hierarchy, you could generalize the exception handling behavior in the caller. For example, you could use a root exception like DomainException which has several subclasses like InvalidCustomerException, InvalidProductException etc. The caveat here is that your exception hierarchy can explode very quickly if you represent each separate exceptional scenario as a separate exception.
  • Avoid catching exceptions you cannot handle. Pretty obvious, but a lot of developers attempt to catch java.lang.Exception or java.lang.Throwable. Since all subclassed exceptions can be caught, the runtime behavior of the application can often be vague when "global" exception classes are caught. After all, one wouldn't want to catch OutOfMemoryError - how should one handle such an exception?
  • Wrap exceptions with care. Rethrowing an exception resets the exception stack. Unless the original cause has been provided to the new exception object, it is lost forever. In order to preserve the exception stack, one will have to provide the original exception object to the new exception's constructor.
  • Convert checked exceptions into unchecked ones only when required. When wrapping an exception, it is possible to wrap a checked exception and throw an unchecked one. This is useful in certain cases, especially when the intention is to abort the currently executing thread. However, in other scenarios this can cause a bit of pain, for the compiler checks are not performed. Therefore, adapting a checked exception as an unchecked one is not meant to be done blindly.

这篇关于异常传播指南(Java 中)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆