Java是否有using语句?


107

Java是否具有在休眠状态下打开会话时可以使用的using语句?

在C#中,它类似于:

using (var session = new Session())
{


}

因此,对象超出范围并自动关闭。


4
“允许定义对象的作用域”这不是这样using做的。范围不是生命周期(using严格来说,生命周期也不是生命周期,因为Dispose它不会破坏对象的内存。)
Joren

4
@Joren您的评论越来越高,但是我可以提供更多信息。您是介绍“终生”想法的人,然后说这与“终生”无关。范围是msdn库定义中使用的术语,也许我滥用了它。您将如何定义using statement
Jla 2011年

1
范围是指代码中的一个区域,您可以在其中使用标识符而不使用其完全限定的名称(局部变量,类型,方法名称等)。生存期是指对象或变量可访问的时间。参见blogs.msdn.com/b/ericlippert/archive/2009/08/03/…–
Joren

2
因此,例如,如果您有一个局部变量并为其分配一个值类型实例,则该值的生存期将在其变量的生存期结束时终止。但是,如果您分配了一个对象,并在本地存储了对该对象的引用,则该对象的生存期很可能会超过其存储的生存期,只要在其他地方仍然有对该对象的某些引用即可。至于using,它会自动部署在其范围末端的对象,但它不会解除分配对象-它的生命周期还没有结束,直到它的所有引用都消失了。
Joren

Answers:


124

Java 7引入了自动资源块管理,该功能将该功能引入了Java平台。Java的早期版本没有任何相似之处using

例如,您可以使用java.lang.AutoCloseable通过以下方式实现的任何变量:

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

java.io.Closeable由流实现的Java 接口自动扩展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:谢谢。我更新了答案以反映时间的流逝。就您而言,ARM块正是使用所做的;在我编写此答案时,在我看来,ARM块必须是try块,而using可以应用于任何任意块。现在来看,似乎可以在Java中使用do关键字来完成此操作。这是最近添加到提案中的还是我第一次错过了?OP还专门询问了有关休眠会话的问题。AFAIK:Hibernate Session仍然没有实现,AutoCloseable因此它们还不能使用ARM。
Asaph,

1
4.3休眠中的非事件实现AutoCloseable。docs.jboss.org/hibernate/orm/4.3/javadocs/index.html?org / ...我想每个人都应该编写自己的包装程序吗?
AndreiRînea13年

1
会话无法实现AutoCloseable,因为Session.close()返回一个Connection。我认为这是一个不好的设计,但我怀疑这种情况是否会改变。
USR-本地ΕΨΗΕΛΩΝ

@usr-local-ΕΨΗΕΛΩΝ我只是想知道如果他们实现了该接口,他们将迫使使用hibernate的任何人切换到Java 7。 AutoCloseable在Java 7之前不存在吗?
尼尔

31

在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 2010年

3
当构造函数引发异常时,我认为您的代码将导致一个NullPointerException,该NullPointerException掩盖了原始异常。
Michael Borgwardt'5

@Michael:实际上我的示例无法编译,因为此时horray可能尚未初始化(现已修复)。
约阿希姆·绍尔

4
+1表示可以限制范围的浮动简单块。但是,每当我看到这些内容时,几乎总是表明该方法应分解为较小的块。
马克·彼得斯

1
如果要从构造函数中捕获任何异常,则必须将其包含在try块中。将整个内容包装在另一个try / catch中会很麻烦。
2014年



8

最接近的Java等效项是

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



2

如果您对资源管理感兴趣,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();
    }
  }
}

与龙目岛

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);
    }
  }
}


1

请参阅此Java关键字列表

  1. using不幸的是,该关键字不在列表中。
  2. 而且,与usingJava中的其他任何关键字一样,C#关键字也没有等效性。

要模仿这种"using"行为,您将不得不使用一个try...catch...finally块,在其中您将处置中的资源finally


6
这个事实using是不是关键字并不意味着一件事。正如@BalusC所提到的,可以使用另一个关键字来实现(并且将实现!)相同的功能。
约阿希姆·绍尔

1
我同意!但目前尚不存在,对吗?这就是OP所问的,如果刚才有类似的事情。很高兴知道它会在将来的版本中存在,但是现在并不会以某种方式改变事物。无论如何,@ BalusC提供的信息非常棒!=)
Will Marcouiller 2010年

2
我同意这一点,但是您的帖子似乎表明using,Java关键字列表中未包含的事实意味着该功能在Java语言中不存在。那是不对的。
Joachim Sauer'5

如果这是我的帖子所说的话,那么我将进行编辑以反映我的意图。
Will Marcouiller

我编辑了答案,指定没有using关键字,也没有任何等效项。感谢@Joachim Sauer!=)
威尔·马库里耶


0

要回答有关限制变量范围的问题,而不是谈论自动关闭/处置变量。

在Java中,您可以使用大括号定义封闭的匿名作用域。非常简单。

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

该变量hooray仅在此范围内可用,而在该范围之外不可用。

如果您重复的变量只是临时的,这将很有用。

例如,每个带有索引。就像item变量在for循环上关闭(即仅在其中可用)一样,index变量在匿名作用域上关闭。

// first loop
{
    Integer index = -1;
    for (Object item : things) {index += 1;
        // ... item, index
    }
}

// second loop
{
    Integer index = -1;
    for (Object item : stuff) {index += 1;
        // ... item, index
    }
}

如果您没有for循环来提供变量作用域,但您想使用通用变量名,我有时也会使用它。

{
    User user = new User();
    user.setId(0);
    user.setName("Andy Green");
    user.setEmail("andygreen@gmail.com");
    users.add(user);
}

{
    User user = new User();
    user.setId(1);
    user.setName("Rachel Blue");
    user.setEmail("rachelblue@gmail.com");
    users.add(user);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.