异常传播指南(Java)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3551221/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Guidelines on Exception propagation (in Java)
提问by wen
Are there any guidelines on exception propagation in Java?
Java 中是否有关于异常传播的指南?
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.
如果我含糊不清,我很抱歉,但我只是在寻找有关异常的编程风格的一些(一般)建议。
采纳答案by Vineet Reynolds
Guidelines that have helped me in the past include:
过去帮助过我的指导方针包括:
- 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, UnsupportedOperationExceptionetc. 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.
- 当方法无法处理异常时抛出异常,更重要的是应该由调用者处理。一个很好的例子恰好出现在 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 - 应该如何处理这样的异常?
- 小心包裹异常。重新抛出异常会重置异常堆栈。除非原始原因已提供给新的异常对象,否则它将永远丢失。为了保留异常堆栈,必须向新异常的构造函数提供原始异常对象。
- 仅在需要时将已检查的异常转换为未检查的异常。包装异常时,可以包装已检查的异常并抛出未检查的异常。这在某些情况下很有用,特别是当意图中止当前正在执行的线程时。但是,在其他情况下,这可能会引起一些痛苦,因为不会执行编译器检查。因此,将已检查的异常改编为未检查的异常并不是盲目的。
回答by Colin Hebert
You should handle the method as soon as possible, but it must make sense. If the exception doesn't make sense to be thrown by your method, but you can't handle it, wrap it into another exception and throw this new exception.
您应该尽快处理该方法,但它必须有意义。如果您的方法抛出异常没有意义,但您无法处理它,请将其包装到另一个异常中并抛出这个新异常。
A bad practice about exception is to catch them all (it's not pokemon, it's java !) so avoid catch(Exception e)
or worse catch(Throwable t)
.
关于异常的一个不好的做法是将它们全部捕获(它不是 pokemon,它是 java !)所以避免catch(Exception e)
或更糟catch(Throwable t)
。