Java空块作用域

10

我想知道使用空块的目的是什么。例如,

    static{
        int x = 5;
    }

    public static void main (String [] args){

          int i = 10;

          {
               int j = 0 ;
               System.out.println(x);  // compiler error : can't find x ?? why ??
               System.out.println(i);  // this is fine
          }
          System.out.println(j); //compiler error :  can't find j

    }

有人可以解释一下吗?

  1. 在什么情况下我们会想要使用空块。
  2. 所有在那个空块中的变量还是会进入 stack 吗?
  3. 为什么它不能访问 static变量x

3
x在静态初始化程序的作用域内声明。类似地,jmain内部的内部块作用域内声明。您试图解析这些符号,但它们在这些作用域中未定义。 - obataku
除了在静态块内部,没有人可以访问它吗? - peter
是的,确实 :-) 您可以在JLS的§6.3中阅读更多有关声明范围的内容。 - obataku
顺便说一下,正如我在这里所说的那样,局部变量分配是实现特定的,并且在VM规范中未定义。 - obataku
3个回答

7
  1. 您在帖子中显示的块不是空块,而是静态初始化程序。它用于为类的静态变量提供非平凡的初始化。
  2. 在初始化期间使用的局部变量放置在堆栈上,除了从堆中分配的对象。
  3. 您无法访问静态变量x,因为您没有声明它。相反,您在静态初始化程序中声明了一个局部变量x

如果您想将x变为静态变量,然后在静态初始化块中初始化,请执行以下操作:

private static int x;
static {
    x = 5;
}

在像这样的简单情况下,直接的初始化语法效果最佳:
private static int x = 5;

初始化块保留用于更复杂的工作,例如当您需要使用循环初始化结构时:

private static List<List<String>> x = new ArrayList<List<String>>();
static {
    for (int i = 0 ; i != 10 ; i++) {
        x.add(new ArrayList<String>(20));
    }
}

我在想,如果您将这些操作留在构造函数中而不是静态块中,是否会有所不同? - peter
@user1389813 不,这完全不一样:构造函数仅在每个类实例中运行一次,而静态初始化程序(如果有)则在整个类中运行一次。这是有道理的,因为静态字段在类的所有实例之间共享,所以它们的初始化只需要发生一次。 - Sergey Kalinichenko
@user1389813 我的回答展示了一个简单的例子,说明这种情况何时是可取的(底部的代码片段)。初始化一个列表需要循环,因此你需要编写一个静态函数或使用静态初始化块。 - Sergey Kalinichenko
1
@user1389813 静态块不是在方法内声明的,而是直接在类内声明的。试图在方法内这样做会导致语法错误(http://ideone.com/sQDiY)。 - Sergey Kalinichenko
1
@user1389813,就独立的非空块而言,您可以使用它们将变量声明隐藏在函数的其余部分之外。这里是一个链接,展示了在同一个函数中两次声明相同名称变量的示例。如果没有这样的块,您将无法重新声明变量。 - Sergey Kalinichenko
显示剩余10条评论

4
  1. 匿名代码块只有限制变量作用域的作用,没有真正的目的。
  2. 是的,但这个范围仅限于该块内部。
  3. x的范围仅限于静态初始化块,因为它是在方法中声明的(静态上下文在内部是方法),而不是在类的静态范围内声明的。

那么你的意思是,在主方法中的空块并没有实际意义? - peter
2
在任何情况下,空块并没有实际意义,除了无用的作用域限制。在类的上下文中,它们允许实例变量初始化,但更常规的做法是使用构造函数来完成这个目的。 - FThompson
我已经使用了匿名代码块来结构化测试代码,与 C# 区域略有相似。虽然有些滥用,但在测试中可以获得更易读的代码。 - user77115

4

静态块在类初始化时运行,因此对于初始化静态成员非常有用。

static final Map<K, V> MY_MAP = ...;

static {
  MY_MAP.put(...);
  ...
}

那个空块内声明的所有变量是否仍然在堆栈上?

静态块中声明的变量是局部变量,不是类的静态成员。正如@veer所指出的,它是否进入堆栈是虚拟机实现的细节。


为什么main不能访问静态变量x

因为它是一个局部变量,只存在于static初始化程序的持续时间内。


局部变量是否在堆栈上分配完全取决于虚拟机。 - obataku
JVM规范的§2.6节指出,它们是VM帧的一部分,而VM帧并没有明确说明它们是在堆上还是栈上或者_任何地方_分配的。 - obataku
@veer,JVM被指定为堆栈机,因为许多字节码指令操作堆栈而不是寄存器。那个规范抽象堆栈就是我所指的堆栈。 - Mike Samuel
恐怕您对操作数栈(§2.6.2)有所困惑...本地变量不存储在操作数栈中。它们存储在本地变量表(§2.6.1)中。;-) - obataku
不,我并不是想纠正你...只是想澄清一下 :-) 这不是你的错,因为JVM并不是像它所宣传的那样严格的堆栈机器...局部变量非常像寄存器。 - obataku
@veer,谢谢。回答语言问题时使用正确的术语非常重要。否则,当深入研究大型规范时,人们很容易迷失方向。 - Mike Samuel

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