【问题标题】:What is the Best practice for try catch blocks to create clean code? [duplicate]try catch 块创建干净代码的最佳实践是什么? [复制]
【发布时间】:2011-08-03 17:21:41
【问题描述】:

可能重复:
Best practices for exception management in JAVA or C#

我今天早些时候在 stackoverflow 上阅读了a question,它让我思考处理异常的最佳实践是什么。

所以,我的问题是最佳实践来处理异常以生成干净和高质量的代码

这是我的代码,我认为它很简单,但如果我错了或不清楚,请告诉我! 我试图在方法中牢记可测试性和相同的抽象级别。

欢迎提出任何建设性意见。 :)

import java.awt.Point;
import java.io.Closeable;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;

/**
 * <p>This is a dummy code.</p>
 * The aim is present the best practice on exception separation and handling.
 */
public class ExceptionHandlingDemo {
    // System.out is not a good practice. Using logger is good for testing too (can be checked if the expected error messages are produced).
    private Logger logger = LoggerFactory.getLogger(ExceptionHandlingDemo.class);

    // instance of cannot work with List<Point>
    private interface PointList extends List<Point> {}

    /**
     * The method that loads a list of points from a file.
     * @param path - The path and the name of the file to be loaded.
     * Precondition: path cannot be {@code null}. In such case {@link NullPointerException} will be thrown. 
     * Postcondition: if the file don't exist, some IOException occurs or the file doesn't contain the list the returned object is {@code null}.
     * */
    /* (Google throws NullpointerExceptio) since it is not forbidden for the developers to throw it. I know this is arguable but this is out of topic for now. */
    public List<Point> loadPointList(final String path) {
        Preconditions.checkNotNull(path, "The path of the file cannot be null");

        List<Point> pointListToReturn = null;
        ObjectInputStream in = null;

        try {
            in = openObjectInputStream(path);
            pointListToReturn = readPointList(in);
        } catch (final Throwable throwable) {
            handleException(throwable);
        } finally {
            close(in);
        }

        return pointListToReturn;
    }

    /*======== Private helper methods by now ========*/

    private ObjectInputStream openObjectInputStream(final String filename) throws FileNotFoundException, IOException {
        return new ObjectInputStream(new FileInputStream(filename));
    }

    private List<Point> readPointList(final ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
        final Object object = objectInputStream.readObject();

        List<Point> ret = null;

        if (object instanceof PointList) {
            ret = (PointList) object;
        }
        return ret;
    }

    private void handleException(final Throwable throwable) {
        // I don't know the best practice here ...
        logger.error(throwable.toString());
    }

    private void close(final Closeable closeable) { 
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                logger.error("Failed closing: %s", closeable);
            }
        }
    }

    /*======== Getters and setters by now. ========*/

    // ...

    /**
     * @param args
     */
    public static void main(String[] args) {
        ExceptionHandlingDemo test = new ExceptionHandlingDemo();
        test.loadPointList("test-filename.ext");
    }
}

已编辑:

我要避免的是一个接一个地写很多 catch case...

【问题讨论】:

  • @xscape:我一定会读的,谢谢。
  • 作为旁注,我建议您标记您的私有帮助方法static,因为它们就是这样:它们仅根据这些输入获取输入并产生输出。当我在你的方法声明中没有看到 static 关键字时,我不得不检查这些方法是否真的没有使用任何状态,这很糟糕,因为代码迫使我专注于一些根本不重要的东西来阅读它。这个问题涉及:stackoverflow.com/questions/3346764/…
  • @Bruno Reis:谢谢,我会看的。

标签: java exception-handling


【解决方案1】:

乍一看的一些建议:

  1. 您不应捕获Throwable,而应尽可能捕获特定异常。捕获Throwable 的麻烦在于,它将包括Error 类,如OutOfMemoryError 等。你想让它们通过(它们没有被检查是有原因的)。
  2. 当您记录异常时总是传递异常而不仅仅是它的toString()。如果没有堆栈跟踪,很难诊断问题。
  3. 您可能不需要通用异常处理方法。

