处理灾难性异常
Posted
技术标签:
【中文标题】处理灾难性异常【英文标题】:dealing with catastrophic exceptions 【发布时间】:2012-11-10 16:49:38 【问题描述】:我在一本 C# 介绍性书籍中读到,如果您不知道如何处理异常,则不应捕获异常。想到在 Java 中编程时的那一点建议,我有时发现我不知道如何处理异常,但我不得不捕捉它或“渗透它”以避免编译错误。我宁愿不要在调用树中一直使用 throws
子句来混淆方法,所以我经常求助于将异常“转换”为 RuntimeException
,如下所示。将throws
子句添加到许多方法中以处理没有真正“处理”(正确处理)的异常似乎冗长且分散注意力。以下是不好的风格吗?如果是,有什么更好的方法来处理这个问题?
try
thread.join();
catch (InterruptedException e)
Console.printwriter.format("%s\n", e.printStackTrace());
throw new RuntimeException();
编辑:除了混乱之外,渗透异常还有另一个问题:在代码修订之后,您可能最终会得到一些不必要的 throws
子句。我知道清除它们的唯一方法是反复试验:删除它们并查看编译器是否抱怨。显然,如果您想保持代码干净,这很烦人。
【问题讨论】:
处理它的更好方法是编写代码,使您不必不断抛出异常。 同意。我一般不会声明很多新定义的异常。我主要关心第三方和Java异常。 @user1394965 是的,您的代码不应“不断地”抛出异常。但这与是否有可能例外是完全不同的问题。任何涉及 IO 操作或阻塞方法的事情都可能充满不可避免的异常处理。 Wrapping a checked exception into an unchecked exception in Java? 的可能重复项 【参考方案1】:在 Java 中,已检查异常和未检查异常之间的区别是 somewhat controversial。
如果你控制界面,在签名中添加 throws 子句通常是最好的方法。
如果您处于无法处理异常的情况,但由于检查了异常签名而不允许让它冒泡,那么将异常包装成您可以重新抛出的异常(通常是 RuntimeException)是常见的做法。
在许多情况下,您可能希望使用另一个检查异常,例如 IOException 或 SQLException。但这并不总是一种选择。
但在您的示例中,将原始异常包含为“原因”:
throw new RuntimeException(e);
这也可能消除对日志记录的需要(因为这也可以推迟到可以处理异常的人,并且所有信息仍然存在)。
【讨论】:
【参考方案2】:我喜欢 Joshua Bloch 的 Effective Java 2nd edition 中的建议 - Throw exceptions appropriate to the abstraction(第 61 条)。
也就是说,当遇到一系列您希望从方法中“渗透”出来的异常时,请考虑是否应该用对您的方法更有意义的东西重新包装这些异常。
这种方法通常具有将几个较低级别的异常组合成一个较高级别的异常的令人愉悦的副作用。
【讨论】:
这是在简化抛出异常的好处与发明数百个会使代码库膨胀的自定义异常之间取得平衡。【参考方案3】:如果您不知道如何处理异常,则不应捕获它。因此,您的方法现在将抛出一个异常,因此如果它不是运行时异常,它应该有一个 throws 子句。他们没有任何问题。
【讨论】:
@broiyan 是的,我是在你的问题的背景下回答的,你正在处理一个正常的异常。【参考方案4】:良好的编程实践告诉您应该对调用者隐藏对象的内部状态,至少对我而言,这也包括异常。您应该看到该异常对您来说意味着什么,并向您的类的调用者返回一个表示该含义的异常。
如果框架已经提供了具有该含义的异常,例如 IllegalArgumentException,您应该实例化一个新对象,给它一个字符串,很好地描述发生的事情并封装发生的异常,类似于 new IllegalArgumentException( "论证 X 是无效的,因为 ...", e); 如果框架对您的问题没有很好的描述性异常,您应该创建自己的一组异常。我通常为该项目/包创建一个通用异常(它将扩展 Exception 或 RuntimeException)并从中派生异常。 例如,我最近创建了一个通用存储库项目,以便在我们的服务和应用程序模块中重用以访问数据库。因为我想从我用来访问数据库的东西中抽象出调用程序,甚至从异常中抽象出来,所以我最终创建了一些异常来封装 JPA Hibernate 异常。我这里没有代码,但它类似于:
// implementation package
public abstract class GenericRepository<K, E extends<K>> implements IRepository<K, E>
// constructors
public final void add(E entity)
// some code
try
// code that can throw exceptions
catch (EntityExistsException e)
// some code
throw new EntityAlreadyExistsException(e);
catch(RuntimeException e)
// some code
throw new GenericRepositoryException("An unexpected exception occurred while manipulating the database", e);
// some other code
// exception package
public final class EntityAlreadyExistsException extends GenericRepositoryException
public static final String GENERICMESSAGE = "The entity already exists on the table";
public EntityAlreadyExistsException(Throwable cause)
super(GENERICMESSAGE, cause);
// other constructors
【讨论】:
以上是关于处理灾难性异常的主要内容,如果未能解决你的问题,请参考以下文章
201671010127 2016-2017-8 初识异常处理
Tableau server日常维护 17处理Tableau server 灾难性恢复
如何使用 vm、磁盘和本地文件副本正确处理 Azure 云的灾难恢复
Windows Phone 8.1 MediaElement 灾难性故障(HRESULT 异常:0x8000FFFF (E_UNEXPECTED))