我想在这里补充一下,因为我见过的几乎所有java代码中的异常处理都是不正确的。 IE。对于被忽略的异常,您最终会遇到非常难以调试的错误,或者同样糟糕的是,您会得到一个不告诉您任何信息的晦涩异常,因为盲目地遵循“捕获(异常)是不好的”,事情会变得更糟。
首先,了解异常是一种便于跨代码层返回错误信息的方式。现在,错误 1:一个层不仅仅是一个堆栈帧,一个层是具有明确职责的代码。如果您只是编写接口和实现代码只是因为,那么您有更好的东西要修复。
如果层设计得很好并且有特定的职责,那么错误信息在冒泡时具有不同的含义。
因此,这意味着当发生异常时,您有 2 个选项,但您需要了解您在层中的哪个位置:
A) 如果你在一个层的中间,并且你只是一个内部的、通常是私有的、帮助函数并且出现了问题:不要担心,让调用者接收异常。完全没问题,因为您没有业务背景并且
1)您没有忽略错误并且
2)调用者是你层的一部分,应该知道这可能发生,但你现在可能没有上下文来处理它。
或者...
B) 你是层的顶部边界,内部的立面。然后,如果你得到一个异常,默认应该是 CATCH ALL 并阻止任何特定的异常跨越到上层,这对调用者来说没有意义,或者更糟糕的是,你可能会改变并且调用者将依赖于一个实现细节,两者都会打破。
应用程序的优势在于层与层之间的解耦程度。在这里,您将作为一般规则停止一切并使用一般异常重新抛出错误,将信息转换为对上层更有意义的错误。
规则:层的所有入口点都应使用 CATCH ALL 保护,并翻译或处理所有错误。现在这种“已处理”仅发生 1% 的时间,大多数情况下您只需要(或可以)以正确的抽象返回错误。
不,我确信这很难理解。真实示例 ->
我有一个运行一些模拟的包。这些模拟在文本脚本中。有一个包可以编译这些脚本,还有一个通用的 utils 包,它只读取文本文件,当然还有基本的 java RTL。 UML 依赖是->
模拟器->编译器->utilsTextLoader->Java文件
1) 如果某个私有内部的 utils 加载器出现问题,并且我得到 FileNotFound、Permissions 或其他任何内容,那就让它过去吧。你无能为力。
2) 在边界处,在最初调用的 utilsTextLoader 函数中,您将遵循上述规则和 CATCH_ALL。编译器不关心发生了什么,它只需要现在是否加载了文件。因此,在捕获中,重新抛出一个新异常并将 FileNotFound 或其他任何内容转换为“无法读取文件 XXXX”。
3) 编译器现在将知道源没有加载。这就是它需要知道的一切。因此,如果我稍后将 utilsTestLoader 更改为从网络加载,编译器将不会更改。如果您放开 FileNotFound 并在以后进行更改,您将一无所获。
4) 循环重复:为文件调用底层的实际函数在收到异常时将不执行任何操作。所以它让它上升。
5) 当异常到达模拟器和编译器之间的层时,编译器再次 CATCHES_ALL,隐藏任何细节并抛出更具体的错误:“无法编译脚本 XXX”
6) 最后再循环一次,调用编译器的模拟器函数就放手了。
7) 最后的边界是用户。用户是一个 LAYER 并且都适用。 main 尝试 catches_ALL 并最终创建一个漂亮的对话框或页面并向用户“抛出”翻译错误。
所以用户看到了。
模拟器:致命错误无法启动模拟器
-编译器:无法编译脚本 FOO1
--TextLoader: 无法读取文件 foo1.scp
---trl: FileNotFound
比较:
a) 编译器:NullPointer 异常
b) 加载器:找不到文件
或
c) 什么都没有发生,因为一切都被忽略了!!!
当然,这假设在每次重新抛出时您都没有忘记设置原因异常。
好吧,我的 2cts。这个简单的规则多次救了我的命……
-啤酒