在Java中,有没有一种方法可以确保多个方法在finally块中被调用?

5

我有一个try/finally代码块。我需要在finally块中执行许多方法。然而,这些方法中的每一个都可能抛出异常。有没有一种方法可以确保调用(或尝试调用)所有这些方法而不使用嵌套的finally块?

目前我做的事情相当丑陋:

protected void verifyTable() throws IOException {
    Configuration configuration = HBaseConfiguration.create();
    HTable hTable = null;                                               

    try {
        hTable = new HTable(configuration, segmentMatchTableName);      

        //...
        //various business logic here
        //...

    } finally {                         
        try {
            try {
                if(hTable!=null) {
                    hTable.close(); //This can throw an IOException
                }               
            } finally {
                try {
                    generalTableHelper.deleteTable(configuration, segmentMatchTableName); //This can throw an IOException
                } finally {
                    try {
                        generalTableHelper.deleteTable(configuration, wordMatchTableName); //This can throw an IOException
                    } finally {
                        generalTableHelper.deleteTable(configuration, haplotypeTableName); //This can throw an IOException      
                    }
                }
            }                               
        } finally {
            HConnectionManager.deleteConnection(configuration, true); //This can throw an IOException   
        }
    }               
}

有没有更优雅的方法来做这件事?


3
你可以将它们提取到一个清理方法中。 - Reimeus
Is there a way to ensure that all these methods are called (or attempted) **without nested finally blocks**? - Sam I am says Reinstate Monica
8个回答

2
如果使用的是Java 7,您可以考虑使用新的try-with-resources结构。您可能需要创建一些基本的AutoCloseable包装器来删除表格。

2
Java中正确处理资源管理的标准(工作)方法(该原则同样适用于其他语言)是:
Resource resource = acquire(resource);
try {
    use(resource);
} finally {
    resource.release();
}

或者使用当前版本的Java SE中的快捷方式(附带一些聪明的技巧):
try (Resource resource = acquire(resource)) {
    use(resource);
}

如Joe K指出的那样,您可能需要包装资源以符合Java语言所依赖的特定接口。
有两个资源,您只需将该习语应用两次。
Resource resource = acquire(resource);
try {
    SubResource sub = resource.acquire();
    try {
        use(sub);
    } finally {
        sub.release();
    }
} finally {
    resource.release();
}

在Java SE 7中:
try (
    Resource resource = acquire(resource);
    SubResource sub = resource.acquire()
) {
    use(resource, sub);
}

新语言特性的真正优势在于,资源处理通常在编写时就已经损坏了。

您可能会遇到更复杂的异常处理。例如,您不希望将低级别的异常(如 IOException)抛出到应用程序中 - 您可能希望将其包装在某个 RuntimeException 的子类型中。使用Java典型的冗长方式,可以使用“执行周围”习惯用法来分解它(请参见this excellent question)。从Java SE 8开始,还将有具有随机不同语义的更短语法。

with(new ResourceSubAction() { public void use(Resource resource, SubResource sub) {
    ... use resource, sub ...
}});

0
通常情况下,这是无法避免的。您需要多个 finally 块。
不过,我不想对您的具体代码发表评论,无论它是否是一个适当的设计。它看起来确实相当奇怪。

是的,如果您能看到业务逻辑,那么代码会更有意义。出于简洁起见,我忽略了它。 - sangfroid

0

我恐怕没有办法。关闭io资源时有类似的模式。例如,当关闭文件时抛出IOException时该怎么办?通常你只需要忽略它。由于这是一种反模式,他们在Java 7中引入了try-with语法。但对于你的例子,我认为没有其他选择。也许将每个finally放入自己的方法中可以使其更清晰。


0

要从 finally 块中调用多个方法,您必须确保它们都不会抛出异常 -- 这本来就是一个好主意,因为任何从 finally 块中抛出的异常都将覆盖 try/catch 中抛出的异常或返回值。

最常见的用例是文件或数据库连接,在这种情况下,您编写一个“安静关闭”方法(或使用现有库中的方法,例如 Jakarta Commons IO)。如果您需要清理的内容不允许您使用预先存在的方法,则可以编写自己的方法(在您的情况下,deleteTableQuietly())。

如果您正在使用 JDK-7,则还可以使用“try with resource”结构。


0
你可以创建一个抽象类Action,其中包含一个execute方法,并从该类派生出每个要调用抛出异常的方法的一个类,从execute方法调用此方法。然后,您可以创建一个Action列表并循环遍历列表的元素,在try finally块中调用它们的execute方法,忽略异常。

0

考虑使用java.util.concurrent框架--如果您将每个调用编码为单独的Callable(命名或匿名),则可以使用ExecutorService.invokeAll。


0
deleteTableSilently(table1);    
deleteTableSilently(table2);    
deleteTableSilently(table3);


deleteTableSilently()
    try
        deleteTable()
    catch whatever
        log.error();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接