Java是否有类似于C#“using”子句的等价物?

27

我在一些C#的帖子中看到了“using”关键字的提及。Java是否有相当的语法结构?


1
使用Java 7,答案从“否”变成了“是”。 - Lodewijk Bogaards
12个回答

30
是的。Java 1.7引入了“try-with-resources”结构,允许您编写:

是的。Java 1.7引入了try-with-resources结构,使您可以编写:

try(InputStream is1 = new FileInputStream("/tmp/foo");
    InputStream is2 =  new FileInputStream("/tmp/bar")) {
         /* do stuff with is1 and is2 */
}

...就像using语句一样。

不幸的是,在Java 1.7之前,Java程序员被迫使用try{ ... } finally { ... }。在Java 1.6中:

InputStream is1 = new FileInputStream("/tmp/foo");
try{

    InputStream is2 =  new FileInputStream("/tmp/bar");
    try{
         /* do stuff with is1 and is 2 */

    } finally {
        is2.close();
    }
} finally {
    is1.close();
}

能否更新这个答案,提供一个使用C#的using块和Java的try-finally的示例?最好是使用两个不同的资源的示例,以便我们可以记录如何确保正确关闭所有内容。 - Tim Frey
2
作为一名Java程序员,上面的比较实在令人痛苦。 - Aaron Maenpaa
虽然从技术上讲是正确的,但这种try/finally模式并不是最好的选择。建议使用以下方式:X x = new X(); try{ /work/ } finally { x.dispose(); } - McDowell
除非new X()引发异常,例如FileNotFoundException,否则您必须引入另一层try-except,或者使用上述方法并使用现有的try处理FileNotFoundException。两种选择都不是特别令人愉快的。 - Aaron Maenpaa
回顾过去,我同意将处理FNF异常与确保流关闭分开。即使它需要另一个外部try...catch块或throws声明。 - Aaron Maenpaa
显示剩余3条评论

11

是的,自Java 7以来,您可以重写:

InputStream is1 = new FileInputStream("/tmp/foo");
try{

    InputStream is2 =  new FileInputStream("/tmp/bar");
    try{
         /* do stuff with is1 and is2 */

    } finally {
        is2.close();
    }
} finally {
    is1.close();
}

As

try(InputStream is1 = new FileInputStream("/tmp/foo");
    InputStream is2 =  new FileInputStream("/tmp/bar")) {
         /* do stuff with is1 and is2 */
}

try语句中的参数对象应该实现java.lang.AutoCloseable接口。请查看官方文档

对于旧版本的Java,请查看这个答案这个答案


5
最接近该语言的等效方法是使用try-finally。
using (InputStream in as FileInputStream("myfile")) {
    ... use in ...
}

变成

final InputStream in = FileInputStream("myfile");
try {
    ... use in ...
} finally {
    in.close();
}

请注意,通用格式始终如下:
acquire;
try {
    use;
} finally {
    release;
}

如果获取操作在try块中,当获取失败时您将会进行释放。在某些情况下,您可能可以通过不必要的代码(通常是在上面的示例中测试null)来解决问题,但是在ReentrantLock的情况下,会发生糟糕的事情。

如果您经常执行相同的操作,可以使用“执行周围”习语。不幸的是,Java的语法很冗长,因此有很多样板文件。

fileInput("myfile", new FileInput<Void>() {
   public Void read(InputStream in) throws IOException {
       ... use in ...
       return null;
   }
});

在哪里

public static <T> T fileInput(FileInput<T> handler) throws IOException {
    final InputStream in = FileInputStream("myfile");
    try {
        handler.read(in);
    } finally {
        in.close();
    }
}

更复杂的例子是,例如,包装异常。


3

2

据我所知,不行。你可以用try...finally块来进行某种程度上的模拟,但仍然不完全相同。


2

在Java中,最接近的方法是使用try/finally。此外,Java没有提供隐式的Disposable类型。

C#:将变量作用域限制在using块之外

public class X : System.IDisposable {

    public void Dispose() {
        System.Console.WriteLine("dispose");
    }

    private static void Demo() {
        X x = new X();
        using(x) {
            int i = 1;
            i = i/0;
        }
    }

    public static void Main(System.String[] args) {
        try {
            Demo();
        } catch (System.DivideByZeroException) {}
    }

}

Java:在块外范围变量的作用域

public class X {

    public void dispose() {
        System.out.println("dispose");
    }

    private static void demo() {
        X x = new X();
        try {
            int i = 1 / 0;
        } finally {
            x.dispose();
        }        
    }

    public static void main(String[] args) {
        try {
            demo();
        } catch(ArithmeticException e) {}
    }

}

C#: 在块内限定变量的范围

public class X : System.IDisposable {

    public void Dispose() {
        System.Console.WriteLine("dispose");
    }

    private static void Demo() {
        using(X x = new X()) {
            int i = 1;
            i = i/0;
        }
    }

    public static void Main(System.String[] args) {
        try {
            Demo();
        } catch (System.DivideByZeroException) {}
    }

}

Java: 在代码块中限定变量的作用域

public class X {

    public void dispose() {
        System.out.println("dispose");
    }

    private static void demo() {
        {
            X x = new X();
            try {
                int i = 1 / 0;
            } finally {
                x.dispose();
            }
        }
    }

    public static void main(String[] args) {
        try {
            demo();
        } catch(ArithmeticException e) {}
    }

}

1

我认为你可以通过实现一个匿名内部类来实现类似于“using”块的功能。就像Spring在“Dao Templates”中所做的那样。


0

嗯,使用它只是语法糖,所以Java的同行们,不要担心。


0
如果Java中有BGGA闭包,这也将为Java中的类似结构打开大门。例如,Gafter在他的幻灯片中使用了这个例子:
withLock(lock) { //closure }

0

大多数程序员在第一个示例中使用的惯用语是:

InputStream is1 = null;
InputStream is2 = null;
try{
    is1 = new FileInputStream("/tmp/bar");
    is2 = new FileInputStream("/tmp/foo");

    /* do stuff with is1 and is 2 */

} finally {
    if (is1 != null) {
        is1.close();
    }
    if (is2 != null) {
        is2.close();
    }
}

使用这种惯用语法,缩进会减少,当你需要清理超过两个资源时,这一点变得更加重要。

此外,你可以向结构体中添加一个 catch 子句,以处理新的 FileStream() 抛出异常的情况,如果需要的话。在第一个示例中,如果您想要执行此操作,则必须有另一个封闭的 try/catch 块。


如果调用is1.close()抛出异常,这段代码无法关闭is2。问题中给出的嵌套代码更可取。 - cunkel

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