什么是初始化块?

我们可以将代码放在构造函数、方法或初始化块中。初始化块有什么用?是否每个 Java 程序都必须有它?

79606 次浏览

每当初始化类并在调用构造函数之前执行初始化块。它们通常位于大括号内的构造函数之上。完全没有必要把它们包括在你的课程中。

它们通常用于初始化引用变量

这个问题并不完全清楚,但这里有一个简短的描述方式,您可以初始化对象中的数据。假设您有一个类 A,它包含一个对象列表。

1)在字段声明中输入初始值:

class A {
private List<Object> data = new ArrayList<Object>();
}

2)在构造函数中分配初始值:

class A {
private List<Object> data;
public A() {
data = new ArrayList<Object>();
}
}

这两者都假设您不希望将“ data”作为构造函数参数传递。

如果像上面那样将过载的构造函数和内部数据混合在一起,事情就有点棘手了:

class B {
private List<Object> data;
private String name;
private String userFriendlyName;


public B() {
data = new ArrayList<Object>();
name = "Default name";
userFriendlyName = "Default user friendly name";
}


public B(String name) {
data = new ArrayList<Object>();
this.name = name;
userFriendlyName = name;
}


public B(String name, String userFriendlyName) {
data = new ArrayList<Object>();
this.name = name;
this.userFriendlyName = userFriendlyName;
}
}

注意,有很多重复的代码。您可以通过让构造函数相互调用来解决这个问题,或者您可以拥有一个私有的初始化方法,每个构造函数调用:

class B {
private List<Object> data;
private String name;
private String userFriendlyName;


public B() {
this("Default name", "Default user friendly name");
}


public B(String name) {
this(name, name);
}


public B(String name, String userFriendlyName) {
data = new ArrayList<Object>();
this.name = name;
this.userFriendlyName = userFriendlyName;
}
}

或者

class B {
private List<Object> data;
private String name;
private String userFriendlyName;


public B() {
init("Default name", "Default user friendly name");
}


public B(String name) {
init(name, name);
}


public B(String name, String userFriendlyName) {
init(name, userFriendlyName);
}


private void init(String _name, String _userFriendlyName) {
data = new ArrayList<Object>();
this.name = name;
this.userFriendlyName = userFriendlyName;
}
}

The two are (more or less) equivalent.

我希望这能给你一些关于如何在对象中初始化数据的提示。我不会谈论静态初始化块,因为这可能是一个有点先进的时刻。

EDIT: I've interpreted your question as "how do I initialise my instance variables", not "how do initialiser blocks work" as initialiser blocks are a relatively advanced concept, and from the tone of the question it seems you're asking about the simpler concept. I could be wrong.

First of all, there are two types of 初始化块:

  • 实例初始化块
  • 静态初始化块

This code should illustrate the use of them and in which order they are executed:

public class Test {
    

static int staticVariable;
int nonStaticVariable;


// Static initialization block:
// Runs once (when the class is initialized)
static {
System.out.println("Static initalization.");
staticVariable = 5;
}
    

// Instance initialization block:
// Runs each time you instantiate an object
{
System.out.println("Instance initialization.");
nonStaticVariable = 7;
}
    

public Test() {
System.out.println("Constructor.");
}
    

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

印刷品:

Static initalization.
Instance initialization.
Constructor.
Instance initialization.
Constructor.

如果希望运行某些代码,而不管使用哪个构造函数,或者希望为匿名类执行某些实例初始化,则实例初始化块非常有用。

我想补充一下@aioobe 的回答

执行顺序:

  1. 超类的静态初始化块

  2. 类的静态初始化块

  3. 超类的实例初始化块

  4. 超类的构造函数

  5. 类的实例初始化块

  6. 类的构造函数。

还有几点需要记住(第一点是重复@aioobe 的回答) :

  1. 静态初始化块中的代码将在类加载时执行(是的,这意味着每个类加载只执行一次) ,在构造类的任何实例之前,在调用任何静态方法之前。