所以在你捕获异常的地方你想要这样的东西:

} catch (IOException e) {
    logger.error("some relevant message", e);
    // now handle the exception case
}

如果可能,消息应包含一些上下文信息。当有人在寻找日志时,任何可能有助于追踪问题的方法。

【讨论】:

  • 我想创建一个通用的异常处理方法。你能解释一下为什么这是不好的做法吗?我的意思是我可以看到这样我可以吞下一些不应该这样的异常,但直到现在我还不知道有什么更好的方法来保持业务逻辑的整洁。所以我不想看到的是一个接一个的抓案......
  • 如果你有一个通用的代码块,你想为某些异常重用,那么一个通用的方法就可以了。但是,此时您在该方法中只有一行,因此在进行调用的地方代码并不少。如果您保留它,您至少应该添加一个方法变量来传递相关消息。不过,我认为在您的业务逻辑中拥有特定的日志记录和特定的异常处理并不是不干净的。如果您发现自己重复相同的代码块,那么我会将其抽象出来(甚至作为一般规则)。
【解决方案2】:

对于我不想打扰的已检查异常,我总是使用 org.apache.commons.lang.UnhandledException。

例如

/**
 * Calls Thread.sleep(millis)
 */
public static void threadSleep(final long millis) {
    try {
        Thread.sleep(millis);
    } catch (final InterruptedException e) {
        throw new UnhandledException(e);
    }
}

【讨论】:

    【解决方案3】:

    始终尽可能捕获最具体的异常——否则永远不会捕获可抛出的异常。

    对我来说最重要的是,你永远不会有一个空的 catch 块——其中一个可能需要花费大量时间来查找 try 中的某些内容是否真的引发了异常。

    我个人喜欢尽快删除已检查的异常,并尽可能用前置/后置条件检查替换它们。在较小程度上与未经检查的异常相同-但是未经检查的异常实际上是指示程序员错误的一种很好的方法,例如参数检查以确保对象状态(尽管断言可能更好)

    【讨论】:

    • +1 表示永远不会有空的 catch 块 - 并运行 PMD/FindBugs 来获取它
    • 如果记录器在记录异常时抛出异常怎么办?
    • 希望它足够聪明,可以向 stderr 报告它的异常,并将您的异常包装为原因,这样至少您有一些东西。
    【解决方案4】:

    您可以选择一些选项。

    1. 使用异常的最重要优势之一是您可以拥有适用于所有情况的唯一异常处理程序。当您编写代码时,您应该首先考虑功能,然后才考虑异常处理。因此,对于未检查的异常,您可能会在某些地方完全跳过 try/catch,并将您的方法声明为 throws SomeCheckedException 以检查已检查的异常。这允许您拥有最少数量的异常处理程序。

    2. 如果您不知道如何处理异常,只需重新抛出它。

    3. 如果您捕获已检查的异常,但您不希望使用您的代码的客户端必须处理异常,您可以将已检查的异常更新为未检查的异常。 (Hibernate 通过这种方式处理异常。它们捕获已检查的 sql 异常并抛出未检查的异常而不是这种方式)
    4. 如果前面的推荐对你不方便,那么你也可以选择一些选项:
      • 您可以只添加日志记录并重新抛出相同的异常
      • 您可以通过使用 initCause() 方法抛出新异常来使用异常链
      • 您可能认为调用您的代码的代码不应该知道有关原始异常的任何信息,您可以通过创建新异常或使用 fillInStackTrace() 方法将其归档。
    5. 关于捕捉 Throwable。是的,在你不应该抓住它的方法中。但作为一项规则,使用您的程序的客户端根本不需要看到异常,因此,您可能会在链中顶部的某个异常处理程序中捕获 Throwable。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-28
      • 1970-01-01
      • 2013-09-06
      • 2020-10-19
      • 2018-05-29
      • 2011-03-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多