0

可能重复:
Java 中的匿名代码块

我前几天(在SO上)了解到

public method() {
    someCode()
    {
        Object value = localCode();
        doSomethingWith(value);
    }
    moreCode();
}

是有效的 Java,它使包含value该区域本地的块,因此value仅存在于该块中。

这有什么实际用途吗?如果不是,为什么 Java 不对这种(愚蠢的)使用发出警告{}

4

9 回答 9

10

我在想要重复不同场景的测试中使用它。

{
    long start = System.nanoTime();
    // do something
    long time = System.nanoTime() - start;
    // print result.
}
{
    long start = System.nanoTime();
    // do something else
    long time = System.nanoTime() - start;
    // print result.
}
{
    long start = System.nanoTime();
    // do something else again.
    long time = System.nanoTime() - start;
    // print result.
}

这允许复制代码而无需更改名称,或冒着重复使用变量的风险。

于 2012-08-15T14:22:19.817 回答
3

不确定这实际上是为什么,但假设以下(有些人为的)情景:

public static void main(String[] args){
    int[] idsToCheck = {};
    {
       ExpensiveIDComputer sic = new ExpensiveIDComputer(); // very memory intensive call
       idsToCheck = sic.getIds();
    }
    // sic is now out of scope and can be GCed faster
    doManyOperationsOnIds(idsToCheck);
}

因此,从本质上讲,它可以让您更快地对占用内存的操作进行垃圾收集。为什么你不只是使用另一种方法,我不知道,但这是我在阅读你的问题时的第一个想法。

于 2012-08-15T14:24:12.450 回答
1

不,如果moreCode变量在 { } 块之外声明,它将保留value,除非在执行期间更改。

初始化块不经常使用。

但是当你的类有多个构造函数时,它会很棒,它应该做一些常见的初始化操作,比如初始化一些变量或加载一些数据。

您可以将通用构造函数代码粘贴到初始化块中,如下所示。还有一个事实是初始化块只会在构造函数和父构造函数之后执行。

实例变量的初始化块看起来就像静态初始化块,但没有 static 关键字:

{

// whatever code is needed for initialization goes here 

}

Java 编译器将初始化程序块复制到每个构造函数中。因此,这种方法可用于在多个构造函数之间共享代码块。

参考:http ://docs.oracle.com/javase/tutorial/java/javaOO/initial.html

于 2012-08-15T15:35:51.070 回答
1

...所以 moreCode 仅具有该块中的 value 值。

这不完全正确,如果moreCode没有在块内声明。例如:

int moreCode = 0;

{
    moreCode = 1;
    System.out.println(moreCode);
}

// Will print 1, not 0!
System.out.println(moreCode);

您可以使用它来限制变量的范围。如果你在块内声明一个变量,它只会存在于块内。

{
    int moreCode = 1;
    System.out.println(moreCode);
}

// Compile error: The variable moreCode doesn't exist here
System.out.println(moreCode);

但是,这不是常用的。如果您觉得需要以这种方式使用块来限制范围,那么您的方法可能过于复杂,您应该考虑将它们拆分为更简单的方法。

于 2012-08-15T14:23:07.867 回答
1

我使用它有三个原因:

  • 最常见的是,在我初始化一个或多个最终值时声明/使用临时值:

    final int value;
    {
       final int counter;
       ... compute value ...
      value = counter;
    }
    
  • 否则“工作”而不将变量引入外部命名空间(限制范围)。

  • 突出/区分属于一起的一段代码,但我还没有准备好推广到一个独立的方法(通常是为了保持代码的局部性以提高整体的可读性)。[在任何用例的 90% 中,都应该提升代码 - 这有点特殊]。

于 2012-08-15T14:46:42.130 回答
0

另一个用例:为匿名子类声明最终变量。显然,您不能只在下面的匿名子类中引用“foo”。你必须使它成为“最终的”。作为替代方案,我们将值复制到一个新的最终变量中并使用它。新变量是执行期间特定时间点的“foo”快照值。

int foo = 1;

....

{
  final int bar = foo;
  Future<Void> future = executorService.submit(new Callable<Void>() { // use bar here });
}

我意识到你可以在没有大括号的情况下做到这一点,但这限制了新变量的范围。

于 2012-08-15T15:25:03.287 回答
0

它还允许您添加标签,以便您制作意大利面条代码!

MY_LABEL: {
  if (condition) {
    break MY_LABEL;
  }
  System.out.println("IN BLOCK");
}
System.out.println("OUTSIDE BLOCK");

如果condition为真,那么控制流将直接跳过第一个打印语句,您只会得到第二个打印。

免责声明:我不推荐使用意大利面条代码——这只是一个假设性的讨论,讨论你可以用这样的块做什么。

于 2012-08-15T14:39:51.307 回答
0

如果您命名了您的代码块,您可以break通过寻址名称来阻止。

name1 : {
    name2 :{
        final int  i = 1;
        if( i == 1) {
            break name1;
        }
        System.out.println("name2");
    }
    System.out.println("name1");
}

你也可以limit the scope of a variable,所以在块结束后,变量可以很容易地被垃圾收集。

于 2012-08-15T14:38:22.970 回答
0

示例是instance initializers代码必须捕获异常的地方。

class SomeClass {
    private int myVar;

    // Instance initializer
    {
        try{ 
           myVar = getInitialValueFromDB(); 
        }  
        catch(Exception e){
           myVar = -1;
        }       
    }
    // no constructor here
    // ...
}
于 2012-08-15T14:23:43.047 回答