如何在Java中组合可关闭对象?
我正试图创建一个Java类来管理多个如何在Java中组合可关闭对象?,java,Java,我正试图创建一个Java类来管理多个可关闭的资源。C++解决方案将是简单的,并且可以容易地扩展,资源数量也较大: class composed_resource { resource_a a; resource_b b; resource_c c; composed_resource(int x) : a(x), b(x), c(x) { } ~composed_resource() { } }; ComposedRe
可关闭的资源。C++解决方案将是简单的,并且可以容易地扩展,资源数量也较大:
class composed_resource
{
resource_a a;
resource_b b;
resource_c c;
composed_resource(int x)
: a(x), b(x), c(x)
{ }
~composed_resource()
{ }
};
ComposedResource c = new ComposedResource();
c.add(new Resource1());
c.add(new Resource2());
c.add(new Resource3());
...
// do nice thinks
// and, to close;
c.close();
我的朴素Java解决方案:
public class ComposedResource implements Closeable
{
private final ResourceA a;
private final ResourceB b;
private final ResourceC c;
public ComposedResource(int x) /* throws ... */ {
a = new ResourceA(x);
try {
b = new ResourceB(x);
try {
c = new ResourceC(x);
} catch (Throwable t) {
b.close();
throw t;
}
} catch (Throwable t) {
a.close();
throw t;
}
}
@Override
public void close() throws IOException {
try {
a.close();
} finally {
try {
b.close();
} finally {
c.close();
}
}
}
}
稍微改进的版本:
public class ComposedResource2 implements Closeable
{
private final ResourceA a;
private final ResourceB b;
private final ResourceC c;
public ComposedResource2(int x) /* throws ... */ {
try {
a = new ResourceA(x);
b = new ResourceB(x);
c = new ResourceC(x);
} catch (Throwable t) {
close();
throw t;
}
}
@Override
public void close() throws IOException {
try {
if (a != null) a.close();
} finally {
try {
if (b != null) b.close();
} finally {
if (c != null) c.close();
}
}
}
}
是否有一种更优雅的解决方案,可以在保持异常安全的同时避免嵌套的try-catch块?有三种资源是可以管理的,但更多的资源变得难以管理。(如果是本地范围,我可以使用“try with resources”语句,但这在这里不适用。)
我在使用java.rmi
时考虑过这一点。在构造函数中,我创建/查找注册表,查找对象并导出对象。close()需要注销和取消导出对象。我想到创建包装对象来处理导出/非导出(就像我在C++中利用RAII一样),但是我注意到这对我没有多大帮助(我不是那么多的java专家,但我必须用它来上大学)。
目前,我正在使用类似上面的ComposedResource2
之类的东西,而且效果很好。但现在我想知道是否有更优雅的解决方案。这样更改close()如何
@Override
public void close() {
close(a);
close(b);
close(c);
}
public void close(Closeable closeable) throws IOException{
if (closeable != null){
closeable.close();
}
}
我认为它更干净
此外,您还可以将这3个资源作为“Closeables”进行管理,并将它们放在一个数组中,这样ComposedResource将拥有您想要的任意多的资源。这样做:
public class ComposedResource{
List<Closeable> resources = new ArrayList<Closeable>();
public void add(Closeable c){
resources.add(c);
}
@Override
public void close(){
for (Closeable r : resources){
close(r);
}
}
public void close(Closeable c){
try{
c.close();
catch (IOException e){
log(e);
}
}
}
编辑:
@Mohit Kanwar建议以这种方式抛出异常:
@Override
public void close() throws IOException {
for (Closeable r : resources){
r.close(r);
}
}
并编辑了我的代码,@Lii说,执行这一异常将阻止关闭所有资源,我同意Lii的意见,因此我拒绝了Mohit的编辑…使用类似的资源进行尝试
@Override
public void close() throws IOException {
try (Closeable cc = c;
Closeable bb = b;
Closeable aa = a;) {
// do nothing
}
}
你能介绍一个用例吗?也许使用一个具体的例子会比抽象的问题产生更好的答案…为什么不尝试使用资源不适用?1)避免在构造函数中创建资源——理想情况下,它们应该只为字段分配参数,我认为RAII在这里不适用。2) 如果可能,请使用try with resources(当构造函数仅在“赋值模式”下工作时,可能对您更可能)。3) 您可以编写Closeable
装饰器,将多个Closeable
实例连接到一个实例中。4) 或者你可以使用番石榴com.google.common.io.Closer
@AdamMichalik:我已经添加了一些细节,但如果可能的话,我想抽象地讨论一下。在我看来,如果这些是文件、套接字或其他东西,这无关紧要——如果可以创建并关闭它们,那么您必须以这样的方式进行处理,以避免资源泄漏(无论是文件句柄,还是开放服务器套接字等)。此外,如果您对静态方法很在行,并且您认为装饰器是一种过火的工具(我个人认为这是出于几个原因,尽管我在上面提出过):如果资源未关闭,我们不应该在日志记录后抛出异常e吗?在您的解决方案中,如果第一个资源关闭时抛出,则后续资源将不会关闭。这不是它应该如何工作的!@inigoD我现在明白了。我为错误的编辑感到抱歉。我已经为此升起了一个标志,因为它花费了您一个downvo再次抱歉:(保存所有抛出的异常,然后在关闭所有资源后抛出它们可能是个好主意。否则错误可能会被隐藏。这就是该方法的目的。我相信这就是try with resource语句的工作方式。@indigoD不知怎的,我所做的一次编辑仍然在您的答案中(第一个代码组件)也请重新编辑它。如果资源由于初始化错误而为null会怎样?@AdamSkywalker如果b为null,a和c将被关闭。@AdamSkywalkertry with resources
在Java中是空安全的:即使所有cc
、bb
和aa
都为null,也不会失败。此外,未初始化或初始化不好的对象必须是空的不能使用,并引发初始化异常。最好的情况总是初始化良好的对象。@AngraMainyu:父类构造函数在子类中的任何初始值设定项之前被调用。因此,假设您的子类有一个声明并初始化为private Foo Foo=new Foo()的字段
…突然,如果它重写了一个可以从父类构造函数调用的方法,它必须支持foo
仍然为null的可能性!这太混乱和违反直觉了,子类所有者很难记住和跟踪它。@saka1029:没错,但通常它只是用documentat处理的ion(即“如果重写此方法,则必须调用超类实现”),这并不是什么大问题(子类所有者了解,在重写方法时,他们并不总是拥有100%的自由度。必须调用超类实现不是一项繁重的要求)