Java有类似于using语句的功能吗?

125

Java中是否有类似C#的using语句,在Hibernate打开会话时使用?

在C#中,它是这样的:

using (var session = new Session())
{


}

所以对象超出作用域后会自动关闭。


4
允许为对象定义范围。这不是using的作用。范围不等于生命周期(严格来说,using也不涉及生命周期,因为Dispose不会销毁对象的内存)。 - Joren
5
@Joren,您的评论正在得到赞同,但我需要更多信息。您是提出“终身”概念的人,然后又说这不是关于“终身”的。范围是MSDN库中定义所使用的术语,也许我使用不当了。您如何定义“using语句”。 - Jla
1
作用域是指代码中可以引用标识符而无需使用完全限定名称(局部变量、类型、方法名等)的区域。生命周期是指对象或变量可访问的时间。请参阅http://blogs.msdn.com/b/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx - Joren
2
例如,如果您有一个本地变量并将值类型实例分配给它,则该值的生命周期将在其变量的生命周期结束时结束。但是,如果您分配了一个对象,并将其引用存储在本地中,则该对象的生命周期可能会延长到其存储的生命周期之后,只要还有其他地方对该对象的引用。至于“using”,它会在其范围结束时自动“处理”对象,但它不会“释放”对象-直到所有引用都消失,其生命周期才结束。 - Joren
1
可能是"using" keyword in java的重复问题。 - HaveNoDisplayName
显示剩余4条评论
12个回答

145

Java 7引入了自动资源管理块(Automatic Resource Block Management),将此功能引入到Java平台中。在此之前的Java版本中没有任何类似于using的东西。

例如,你可以以以下方式使用实现了java.lang.AutoCloseable接口的任何变量:

try(ClassImplementingAutoCloseable obj = new ClassImplementingAutoCloseable())
{
    ...
}

Java的java.io.Closeable接口由流实现,自动扩展了AutoCloseable,因此您可以在try块中像在C#的using块中使用流一样使用它们。这相当于C#的using

版本5.0开始,Hibernate Sessions实现了AutoCloseable并可以在ARM块中自动关闭。在Hibernate的早期版本中Session没有实现AutoCloseable。因此,您需要使用Hibernate >= 5.0才能使用此功能。


1
幸运的是,现在Java 7已经可用了,这个答案不再正确(我认为ARM块正是using所做的事情)。 - Joachim Sauer
1
即使在4.3版Hibernate中,并没有实现AutoCloseable。http://docs.jboss.org/hibernate/orm/4.3/javadocs/index.html?org/hibernate/internal/SessionImpl.html 我猜每个人都需要编写自己的包装器? - Andrei Rînea
1
会话无法实现AutoCloseable,因为Session.close()返回一个Connection。我认为这是一个糟糕的设计,但我怀疑这将永远不会改变。 - usr-local-ΕΨΗΕΛΩΝ
@usr-local-ΕΨΗΕΛΩΝ 我认为如果他们实现了那个接口,他们会乐意让使用 hibernate 的人切换到 Java 7。AutoCloseable 在 Java 7 之前不存在,对吧? - Neil
正确,但如果他们仍然实现了 java.io.Closeable 接口,你会在 Java 7 代码中得到 try 语句。 - usr-local-ΕΨΗΕΛΩΝ
显示剩余2条评论

33

在Java 7之前,Java中没有这样的功能(如果使用Java 7或更高版本,请参见Asaph的答案关于ARM)。

你需要手动完成它,这是一件非常痛苦的事情:

AwesomeClass hooray = null;
try {
  hooray = new AwesomeClass();
  // Great code
} finally {
  if (hooray!=null) {
    hooray.close();
  }
}

即使在既没有//Great code抛出异常,也没有hooray.close()抛出异常的情况下,这仅仅是代码而已。

如果您真的只想限制变量的作用域,那么一个简单的代码块就可以完成任务:

{
  AwesomeClass hooray = new AwesomeClass();
  // Great code
}

但这可能不是你的意思。


