2009-02-10 136 views
4

Java中存在某些预定义的异常,如果抛出异常,则报告发生了严重的事情,并且您最好改进代码,而不是在catch块中捕获它们(如果我已正确理解它们) 。但我仍觉得在我有以下许多方案:在Java中捕获异常

} catch (IOException e) { 
    ... 
} catch (FileNotFoundException e) { 
    .... 
} 

,我还以为IOException异常和FileNotFoundException异常正是这类例外,这是我们不应该在一个catch块赶上。为什么人们这样做?像这样抓住他们会更好吗?无论如何,Java编译器会警告这类问题。

谢谢。

回答

14

不,没什么不对,赶上IOExceptionFileNotFoundException - 如果你真的可以处理那些例外。这是非常重要的一点 - 面对这个例外,你真的可以继续吗?有时你可以 - 例如,经常在服务器的顶层,因为一个请求失败并不意味着下一个请求不能继续。客户端应用程序不太常见,尽管它非常依赖于情况。尝试进行批量导入时无法读取文件?好吧,中止操作,但不一定关闭整个过程...

你不应该让他们圆这样,诚然 - 在FileNotFoundException将通过它从导出IOException被掩盖。幸运的是,编译器平坦化可以防止你这样做。

3

这样做是为了以不同方式处理不同类型的异常。 通常情况下,您首先需要捕获最细粒度的异常,如果将更多的异常放在catch块的开头,那么您将首先执行该代码,然后再打到finally块。

Jon是对的,捕获IOException的catch将捕获所有IOExceptions和IOException的任何子类型,并且由于FileNotFoundException是一种IOException类型,它将永远不会触及第二个catch。

5

您显示的订单,IOException之前被捕获FileNotFoundException是错误的。由于FileNotFoundException延伸了IOException,当引发FileNotFoundException时,将使用第一个处理程序,第二个处理程序为死代码。

我还没有尝试过,但如果编译的话我感到有点惊讶。我希望FindBugs这样的静态分析工具能够捕捉到这个错误。

至于你是否应该赶上FileNotFoundException,这取决于调用者。但是,我会说FileNotFoundException通常可以以有意义的方式恢复—提示另一个文件,尝试回退位置—而不是简单地记录错误或中止该过程。

+0

非常好点,漂亮的答案。 – hotshot309 2012-12-21 14:26:21

4

Java中有两种类型的异常,检查异常和未检查异常。

检查异常必须在catch块中处理。不这样做会导致编译器错误。 IOException是一个检查异常的例子,必须处理。实际上你在这里做什么取决于有问题的应用程序,但必须处理Exception以保持编译器的快乐。

未检查的异常不需要被捕获。从RuntimeException扩展的所有类均未选中。一个很好的例子是NullPointerException或ArrayIndexOutOfBoundsException。编译器不会强制你捕获这些异常,但是当你的程序运行时它们仍然可能发生,导致它崩溃。

为了记录,IOException可以像试图打开一个不存在的文件一样简单。这是处理这样的事情,并恢复正常(对话框向用户说文件不存在,并在打开的文件对话框重新出现什么的),而不是让程序崩溃是一个好主意。

3

作为Jon says,在许多情况下捕获这些例外情况很好。那种认为你不应该捕捉异常的事情像NullPointerException异常和ArrayIndexOutOfBoundsException异常,这些都表明代码中的错误。

Java有两种类型的异常:检查异常和未检查异常(那些从RuntimeException继承的异常)。

检查异常(如IOException)通常用于不可预知的情况,这些情况无法通过编写更好的代码来避免。它们被检查的事实意味着编译器迫使你编写代码来解释特殊情况的可能性。例如,你必须要考虑一个FileNotFoundException异常的可能性,因为你不能保证该文件将存在(你的程序运行时,有人可能将其移动)。由于网络连接丢失,可能会发生IOException。编译器强制你提供一个处理这些情况的策略,即使它只是通过允许异常传播堆栈以调用代码来处理来传递降级。

另一方面,未经检查的异常最适用于通过更改代码可以避免的事情。如果代码检查空引用的可能性,总是可以避免NullPointerException。同样,如果你对你的索引小心,你永远不会得到一个ArrayIndexOutOfBoundsException。编译器不会要求您处理这些场景,因为它们表示应该修复的错误。

3

我认为,IOException异常和FileNotFoundException异常正是这类例外

