在为我的项目决定异常处理结构时,我一直陷入困境。
假设您有一个例子:
public abstract class Data {
public abstract String read();
}
两个子类FileData,它从一些指定的文件读取你的数据,以及StaticData,它只返回一些预定义的常量数据。
现在,在读取文件时,可能会在FileData中抛出IOException,但StaticData永远不会抛出。大多数样式指南建议将Exception传播到调用堆栈,直到有足够的上下文可用于有效处理它。
但我真的不想在抽象的read()方法中添加throws子句。为什么?因为Data和使用它的复杂机器对文件一无所知,所以它只知道数据。此外,可能还有其他数据子类(以及更多)从不抛出异常并完美地传递数据。
另一方面,IOException是必要的,因为如果磁盘不可读(或某些此类),则必须抛出错误 。因此,我看到的唯一出路就是捕获IOException并在其位置抛出一些RuntimeException。
这是正确的理念吗?
答案 0 :(得分:26)
你是对的。
异常应该与使用的抽象级别相同。这就是为什么java 1.4 Throwable支持异常链接的原因。没有必要为使用数据库的服务或“存储”不可知的服务抛出FileNotFoundException。
可能是这样的:
public abstract class Data {
public abstract String read() throws DataUnavailableException;
}
class DataFile extends Data {
public String read() throws DataUnavailableException {
if( !this.file.exits() ) {
throw new DataUnavailableException( "Cannot read from ", file );
}
try {
....
} catch( IOException ioe ) {
throw new DataUnavailableException( ioe );
} finally {
...
}
}
class DataMemory extends Data {
public String read() {
// Everything is performed in memory. No exception expected.
}
}
class DataWebService extends Data {
public string read() throws DataUnavailableException {
// connect to some internet service
try {
...
} catch( UnknownHostException uhe ) {
throw new DataUnavailableException( uhe );
}
}
}
请记住,如果您考虑继承编程,则应仔细设计特定方案并使用这些方案测试实现。显然,如果编写通用库代码更难,因为你不知道如何使用它。但大多数情况下应用程序都受限于特定域。
您的新例外应该是运行时还是已检查?这取决于,一般规则是抛出运行时编程错误并检查可恢复的条件。
如果通过正确编程可以避免异常(例如NullPointerException或IndexOutOfBounds),请使用Runtime
如果异常是由于某些外部资源失控程序员(例如网络停机)而且有一些事情可以做(可以在5分钟内显示重试消息)然后检查异常应该使用。
如果异常失控程序员,但可以完成NOTHING,则可以使用RuntimeException。例如,您应该写入文件,但文件已被删除,您无法重新创建它或重新尝试该程序应该失败(您无法做任何事情),最有可能是运行时。
请参阅Effective Java中的这两项:
我希望这会有所帮助。
答案 1 :(得分:7)
如果你没有明确说明read()
可以抛出异常,那么你会给开发人员带来惊喜。
在您的特定情况下,我会捕获基础异常并将其重新抛出为新的异常类DataException
或DataReadException
。
答案 2 :(得分:4)
将IOException
包装在适合“Data
”类的异常类型中。事实是read
方法不总是能够提供数据,它应该指出原因。包装异常可能会扩展RuntimeException
,因此不需要声明(尽管应该对其进行适当的记录)。
答案 3 :(得分:3)
使用运行时异常,结合顶部爆炸性的全能。一开始它有点吓人,但是一旦你习惯它就会变得更好。
在我的Web应用程序中,抛出的所有内容都是Runtime。几乎没有“throws”子句,我只能在我真正能够(或想要)处理异常的地方使用catchblock。在最高级别,有一个捕获Throwable,它会呈现一个技术错误页面,并写入一个日志条目。
Log4J邮件程序向我发送日志条目和前面的10个日志条目。所以当客户打电话时,我通常已经知道存在问题。
通过适当的(单元)测试和清洁编程,增加的清洁度和可读性可以随时超过检查异常的丢失。
答案 4 :(得分:0)
你应在抽象read()方法上声明某种异常。抽象类有效地声明了一个接口 - 您已经从两个具体的子类中了解到,由于异常条件,实现可能无法成功返回。
因此声明在抽象Data.read()方法中抛出一些异常是完全正确的。不要试图简单地声明它抛出IOException,因为它不应该绑定到特定的实现(否则你必须声明它也可能抛出SQLException,以防你决定拥有一个数据库 - 读取子类,SAXException,以防您有基于XML的阅读器(使用SAX),等等)。您需要一个自己的自定义异常,在抽象级别上充分捕获它 - 类似于上面推荐的DataException,或者如果有意义,可能会重用同一个包中的现有自定义异常。