15

我知道这段代码:

Set<String> set = new HashSet<String>() {{
  add("test1");
  add("test2");
}};

是真的:

Set<String> set = new HashSet<String>() {
  {//initializer
    add("test1");
    add("test2");
  }
};

初始化程序块在构造程序块之前执行。在上面的例子中, add("test1") 在构造函数被执行之前被调用。构造函数可能正在初始化许多实例字段,以便此类可以工作。我想知道为什么在构造函数之前调用.add()会起作用?有没有导致问题的案例?

4

4 回答 4

17

您遗漏了一个细节来解释这一点。

首先,让我们回顾一下初始化过程的第 3 步到第 5 步(总结):

3. 调用超类构造函数
4. 调用实例初始化器
5. 调用构造函数体

您遗漏的细节是您的表达式不仅仅是创建HashSet类的新实例,它实际上是创建匿名子类的新实例HashSet。(我相信这是在第 15.9.1 节中指定的。)

由于您没有声明构造函数,因此使用默认构造函数。但在此之前,超类的构造函数HashSet已经完成。

因此,总而言之,HashSet构造函数在初始化程序块运行之前完成。

于 2013-01-18T19:04:07.773 回答
6

这个假设是错误的:

初始化程序块在构造程序块之前执行。

因为在这种特殊情况下,初始化块构造器块的一部分。

文档明确指出

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

我认为您对静态初始化程序感到困惑。

于 2013-01-18T18:56:24.467 回答
4

实例初始化器在对象被构造后立即执行。您基本上是在创建 HashSet 的内联扩展,然后在创建它之后“立即”向其中添加两个项目。

这是用于测试的模拟对象中的常见使用模式,例如在 JMock 中,但也有其他方便的用途。

希望这可以帮助。

于 2013-01-18T18:59:16.630 回答
3

我认为这是一种不好的做法,因为它会创建毫无意义的子类,这些子类会影响应用程序的内存使用和性能。无论如何,该程序是正确的,因为在实例初始化程序之前调用了超类构造函数。因此,当您的初始化程序运行时,HashSet构造函数已运行,因此调用add将起作用。

于 2013-01-18T19:00:16.280 回答