实例初始化程序和构造函数有什么区别?

20

只是想知道编译代码这样做的原因:

class MyClass extends AnotherClass {
  {
    MySecondClass object = new MySecondClass();
    object.doSomething();
  }
}

这段代码和构造函数中的代码有什么区别?这段代码在对象创建之前执行。


我一直在想这个问题,我记得有一段时间以前看到过... 有人知道这种结构叫什么吗? - SimonT
1
它被称为“初始化块”。实际上,它只是复制到每个构造函数中。 - wchargin
1
这个问题可能是重复的问题:http://stackoverflow.com/questions/16128076/instance-initialization-block-and-subclasses - Rong Nguyen
2
@Tangmeister 这被称为“实例初始化块”。与“静态初始化块”不同,它不使用任何关键字来前缀该块,因此看起来很令人困惑。 - Ravi K Thapliyal
如果你有适当的构造函数链,就可以避免使用初始化块。 - Suresh Atta
3个回答

23

花括号内没有名称的代码将成为类的构造函数的一部分,并在类构造函数中包含的逻辑之前执行。

快速示例:
public class Foo {
    {
        System.out.println("Before Foo()");
    }

    public Foo() {
        System.out.println("Inside Foo()");
    }

    {
        System.out.println("Not After Foo()");
    }
}

5
它出现在每个构造函数之前。(javap -c命令可以告诉你。) - millimoose
哦,非常感谢。我刚才在玩代码,突然意识到这样的代码可以编译。它让我想起了学校里的 Pascal 语言 :D - Oldestkon
1
使用实例初始化器,您只需编写一次代码,无论用哪个构造函数创建对象,它都将被执行。 - Miuler

13

这被称为实例初始化程序。初始化程序中的代码在调用超类构造函数之后,构造函数的其余代码之前插入。

任何构造函数的第一个操作都是调用超类构造函数。如果显式调用了构造函数,super(...)将使用指定的构造函数。如果没有显式调用任何构造函数,则会在超类中调用默认构造函数(不带参数的构造函数)。如果不存在这样的构造函数,则是编译时错误。

在此显式或隐式构造函数调用之后,按照它们在源代码中出现的顺序调用实例初始化程序(是的,您可以有多个初始化程序)。

为了说明,运行此程序会打印:

Another constructor
Init 1
Init 2
Test constructor
class Another {
  Another() { System.out.println("Another constructor"); }
}

class Test extends Another {

  public static void main(String[] args) { new Test(); }

  { System.out.println("Init 1"); }

  Test() { System.out.println("Test constructor"); }

  { System.out.println("Init 2"); }

}

最常见的应用是在初始化"双括号初始化"习惯用法中,其中定义了一个匿名内部类,同时创建并配置实例。以下是Swing编程中一个相当常见的示例:

JButton popupButton = new JButton(new AbstractAction("Popup") {
  {
    putValue(Action.SHORT_DESCRIPTION, "Popup a dialog");
  }

  @Override
  public void actionPerformed(ActionEvent evt)
  {
    popup();
  }
});

如果您有多个构造函数,并且需要在每个构造函数中执行一些无参数初始化,则可以使用此方法。这可以合并到一个初始化块中。


这个答案非常清楚地解释了实例初始化器和构造函数被调用的顺序。谢谢! - Louis CAD

3
这是一个在构造函数之前运行的实例初始化块,你可能会问为什么要使用它来代替构造函数?答案是不需要。

只是想知道编译这样的代码的原因:

通常情况下,当使用构造函数重载时,您可以使用它来提取公共代码。因此,上面的"the"实际上是指在执行公共实例初始化代码块后,在对象实例化时调用的重载构造函数之一。
顺便说一句,有时您可以通过从其中一个构造函数调用另一个构造函数来实现相同的效果,但调用必须位于调用构造函数内的第一行,否则代码将无法编译。

不,我当然不打算用它来代替构造函数。这只是一种普通的兴趣,没有别的意思。感谢您对构造函数重载的解释。 - Oldestkon
1
不客气。你可能已经知道了。还有静态初始化块,用于初始化静态类成员。此外,多个块按照它们在代码中出现的顺序执行。 - Ravi K Thapliyal
我已经掌握了基础知识,目前正在学习多线程的“深度”,例如volatile变量或同步块。 - Oldestkon

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