60

我最近接受了面试,面试官要我做一个技术测试来看看我的知识。在我完成它之后,他给了我关于我是如何做到的反馈,这是我没想到的,我很感激,因为如果他们不想雇用你,很少有面试官会这样做。

他告诉我,他认为我的代码不好的一件事是,我在编写的每个方法中都使用了多个 try-catch 块。这引起了我的注意,因为我觉得它很有趣。

我认为目前我应该制作 try-catch 块,其中存在语义上可区分的代码块,该代码块具有一个或多个可以引发需要捕获的异常的方法。我遵循的唯一例外是,如果两个方法抛出相同的异常类型,我最好将它们放在不同的 try-catch 块中,以便在调试时清楚地区分抛出异常的位置和原因。

这与面试官希望我做的有很大不同。那么,每种方法只使用一个 try-catch 块是一种已知的良好做法吗?如果这是一种已知的良好做法,这样做有什么好处?


请注意,我想知道这是否是一种已知的良好做法。即,如果大多数程序员/作者都同意这是一个很好的做法。

4

7 回答 7

42

对我来说,两个 try-catch 块使大多数方法都太长了。如果该方法正在做很多事情,它会混淆意图。

准确地说,使用两个 try-catch 块,它至少做了四件事

  • 主流的两种情况(两个尝试块)
  • 错误处理的两种情况(catch 块)

我宁愿从每个 try-catch 块中制作简短而清晰的方法——比如

private getHostNameFromConfigFile(String configFile, String defaultHostName) {
    try {
        BufferedReader reader = new BufferedReader(new FileReader(configFile));
        return reader.readLine();
    } catch (IOException e) {
        return defaultHostName;
    }
}
public Collection<String> readServerHostnames(File mainServerConfigFile, File  backupServerConfigFile) {
    String mainServerHostname=getHostNameFromConfigFile(mainServerConfigFile,"default- server.example.org");
    String backupServerHostName=getHostNameFromConfigFile(backupServerConfigFile,"default- server.example.ru")
    return Arrays.asList(mainServerHostname,backupServerHostName);
}

Robert C. Martin 在“清洁代码”中将其提升到一个新的水平,建议:

如果关键字“try”存在于函数中,它应该是函数中的第一个单词,并且在 catch/finally 块之后应该没有任何内容。

我肯定会用两个单独的 try/catch 块将方法重构为更小的方法。

于 2013-09-13T20:48:32.770 回答
34

我想说,如果您发现自己包装了两个单独的代码块,则try/catch应该考虑将这些块重构为单独的方法。如果这是您在面试中使用的模式,那么您可能误解了面试官。

try/catch如果算法需要,使用两个块是完全可以的。我经常try/catch在 catch 块中使用 new 来确保安全清理,因此不可能进行一揽子声明。

于 2013-09-13T11:00:27.887 回答
13

为了回答您的问题,当我们谈论实际上在代码中应用大量优化的现代 JVM 时,当您编写一些效率低下的代码时,JVM 将自动引入优化。

请参考(Java:输入/使用“try-catch”块的开销?)中的答案。

因此,良好的实践并不重要。

就个人而言,我认为不得将任何内容不必要地封装在 a try-catch, static, synchronizedetc 块中。

让我们让我们的代码对那些将要从事此工作的人更具可读性。如果捕获到异常,最好明确指出是哪段代码抛出了它。

读者无需猜测,这就是为什么 JVM 很聪明,可以随心所欲地编写,让它对人类更好,而 JVM 负责优化部分。

编辑:我读了很多书,但我没有找到任何地方说一个大的尝试比多个小的好。

此外,开发者社区中的许多人认为相反。

于 2013-09-13T11:01:34.743 回答
8

我尽量避免在 catch 块中重复。如果方法中的所有异常都在 catch 块中得到相同的处理,那么继续将它们全部捕获。如果你需要对它们做不同的事情,那么分别捕获它们。

例如,这里我们可以一起捕获所有异常,因为任何类型的异常都意味着整个方法失败:

public PasswordAuthentication readAuthenticationDetails(File authenticationFile) {
    try {
        BufferedReader reader = new BufferedReader(new FileReader(authenticationFile));
        String username = reader.readLine();
        String password = reader.readLine();
        return new PasswordAuthentication(username, password.toCharArray());
    } catch (IOException e) {
        return null;
    }
}

而在这里,我们对每组调用都有不同的回退行为,因此我们分别捕获:

public Collection<String> readServerHostnames(File mainServerConfigFile, File backupServerConfigFile) {
    String mainServerHostname;
    try {
        BufferedReader reader = new BufferedReader(new FileReader(mainServerConfigFile));
        mainServerHostname = reader.readLine();
    } catch (IOException e) {
        mainServerHostname = "default-server.example.org";
    }

    String backupServerHostname;
    try {
        BufferedReader reader = new BufferedReader(new FileReader(backupServerConfigFile));
        backupServerHostname = reader.readLine();
    } catch (IOException e) {
        backupServerHostname = "default-server.example.ru";
    }

    return Arrays.asList(mainServerHostname, backupServerHostname);
}

(此代码的存在纯粹是为了说明关于捕获异常的这一点;我请求您忽略它在其他方面非常可怕的事实)

于 2013-09-13T17:19:22.617 回答
5

至于我,更清楚的是只有一个try-catch块将所有“危险”代码包装在一个方法中。关于当两行抛出相同的异常时该怪谁,您将始终拥有堆栈跟踪。

此外,try-catch在一个方法中包含多个通常意味着包含多个return行(这也可能使代码执行难以一目了然),因为如果第一个出现问题try-catch,那么将没有意义继续运行其余的代码。

在这里您可以找到一些“标准”最佳实践,以防万一您发现它们有用。-

http://howtodoinjava.com/2013/04/04/java-exception-handling-best-practices/

于 2013-09-13T11:02:56.080 回答
4

这是另一件事经常引发 Java-flamewar ... ;-)

基本上,对于性能而言,只抛出异常。所以使用几个try-catch块根本不应该影响性能。在某些人看来,以这种方式编写代码会混淆代码,甚至不记得“干净的代码”,在另一些人看来,最好try只用于实际上可能引发任何异常的行。

由您决定(或团队大会)。

于 2013-09-13T10:58:03.663 回答
4

考虑代码的上下文也很重要。如果您正在编写具有大量 IO 的代码,那么您可能需要知道代码的哪些部分失败了。我还没有在任何地方看到 try...catch 旨在让您有机会从问题中恢复过来。

因此,如果您在读取一个文件时遇到 IO 异常,您可能需要重试读取。写作也一样。但是,如果您进行了一次大尝试...catch,您将不知道该重试哪个。

于 2013-09-13T16:01:01.393 回答