使用初始化块有哪些优势?

3

我知道初始化块会在类第一次加载时运行(静态初始化块)或在创建实例时运行(实例初始化块)。

class SmallInit {
   static int x;
   int y;
   static { x = 7 ; } // static init block
   { y = 8; } // instance init block
}

但是这样做的特殊好处是什么呢?当我们可以像下面这样做时:

class SmallInit {
   static int x = 7;
   int y = 8;
}

1
请查看这个问题以及所有的回答,但不包括被接受的回答 ;) - Grzegorz Rożniecki
可能是https://dev59.com/8XVD5IYBdhLWcg3wL4YA中静态块的模拟的重复问题。 - paulsm4
@Xaerxess:我希望我能够将评论作为答案接受 :) - Bhushan
下次在提问之前,请先在 Stack Overflow 上搜索类似的问题,这样对我也有好处 :) 另外,如果你愿意,可以在回答中包含该链接并解释你在那里找到了最佳答案。 - Grzegorz Rożniecki
6个回答

7
实例初始化块的一个好处是它使得双括号初始化模式成为可能。
而不是这样:
Set<String> names = new HashSet<String>();
names.add("Peter");
names.add("Paul");
names.add("Mary");

你可以这样做:
Set<String> names = new HashSet<String>() {{
    add("Peter");
    add("Paul");
    add("Mary");
}};

第一个大括号创建了一个匿名内部类;第二个大括号开始实例初始化块。(注意,这意味着namesHashSet的匿名子类的实例,但通常不是问题。这也意味着这种模式仅适用于非final类。)
这在需要将对象初始化为表达式的一次性情况下特别有用。例如:
doSomethingToSet(new HashSet<String>() {{
    add("Peter");
    add("Paul");
    add("Mary");
}});

请注意 - 这绝对有效,但它会创建一个内部类,该类具有对'this'的隐式引用。如果您没有预料到这一点,可能会导致一些有趣的GC行为...我并不是说不要使用它(我肯定会使用),但要意识到这一点。 - Kevin Day
@KevinDay 你能详细说明一下吗?我对可能出现的问题很感兴趣。 - Craig Otis
当你像这样初始化一个映射时,它会隐式地引用外部对象。如果你将该映射传递到应用程序的另一个区域,那么“外部对象”直到映射本身被释放之前都不会被释放。如果该映射仅是对象的实现,则没有问题。但是,如果你在外部接口中公开了该映射,就会出现意外的引用。 - Kevin Day

3

我认为仅包含一行变量声明的静态块没有任何特殊好处。事实上,当您只是为类或实例变量分配值时,理解正在发生的事情会更加困难。

然而,在需要构建更复杂的起始状态时,静态块和实例块确实非常有用。以下是一个示例,其中使用了声明和静态块:

static List<Sprocket> mySprockets = new ArrayList<Sprocket>();

static {
    mySprockets.add(new Sprocket("foo", 17));
    mySprockets.add(new Sprocket("bar", 8));
}

2

过去我使用初始化块来填充复杂的数据结构。然而,我认为编写一个静态函数来填充数据结构并调用它是更好的方法。

例如:

private static Map foo = initFoo();

private static Map initFoo() {
  Map foo = new Map();
  foo.put("x", "y");
  foo.put("a", "b");
  return foo;
}

有些人可能不太熟悉初始化块,如果你决定想在其他上下文中使用你的初始化代码,那么你可以轻松地调用该函数。


0

没有特别的优点,但在某些情况下可以提高可读性(通常是在声明多个成员时)。


0

实例初始化块的优点可能包括:

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

点击此处获取更好的概述。


0
  1. 如果没有初始化程序,变量将被初始化为0或null。因此,如果您要在构造函数中设置其他值,则实际上会设置两次(一次为0或null,然后再次在构造函数中设置为所需值)。在绝大多数情况下,性能损失可以忽略不计(一个例外可能是您在紧密循环中使用的数学类,但即使这样也非常依赖于该循环中发生的其他事情)。
  2. 它可以提高可读性。
  3. 如果从构造函数调用虚拟方法,则派生类中使用的重写虚拟方法的数据成员将具有0或null作为值,除非它具有初始化程序。我不确定Java是否是这种情况,因为已经过了一段时间;我知道C#是这样的。

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