  2. The instance initialization block is actually copied by the Java compiler into every constructor the class has. So every time the code in instance initialization block is executed 没错 before the code in constructor.

示例代码,在这里被认可为一个答案是正确的,但我不同意它。它没有显示正在发生的事情,我将向您展示一个很好的例子来理解 JVM 实际上是如何工作的:

package test;


class A {
A() {
print();
}


void print() {
System.out.println("A");
}
}


class B extends A {
static int staticVariable2 = 123456;
static int staticVariable;


static
{
System.out.println(staticVariable2);
System.out.println("Static Initialization block");
staticVariable = Math.round(3.5f);
}


int instanceVariable;


{
System.out.println("Initialization block");
instanceVariable = Math.round(3.5f);
staticVariable = Math.round(3.5f);
}


B() {
System.out.println("Constructor");
}


public static void main(String[] args) {
A a = new B();
a.print();
System.out.println("main");
}


void print() {
System.out.println(instanceVariable);
}


static void somethingElse() {
System.out.println("Static method");
}
}

在开始注释源代码之前,我将简短地解释一下类的静态变量:

首先,它们被称为类变量,它们属于类,而不属于类的特定实例。类的所有实例都共享这个静态(类)变量。每个变量都有一个默认值,具体取决于基元类型或引用类型。另一件事是,当你在类的一些成员(初始化块、构造函数、方法、属性)中重新分配静态变量时,你是在改变静态变量的值,而不是特定实例的值,你是在改变所有实例的值。作为静态部分的结束语,我要说的是,类的静态变量不是在你第一次实例化类的时候创建的,而是在你定义类的时候创建的,它们存在于 JVM 中,不需要任何实例。因此,从外部类(没有定义静态成员的类)正确访问静态成员的方法是使用类名后跟点,然后是您想要访问的静态成员(模板: <CLASS_NAME>.<STATIC_VARIABLE_NAME>)。

现在让我们看看上面的代码:

入口点是主要的方法-只有三行代码。我想参考一下目前通过的例子。根据它,在打印“静态初始化块”之后必须打印的第一件事是“初始化块”,这是我的不同意见,非静态初始化块不在构造函数之前调用,而是在定义初始化块的类的构造函数的任何初始化之前调用。当你创建一个对象(类的实例) ,然后当你进入构造函数,第一部分调用的是隐式(默认)超构造函数或显式超构造函数或显式调用另一个重载构造函数(但在某些时候,如果有一个重载构造函数链,最后一个调用超构造函数,隐式或显式)。

对象是多态创建的,但是在进入类 B 及其主方法之前,JVM 初始化所有类(静态)变量,然后通过静态初始化块(如果有的话) ,然后进入类 B 并开始执行主方法。它进入类 B 的构造函数,然后立即(隐式地)调用类 A 的构造函数,使用多态性类 A 构造函数体中调用的方法(重写方法)是在类 B 中定义的方法,在这种情况下,在重新初始化之前使用名为 instanceVariable 的变量。在关闭类 B 的构造函数之后,线程被返回到类 B 的构造函数,但是它首先进入非静态初始化块,然后打印“构造函数”。为了更好地理解使用某些 IDE 进行调试,我更喜欢使用 Eclipse。

很好的回答 再加几分

public class StaticTest extends parent {
static {
System.out.println("inside satic block");
}


StaticTest() {
System.out.println("inside constructor of child");
}


{
System.out.println("inside initialization block");
}


public static void main(String[] args) {
new StaticTest();
new StaticTest();
System.out.println("inside main");
}
}


class parent {
static {
System.out.println("inside parent Static block");
}
{
System.out.println("inside parent initialisation block");
}


parent() {
System.out.println("inside parent constructor");
}
}

这给了

inside parent Static block
inside satic block
inside parent initialisation block
inside parent constructor
inside initialization block
inside constructor of child
inside parent initialisation block
inside parent constructor
inside initialization block
inside constructor of child
inside main

its like stating the obvious but seems a little more clear.

public class StaticInitializationBlock {


static int staticVariable;
int instanceVariable;


// Static Initialization Block
static {
System.out.println("Static block");
staticVariable = 5;


}


// Instance Initialization Block
{


instanceVariable = 7;
System.out.println("Instance Block");
System.out.println(staticVariable);
System.out.println(instanceVariable);


staticVariable = 10;
}




public StaticInitializationBlock() {


System.out.println("Constructor");
}


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


}

Output:

Static block
Instance Block
5
7
Constructor
Instance Block
10
7
Constructor

Initializer 块包含的代码总是在任何时候执行 创建一个实例,用于声明/初始化通用的 类的各种构造函数的一部分。

初始化构造函数和初始化块的顺序并不重要,初始化块总是在构造函数之前执行。

如果我们想为一个类的所有对象执行一次代码会怎么样?

We use Static Block in Java.

只是为了增加来自 @aioobe@Biman Tripathy的优秀答案。

A 静态初始化程序 is the equivalent of a constructor in the static context. which is needed to setup the static environment. 实例初始化程序最适合匿名内部类。

  • 类中还可能有多个初始化器块
  • 当我们有多个初始化器块时,它们按出现的顺序执行(实际上是通过 JVM 复制到构造函数)
  • 初始化程序块的顺序很重要,但是与构造函数混合的初始化程序块的顺序不重要
  • 抽象类还可以同时具有静态初始值设定项块和实例初始值设定项块。

Code Demo -

abstract class Aircraft {


protected Integer seatCapacity;


{   // Initial block 1, Before Constructor
System.out.println("Executing: Initial Block 1");
}


Aircraft() {
System.out.println("Executing: Aircraft constructor");
}


{   // Initial block 2, After Constructor
System.out.println("Executing: Initial Block 2");
}


}


class SupersonicAircraft extends Aircraft {


{   // Initial block 3, Internalizing a instance variable
seatCapacity = 300;
System.out.println("Executing: Initial Block 3");
}


{   // Initial block 4
System.out.println("Executing: Initial Block 4");
}


SupersonicAircraft() {
System.out.println("Executing: SupersonicAircraft constructor");
}
}

SupersonicAircraft的实例创建将按以下顺序生成日志

Executing: Initial Block 1
Executing: Initial Block 2
Executing: Aircraft constructor
Executing: Initial Block 3
Executing: Initial Block 4
Executing: SupersonicAircraft constructor
Seat Capacity - 300

In addition to what was said in previous answers, blocks can be synchronized .. never felt I need to use it, however,it's there