121

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

4

10 回答 10

211

首先,有两种类型的初始化块

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

此代码应说明它们的使用以及它们的执行顺序:

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.

如果您想让一些代码运行而不管使用哪个构造函数,或者如果您想为匿名类做一些实例初始化,实例初始化块很有用。

于 2010-10-21T12:46:17.977 回答
107

想添加到@aioobe 的答案

执行顺序:

  1. 超类的静态初始化块

  2. 类的静态初始化块

  3. 超类的实例初始化块

  4. 超类的构造函数

  5. 类的实例初始化块

  6. 类的构造函数。

还有几点要记住(第 1 点是对@aioobe 答案的重申):

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

  2. 实例初始化块实际上由 Java 编译器复制到类的每个构造函数中。因此,每次实例初始化块中的代码都在构造函数中的代码之前执行。

于 2013-03-14T15:36:33.030 回答
12

aioobe 的好回答又加了几分

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

这就像陈述显而易见的,但似乎更清楚一点。

于 2014-07-03T06:36:13.833 回答
4

此处被批准为答案的示例代码是正确的,但我不同意。它没有显示正在发生的事情,我将向您展示一个很好的示例,以了解 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>)。

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

入口点是 main 方法——只有三行代码。我想参考当前批准的示例。根据它,在打印“静态初始化块”之后必须打印的第一件事是“初始化块”,这是我的不同意,非静态初始化块在构造函数之前不被调用,它在构造函数的任何初始化之前被调用定义初始化块的类。

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

于 2014-03-14T13:48:33.487 回答
2

Initializer 块包含在创建实例时始终执行的代码。它用于声明/初始化类的各种构造函数的公共部分。

初始化构造函数和初始化块的顺序无关紧要,初始化块总是在构造函数之前执行。

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

我们在 Java 中使用静态块。

于 2016-10-10T09:45:10.427 回答
1

除了以前的答案中所说的之外,块可以synchronized..从来没有觉得我需要使用它,但是,它就在那里

于 2020-08-09T23:55:12.150 回答
0

每当初始化类并且在调用构造函数之前执行初始化块。它们通常放置在大括号内的构造函数上方。完全没有必要将它们包含在您的课程中。

它们通常用于初始化引用变量。这个页面给出了很好的解释

于 2010-10-21T12:39:58.723 回答
0

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

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

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

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

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

这些都假设您不想将“数据”作为构造函数参数传递。

如果您将重载的构造函数与上面的内部数据混合使用,事情会变得有些棘手。考虑:

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;
    }
}

两者(或多或少)等价。

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

编辑:我将您的问题解释为“我如何初始化我的实例变量”,而不是“初始化程序块如何工作”,因为初始化程序块是一个相对先进的概念,从问题的语气来看,您似乎在问更简单的概念。我可能是错的。

于 2010-10-21T12:45:56.683 回答
0
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();
    }

}

输出:

Static block
Instance Block
5
7
Constructor
Instance Block
10
7
Constructor
于 2015-02-05T23:30:33.853 回答
0

只是为了补充来自@aioobe@Biman Tripathy的出色答案。

静态初始化器相当于静态上下文中的构造器。这是设置静态环境所必需的。实例初始化器最适合匿名内部类。

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

代码演示 -

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
于 2020-01-04T15:42:34.753 回答