都能跟得上的,这些其实都是“其他”类型的异常,超越你的编程技能的那种。不管你编程多好,编译器和库都会让你“意识到”会发生什么。

想想这样的场景:

您创建的数据保存到临时文件夹的应用程序。

一切正常,你已经检查了文件夹存在,如果没有,你创建你的自我。

然后你写2 MB到临时文件夹。

突然,其他的系统进程,删除临时文件夹,你不能写下去了。

没有什么可以做的,以防止这种编程方式,在一些系统中可能会发生操作(在unix中,root用户可能会执行rm -rf/tmp,并且你无能为力。系统不会让其他进程删除正在使用的文件)

通过强制您检查代码中的这类异常,平台设计人员认为至少您意识到了这一点。

Jon is correct有时没有什么可以做这件事,大概采伐程序死掉之前,被认为是“处理异常”(手柄差肯定的,但至少处理)

try { 
    .... 
} catch(IOException ioe) { 
    logger.severe( 
     String.format("Got ioe while writting file %s. Data was acquired using id = %d, the message is: %s", 
      fileName, 
      idWhereDataCame, 
      ioe.getMessage())); 
    throw ioe; 
} 

另一件事你可以做的就是“链接”异常以适应抽象。

也许你的应用程序有一个GUI,向用户显示IOException并不意味着什么,或可能是一个security vulnerability。可以发送修改的消息。

try { 
    .... 
} catch(IOException ioe) { 
    throw new EndUserException("The operation you've requeste could not be completed, please contact your administrator" , ioe); 
}  

而且EndUserException可能某处GUI的对话消息被捕获并呈现给用户(而不是刚刚消失在他眼中的应用,而无需进一步的信息)。当然,你不能做任何事情来恢复这个IOException,但至少你死于风格:P

最后一个客户端代码,可以使用不同的实现,并不是所有的异常都是合理的。

例如,再次考虑第一种情况。同一个“操作”可以有三种“插件”服务来执行数据保存。

a) Write the data to a file. 
b) Or, write to a db 
c) Or write to a remote server. 

接口不应该抛出:

java.io.IOException 
java.sql.SQLException 

也不

java.net.UnknownHostException 

但不是像

my.application.DataNotSavedException 

和不同的实现会在正确处理异常水平,其转换为相应的抽象:

客户端代码:

DataSaver saver = DataServer.getSaverFor("someKeyIdString"); 

try { 
    saver.save(myData); 
} catch(DataNotSavedException dnse) { 

    // Oh well... . 
    ShowEndUserError("Data could not be saved due to : " dnse.getMessage()); 
} 

实现代码:

class ServerSaver implements DataSaver { 
.... 
    public void save(Data data) throws DataNotSavedException { 
     // Connect the remore server. 
     try { 
      Socket socket = new Socket(this.remoteServer, this.remotePort); 
      OuputStream out = socket.getOut.... 

      .... 
      .... 
     } catch (UnknownHostException uhe) { 
      // Oops.... 
      throw new DataNotSavedException(uhe); 
     } 
    } 
} 

FileSaver和DatabaseSaver会做同样的事情。

所有这些都是检查异常因为编译器让你检查它们。

当使用一个或另一个(选中/取消):here

还有其他两种:here

最后运行时的一个更简单的解释是:here

0

为了让这个想法横向一点:也许在另一个领域它更清晰

你怎么做,如果在fr你突然停下来。

停!

所以我们处理这个异常。

所以回到代码:

你会怎么做,如果你需要的文件不可用?

要么

  1. 有一个备份。编译为 资源,因为它是您的 计划的一部分。 我不是在开玩笑。
  2. IFF这是用户提供的文件:告诉用户;这是他们的文件。
  3. 因为您的软件系统被破坏 而中止程序并向用户发送消息。

这是我认为没有第四种选择。

我们的C#/ VC++兄弟选择未经检查的异常。许多“专家”认为检查异常是不好的:我认为生活是困难的,克服它。 检查的异常代表已知的故障模式:必须予以解决。 你的鱼骨图对于正常操作有一个直接的谎言,并为故障分支。检查的异常是预期的失败。

现在,一旦开始处理运行时异常,它会变得有趣。 Java程序可以正常运行,但功能无法正常运行。由此,我的意思是它们抛出空指针异常,数组边界错误,无效参数和堆空间不足。这使得增量交付非常可行。

(如果你做赶上运行时错误,记录它们,否则你永远不知道解决的事情)