1
如果“// Great code”引发异常,那么你的Java等效代码应该没有问题。 - Cheeso
3
当构造函数抛出异常时,我认为你的代码会导致一个空指针异常来掩盖原始异常。 - Michael Borgwardt
4
支持浮动简单块能够限制范围。但是,每当我看到这些块时,几乎总是表明该方法应该分解为更小的块。 - Mark Peters
为什么不能将“new AwesomeClass()”移动到第一行并避免在“finally”块中进行空值检查? - ironic
1
如果你想从构造函数中捕获任何异常,你必须将其放在try块内。把整个东西包装在另一个try/catch中会很麻烦。 - ths
显示剩余4条评论

20

8

从技术角度来说:

DisposableObject d = null;
try {
    d = new DisposableObject(); 
}
finally {
    if (d != null) {
        d.Dispose();
    }
}

2
这不是最好的做法。https://dev59.com/vnI-5IYBdhLWcg3wVWvv - Mark Byers
5
这实际上是等同的。我不在乎它是否是最好的方法。 - ChaosPandion
3
写得像个真正的C#程序员。;) - Neil

8

最接近的Java等价物是

AwesomeClass hooray = new AwesomeClass();
try{
    // Great code
} finally {
    hooray.dispose(); // or .close(), etc.
}

3

如果您对资源管理感兴趣,Project Lombok提供@Cleanup注释。直接摘自其网站:

您可以使用@Cleanup来确保在代码执行路径退出当前作用域之前自动清除给定的资源。您可以通过在任何局部变量声明中注释@Cleanup来实现这一点,如下所示:@Cleanup InputStream in = new FileInputStream("some/file");因此,在您所处的作用域结束时,将调用in.close()。通过try/finally结构,保证该调用会运行。请查看下面的示例以了解其工作原理。如果您想要清除的对象类型没有close()方法,但有其他无参数方法,您可以像这样指定该方法的名称:@Cleanup("dispose") org.eclipse.swt.widgets.CoolBar bar = new CoolBar(parent, 0);默认情况下,清除方法被假定为close()。带有参数的清除方法不能通过@Cleanup调用。
import java.io.*;

public class CleanupExample {
  public static void main(String[] args) throws IOException {
    InputStream in = new FileInputStream(args[0]);
    try {
      OutputStream out = new FileOutputStream(args[1]);
      try {
        byte[] b = new byte[10000];
        while (true) {
          int r = in.read(b);
          if (r == -1) break;
          out.write(b, 0, r);
        }
      } finally {
        out.close();
      }
    } finally {
      in.close();
    }
  }
}

使用Lombok
import lombok.Cleanup;
import java.io.*;

public class CleanupExample {
  public static void main(String[] args) throws IOException {
    @Cleanup InputStream in = new FileInputStream(args[0]);
    @Cleanup OutputStream out = new FileOutputStream(args[1]);
    byte[] b = new byte[10000];
    while (true) {
      int r = in.read(b);
      if (r == -1) break;
      out.write(b, 0, r);
    }
  }
}

3

目前还没有支持ARM架构的Java 7版本。

不过,ARM提出了一项Java 7的建议。


2
不,Java没有等同于“using”语句的语法。

5
现在可以访问:http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html。 - panzi

2

1
请查看此Java关键字列表
  1. using关键字不幸地不在列表中。
  2. 目前在Java中也没有任何其他关键字等同于C#的using关键字。

为了模仿这种"using"行为,您将需要使用try...catch...finally块,在其中您将在finally中处理资源。


7
using不是关键字并不代表什么。正如@BalusC所提到的,同样的功能可以(而且将会)使用另一个关键字实现。” - Joachim Sauer
1
我同意!但是现在还没有这个功能,对吧?这就是OP刚才问的,有没有类似的东西。虽然知道它将会在未来版本中存在,但是现在无论如何都没有改变什么。不管怎样,@BalusC提供的信息真的很棒!=) - Will Marcouiller
2
我同意这一点,但是你的帖子似乎在说using不在Java关键字列表中意味着Java语言中没有这个功能。这是不正确的。 - Joachim Sauer
如果我的帖子看起来是这样的话,那么我会进行编辑以反映我的意图。 - Will Marcouiller
我编辑了我的答案,指出目前没有using关键字或等价物。感谢@Joachim Sauer!=) - Will Marcouiller

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