Java 如何以安全且可读的方式处理我知道永远不会抛出的 IOException?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1045829/
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
How can I handle an IOException which I know can never be thrown, in a safe and readable manner?
提问by Ethan Heilman
"The major difference between a thing that might go wrong and a thing that cannot possibly go wrong is that when a thing that cannot possibly go wrong goes wrong it usually turns out to be impossible to get at or repair." -Douglas Adams
“可能出错的事情和不可能出错的事情之间的主要区别在于,当不可能出错的事情出错时,结果通常是无法解决或修复的。” -道格拉斯·亚当斯
I have an class FileItems. FileItems constructor takes a file, and throws an exception (FileNotFoundException) if the file doesn't exist. Other methods of that class also involve file operations and thus have the ability throw the FileNotFoundException. I would like to find a better solution. A solution which doesn't require that other programmers handle all of these extremely unlikely FileNotFoundExceptions.
我有一个类 FileItems。FileItems 构造函数接受一个文件,如果文件不存在则抛出异常 (FileNotFoundException)。该类的其他方法也涉及文件操作,因此具有抛出 FileNotFoundException 的能力。我想找到一个更好的解决方案。一个不需要其他程序员处理所有这些极不可能的 FileNotFoundExceptions 的解决方案。
The facts of the matter:
事情的事实:
- The file has been checked to exist but the extremely unlikely possibility exists that through some major fault of reality the file might be deleted before this method is called.
- Since the probability of 1 happening is extremely unlike and unrecoverable, I would prefer to define an unchecked exception.
- The file is has already been found to exist, forcing other programmers to write code and catch the checked FileNotFoundException seems tedious and useless. The program should just fail completely at that point. For example there is always the chance that a computer may catch fire, but no one is insane enough to force other programmers to handle that as a checked exception.
- I run into this sort of Exception issue from time to time, and defining custom unchecked exceptions each time I encounter this problem (my old solution) is tiresome and adds to code-bloat.
- 该文件已被检查存在,但极不可能存在的可能性是,由于现实中的某些重大故障,在调用此方法之前该文件可能会被删除。
- 由于 1 发生的概率极其不同且不可恢复,因此我更愿意定义一个未经检查的异常。
- 该文件已被发现存在,迫使其他程序员编写代码并捕获已检查的 FileNotFoundException 似乎乏味且无用。该程序应该在那时完全失败。例如,计算机总是有可能着火,但没有人会疯狂到强迫其他程序员将其作为已检查异常来处理。
- 我不时遇到这种异常问题,每次遇到这个问题(我的旧解决方案)时定义自定义的未检查异常很烦人,并且会增加代码膨胀。
The code currently looks like this
代码目前看起来像这样
public Iterator getFileItemsIterator() {
try{
Scanner sc = new Scanner(this.fileWhichIsKnowToExist);
return new specialFileItemsIterator(sc);
} catch (FileNotFoundException e){ //can never happen}
return null;
}
How can I do this better, without defining a custom unchecked FileNotFoundException? Is there some way to cast a checkedException to an uncheckException?
如何在不定义自定义的未检查 FileNotFoundException 的情况下做得更好?有什么方法可以将checkedException 转换为uncheckException?
采纳答案by Henning
The usual pattern to deal with this is exception chaining. You just wrap the FileNotFoundException in a RuntimeException:
处理此问题的常用模式是异常链接。您只需将 FileNotFoundException 包装在 RuntimeException 中:
catch(FileNotFoundException e) {
throw new RuntimeException(e);
}
This pattern is not only applicable when an Exception cannot occur in the specific situation (such as yours), but also when you have no means or intention to really handle the exception (such as a database link failure).
这种模式不仅适用于在特定情况下(例如您的)无法发生异常的情况,也适用于您没有办法或意图真正处理异常(例如数据库链接失败)的情况。
Edit: Beware of this similar-looking anti-pattern, which I have seen in the wild far too often:
编辑:当心这种看起来很相似的反模式,我在野外经常看到它:
catch(FileNotFoundException e) {
throw new RuntimeException(e.getMessage());
}
By doing this, you throw away all the important information in the original stacktrace, which will often make problems difficult to track down.
通过这样做,您会丢弃原始堆栈跟踪中的所有重要信息,这通常会使问题难以追踪。
Another edit:As Thorbj?rn Ravn Andersen correctly points out in his response, it doesn't hurt to state why you're chaining the exception, either in a comment or, even better, as the exception message:
另一个编辑:正如 Thorbj?rn Ravn Andersen 在他的回应中正确指出的那样,在评论中或者更好地作为异常消息说明为什么要链接异常并没有什么坏处:
catch(FileNotFoundException e) {
throw new RuntimeException(
"This should never happen, I know this file exists", e);
}
回答by Emil H
You can convert a checked exception to an unchecked by nestling it inside a RuntimException. If the exception is caught higher up in the stack and outputted using printStackTrace(), the stack for the original exception will be displayed as well.
您可以通过将已检查异常嵌套在 RuntimException 中将其转换为未检查异常。如果异常在堆栈中的更高位置被捕获并使用 printStackTrace() 输出,则原始异常的堆栈也将显示。
try {
// code...
}
catch (IOException e) {
throw new RuntimeException(e);
}
This is a good solution, that you shouldn't hesitate to use in these situations.
这是一个很好的解决方案,在这些情况下您应该毫不犹豫地使用它。
回答by Yishai
If your position is that this is so unlikely and should just end the program, use an existing runtime exception, even RuntimeException itself (if not IllegalStateException).
如果您的立场是这不太可能并且应该结束程序,请使用现有的运行时异常,甚至 RuntimeException 本身(如果不是 IllegalStateException)。
try {
....
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
回答by matt b
You are probably better off throwing the superclass and more generic exception IOException
at any point in your code which involves reading or writing from the file.
您可能最好IOException
在代码中涉及读取或写入文件的任何点抛出超类和更通用的异常。
The file may exist when your class's constructor runs, but that doesn't guarantee that:
当您的类的构造函数运行时,该文件可能存在,但这并不能保证:
- It exists when methods are called
- It's writable/readable
- Another thread doesn't access it and somehow screw up your streams
- The resource doesn't go away in the middle of processing
- 它在方法被调用时存在
- 它是可写/可读的
- 另一个线程无法访问它并以某种方式搞砸了您的流
- 资源不会在处理过程中消失
etc.
等等。
Instead of reinventing the wheel, I would say just re-throw IOException wherever the JDK/java.io
classes you are using force you to do so.
与其重新发明轮子,我想说只需在java.io
您使用的 JDK/类强制您这样做的任何地方重新抛出 IOException 。
Also I for one hate classes that throw Exceptions from their constructor - I'd get rid of these if I were you.
另外,我讨厌从构造函数中抛出异常的类 - 如果我是你,我会摆脱这些。
回答by branchgabriel
I did a little googling and found this glob of code. It's a bit more flexible of an approach me thinks
我做了一点谷歌搜索,发现了这个代码团。我认为这是一种更灵活的方法
Compliments of this article
对这篇文章的赞美
class SomeOtherException extends Exception {}
public class TurnOffChecking {
private static Test monitor = new Test();
public static void main(String[] args) {
WrapCheckedException wce = new WrapCheckedException();
// You can call f() without a try block, and let
// RuntimeExceptions go out of the method:
wce.throwRuntimeException(3);
// Or you can choose to catch exceptions:
for(int i = 0; i < 4; i++)
try {
if(i < 3)
wce.throwRuntimeException(i);
else
throw new SomeOtherException();
} catch(SomeOtherException e) {
System.out.println("SomeOtherException: " + e);
} catch(RuntimeException re) {
try {
throw re.getCause();
} catch(FileNotFoundException e) {
System.out.println(
"FileNotFoundException: " + e);
} catch(IOException e) {
System.out.println("IOException: " + e);
} catch(Throwable e) {
System.out.println("Throwable: " + e);
}
}
monitor.expect(new String[] {
"FileNotFoundException: " +
"java.io.FileNotFoundException",
"IOException: java.io.IOException",
"Throwable: java.lang.RuntimeException: Where am I?",
"SomeOtherException: SomeOtherException"
});
}
} ///:~
回答by Thorbj?rn Ravn Andersen
Consider using the form
考虑使用表格
throw new RuntimeException("This should never happen", e);
instead. This allows you to convey meaning to the maintainer to follow you, both when reading the code but also SHOULD the exception happen to be thrown in some strange scenario.
反而。这允许您向维护者传达意义以跟随您,无论是在阅读代码时还是应该在某些奇怪的情况下抛出异常。
EDIT: This is also a good way to pass exceptions through a mechanism not expecting those exceptions. E.g. if you have a "get more rows from the database" iterator the Iterator interface does not allow to throw e.g. an FileNotFoundException so you can wrap it like this. In the code USING the iterator, you can then catch the runtimeexception and inspect the original excpetion with getCause(). VERY useful when going through legacy code paths.
编辑:这也是通过不期望这些异常的机制传递异常的好方法。例如,如果您有一个“从数据库中获取更多行”迭代器,则 Iterator 接口不允许抛出 FileNotFoundException 等,因此您可以像这样包装它。在使用迭代器的代码中,您可以捕获运行时异常并使用 getCause() 检查原始异常。在浏览遗留代码路径时非常有用。
回答by bohdan_trotsenko
I have experienced the same problem.
我也遇到过同样的问题。
In the simplest case you inform the user of the error and suggest to either to repeat or to cancel the operation.
在最简单的情况下,您将错误通知用户并建议重复或取消操作。
In general case, workflow is a sequence of actions (I/O including), where each action "assumes" that the previous one had succeeded.
在一般情况下,工作流是一系列动作(包括 I/O),其中每个动作“假设”前一个动作已经成功。
The approach I have chosen is to create a list of 'roll-back' actions. If the workflow succeeds, they are ignored. If an exception occurs, I execute rollbacks and present an exception to the user.
我选择的方法是创建一个“回滚”操作列表。如果工作流成功,它们将被忽略。如果发生异常,我执行回滚并向用户呈现异常。
This:
这个:
- keeps integrity of data
- allows to code much easier
- 保持数据的完整性
- 允许更容易地编码
Typical function is like:
典型的功能是这样的:
returntype func(blah-blah-blah, Transaction tr)
{
// IO example
Stream s = null;
try
{
s = new FileStream(filename);
tr.AddRollback(File.Delete(filename));
}
finally
{
if (s != null)
s.close();
}
}
Typical usage is:
典型用法是:
Transaction tr = new Transaction();
try
{
DoAction1(blah1, tr);
DoAction2(blah2, tr);
//...
}
catch (Exception ex)
{
tr.ExecuteRollbacks();
// queue the exception message to the user along with a command to repeat all the actions above
}
This is a littlebit trickier in real-world, because
这是一个有点现实世界有点棘手,因为
- sometimes it is necessary to obtain a bunch of locks before execution actions
- rollback code should be exception-silent itself (for instance)
- 有时需要在执行动作之前获得一堆锁
- 回滚代码本身应该是异常静默的(例如)
But I have already get used to this approach, now my applications are more stable.
但是我已经习惯了这种方法,现在我的应用程序更加稳定。
回答by Christopher Perry
Don't just tank your entire application with a RuntimeException
when you encounter an unlikely error condition. RuntimeException
should be reserved for programmer errors, and IOException
is most likely not caused by programmer error.
RuntimeException
当您遇到不太可能出现的错误情况时,不要只是用 a 来处理整个应用程序。RuntimeException
应该是为程序员错误保留的,并且IOException
很可能不是由程序员错误引起的。
Instead, encapsulate the lower level exception in a higher level exception and rethrow. Then handle the higher level exception up the call chain.
相反,将较低级别的异常封装在较高级别的异常中并重新抛出。然后在调用链上处理更高级别的异常。
For example:
例如:
class SomeClass {
public void doActions() {
try {
someAction();
} catch (HigherLevelException e) {
notifyUser();
}
someOtherUnrelatedAction();
}
public void someAction() throws HigherLevelException {
try {
// user lower-level abstraction to do our bidding
} catch(LowerLevelException e) {
throw new HigherLevelException(e);
}
}
public void someOtherUnrelatedAction() {
// does stuff
}
}
Most likely the call stack that threw the exception was performing some task in your application. Instead of force crashing your entire application with a RuntimeException
figure out what to do when the problem occurs during that task. For example, were you trying to save a file? Don't crash, instead notify the user there was an issue.
引发异常的调用堆栈很可能正在您的应用程序中执行某些任务。而不是强制使整个应用程序崩溃,而是RuntimeException
弄清楚在该任务期间出现问题时该怎么做。例如,您是否尝试保存文件?不要崩溃,而是通知用户有问题。