题 什么是初始化块?


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


75
2017-10-21 12:28


起源


你的意思是在constructoe之后或之后调用的init()函数?或者在任何方法之外声明的sttatic块? - atk
我不清楚你的问题是什么,也许标题有点误导 - Junior M
可能重复 在Java中使用Initializers与Constructors - Mark Peters


答案:


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

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

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

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.

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


143
2017-10-21 12:46



目前看起来它们是按照代码中的外观顺序执行的。可以通过代码中的顺序与实际执行顺序不同的方式来改进示例。另外:可以有几个初始化块,然后它们按照出现的顺序执行(但仍然在构造函数之前)。 - Thomas Weller
@Pacerier所以你可以在拥有多个构造函数时使用公共代码而不必使用 init() 方法(有人更新课程可能会忘记调用它) - pablisco
@Thomas wellerif它在构造函数之前执行它是如何允许的 this 关键字inisde实例初始化块。 this 是curernt类对象,它会在构造函数调用完成后完全构造好吗? - amarnath harish


想加入@ aioobe的答案

执行顺序:

  1. 超类的静态初始化块

  2. 类的静态初始化块

  3. 实例初始化超类的块

  4. 超类的构造者

  5. 类的实例初始化块

  6. 类的构造函数。

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

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

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


79
2018-03-14 15:36



因此,如果我创建10个SomeClass实例,则步骤1和2仅执行一次,直到某些事情导致类被卸载(我只能想到的是重新启动程序,但是如果有其他事情可以导致它,我我想知道。 - Glen Pierce
@GlenPierce你走了: docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.7 - Biman Tripathy


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

它表示明显但似乎更清晰。


5
2017-07-03 06:36





示例代码在此处被批准作为答案是正确的,但我不同意它。它没有显示正在发生的事情,我将向您展示一个很好的例子来了解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中而不需要任何实例。因此,从外部类(未定义它们的类)中正确访问静态成员的方法是使用后跟dot的类名,然后使用要访问的静态成员(模板: <CLASS_NAME>.<STATIC_VARIABLE_NAME>)。

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

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

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


3
2018-03-14 13:48



TL; DR   OP只是要求对初始化块进行解释,而不是对静态变量,构造函数或IDE首选项的基本原理进行冗长的解释。 - b1nary.atr0phy
有时,这些冗长的解释可能会出乎意料地受欢迎。要么那些问原始问题的人真的需要一个长篇大论的解释才能得到他们的基础。或者,如果人们自己阅读答案,就好像它是关于给定主题的博客一样。在这种情况下,它不是,我会说。 - user7610
@nenito,我认为你对接受的答案的评论是误导性的。我鼓励你把它改成更像“我有一个可能感兴趣的更微妙的解释”。接受的答案似乎完全正确,根本不像你的那么详细。 - Glen Pierce
@Glen Pierce:在我的评论之后修改了接受的答案。我的句子不仅给出了答案,还提供了一些我认为对初级和中级Java开发人员有用的额外信息。 - nenito


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

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

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

我们在Java中使用Static Block。


1
2017-10-10 09:45





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

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


0
2017-10-21 12:39



根据@Biman,超类的构造函数运行 之前 init块。 - Nicolas Barbulesco


问题并不完全清楚,但这里简要介绍了如何初始化对象中的数据。假设你有一个包含对象列表的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;
    }
}

这两者是(或多或少)等价的。

我希望能为您提供有关如何初始化对象数据的一些提示。我不会谈论静态初始化块,因为此刻可能有点先进。

编辑:我已经将你的问题解释为“如何初始化我的实例变量”,而不是“初始化块如何工作”,因为初始化块是一个相对先进的概念,并且从问题的基调来看,你似乎在询问更简单的概念。我错了。


0
2017-10-21 12:45



即使您将问题解释为“我如何初始化我的实例变量?”,您的答案也没有提到可以使用初始化程序完成。 - Thomas Weller


要知道静态初始化块的使用,请参阅Class.forName源代码以及它的使用 http://cephas.net/blog/2005/07/31/java-classfornamestring-classname-and-jdbc/  ,他们使用初始化块进行动态类加载。


0
2017-10-21 12:45





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

0
2018-02-05 23:30





初始化程序块在类中定义,而不是作为方法的一部分。它为为类创建的每个对象执行。在以下示例中,类 Employee 定义初始化块:

class Employee { 
 { 
 System.out.println("Employee:initializer"); 
 } 
}

0
2017-08-18 08:21