Java 捕获各种特定异常以及一个通用的捕获所有异常是一种好的做法吗?

Java 捕获各种特定异常以及一个通用的捕获所有异常是一种好的做法吗?,java,exception,exception-handling,Java,Exception,Exception Handling,一般问题 在编写服务方法时,抛出许多异常与将它们抽象为通用的ServiceException相比,有其优点和缺点。如果抛出了许多异常,那么您可以告诉客户机到底出了什么问题,以便客户机能够更明智地处理错误。如果您将它们抽象为一个服务异常(例如via),则您隐藏了出错的细节,这在某些情况下可能有用,但在其他情况下则有害 例如,通过抛出一个泛型DaoException,隐藏DAO类中出错的详细信息是很有用的。这样,客户机就不必知道您是在读取文件、web服务还是关系数据库。但假设您正在验证用户输入,并且

一般问题

在编写服务方法时,抛出许多异常与将它们抽象为通用的
ServiceException
相比,有其优点和缺点。如果抛出了许多异常,那么您可以告诉客户机到底出了什么问题,以便客户机能够更明智地处理错误。如果您将它们抽象为一个
服务异常
(例如via),则您隐藏了出错的细节,这在某些情况下可能有用,但在其他情况下则有害

例如,通过抛出一个泛型
DaoException
,隐藏DAO类中出错的详细信息是很有用的。这样,客户机就不必知道您是在读取文件、web服务还是关系数据库。但假设您正在验证用户输入,并且用户输入至少有四个方面可能出错。在后一种情况下,抛出四个不同的异常不是更明智吗?对于验证中出现错误的每件事,抛出一个异常

我的具体问题

在我当前的项目中,我正在研究一种服务方法,它将树状结构保存到关系数据库中。它必须验证树,并且树至少有四个方面可能是错误的。可能存在重复节点,或者节点可能缺少其必需的X、Y、Z字段。因此,服务方法抛出四种不同的异常是有意义的:
DuplicateNodeException
MissingXException
MissingYeException
MissingZeException

但是服务方法也使用DAO类,它通过JdbcTemplate抛出一个
DataAccessException
。我想知道如何处理此
DataAccessException
。将其包装为
ManagerException
,然后再包装为
ServiceException
,并将其作为
ServiceException
处理,这样做有意义吗?然后,我将混合两种方法:(a)抛出特定的、描述性的异常,(b)抛出通用的包装器异常。在这里混合使用这些方法是个好主意吗

我问这个问题的原因是,捕获这四个与验证相关的异常,然后再捕获一个通用的、不起眼的
ServiceException
,对我来说似乎有点奇怪。这看起来很奇怪,因为
ServiceException
是如此抽象,以至于无法一眼看出哪里出了问题,您必须检查日志并读取异常消息,或者在代码中导航调用层次结构。那么,我想验证一下,将这两种方法混合在一起确实是一种好的做法,或者是普通的,或者是明智的做法,因为直觉上我觉得这很奇怪。在核心Java中,两种异常处理方法都在同一个方法中使用,这有什么相似之处吗

可能的解决方案(根据Andreas的回答)

在我的服务类中处理这样的异常有意义吗?下面是一些伪代码,提供了一种可能的解决方案。这样,我就不会创建一个无用的、已检查的、无法描述的ServiceException,但我确实有一个针对未检查异常的全面捕获(通过在末尾捕获异常)。你对这个解决方案有什么想法

class HttpService {

    private Service service;

    // ...

    public HttpServiceResponse saveTree(Node root) {
        try {
            service.saveTree(root);  
        } catch (DuplicateNodeException e) {
            return HttpServiceResponse.failure(DUPLICATE_NODE_EXCEPTION);
        } catch (MissingXException e) {
            return HttpServiceResponse.failure(MISSING_X_EXCEPTION);
        } catch (MissingYException e) {
            return HttpServiceResponse.failure(MISSING_Y_EXCEPTION);
        } catch (MissingZException e) {
            return HttpServiceResponse.failure(MISSING_Z_EXCEPTION);
        } catch (Exception e) {
            return HttpServiceResponse.failure(INTERNAL_SERVER_ERROR);
        }
    }
}
大多数异常都应该被取消选中,因为它们实际上是不可操作的,也就是说,调用者除了传递它,或者记录它并使当前正在进行的任何操作失败之外,实在无能为力。只有可操作且需要调用方操作的异常才应该被检查,这是非常罕见的

如果所有异常都未选中,则意味着调用方可以使用一个catch all。由于服务层不再需要包装来自DAO层的异常,因此调用方可以捕获并处理特定的异常(如果需要)。

大多数异常都应该取消选中,因为它们不是真正可操作的,也就是说,调用方除了传递它之外,实际上什么也做不了,或者记录它并失败当前正在进行的任何操作。只有可操作且需要调用方操作的异常才应该被检查,这是非常罕见的


如果所有异常都未选中,则意味着调用方可以使用一个catch all。由于服务层不再需要包装来自例如DAO层的异常,调用方可以捕获并处理特定的异常(如果需要)。

有效Java的建议是抛出适合于抽象的异常。@AndyTurner谢谢,这是一个很好的建议。你投了反对票吗?如果是,为什么?我没有投反对票。虽然JDBC API抛出checked
SQLException
,但是
JdbcTemplate
包装了那些未选中的
DataAccessException
,所以调用方不需要专门声明和处理它们。无需使用
ServiceException
包装
DataAccessException
。如果服务执行其他抛出已检查异常的操作,则应使用特定异常类似地包装这些异常,而不是通用的
ServiceException
。调用者并不关心这些异常,因为调用者对它们无能为力,但不要将所有异常都包装成一个通用的
ServiceException
@tsolakp,但我希望调用者对它们采取行动,因为它们是可恢复的异常,所以将它们声明为选中是有意义的,不是吗?请参阅本文末尾的十大技巧:。高效Java的建议是抛出适合于抽象的异常。@AndyTurner谢谢,这是一个很好的建议。你投了反对票吗?如果是,为什么?我没有投反对票。虽然JDBC API抛出checked
SQLException
,但是
JdbcTemplate
包装了那些未选中的
DataAccessException
,所以调用方不需要专门声明和处理它们。有