【问题标题】:What is an initialization block?什么是初始化块?
【发布时间】:2011-04-28 14:18:11
【问题描述】:

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

【问题讨论】:

  • 你的意思是由构造函数调用还是在构造函数之后调用的 init() 函数?还是在任何方法之外声明的静态块?
  • 看不清楚你的问题是什么,可能标题有点误导

标签: java constructor initialization initialization-block


【解决方案1】:

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

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

【讨论】:

  • 根据@Biman 的说法,来自超类的构造函数在 init 块之前运行。
【解决方案2】:

这个问题并不完全清楚,但这里简要描述了您可以在对象中初始化数据的方法。假设您有一个包含对象列表的类 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;
    }
}

两者(或多或少)等价。

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

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

【讨论】:

  • 即使您将问题解释为“如何初始化我的实例变量?”,您的回答并没有提到可以使用初始化程序来完成。
【解决方案3】:

首先initialization blocks有两种类型:

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

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

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.

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

【讨论】:

  • 目前看起来它们是按照代码中出现的顺序执行的。该示例可以通过代码顺序与实际执行顺序不同的方式进行改进。另外:可以有几个初始化块,然后按照出现的顺序执行它们(但仍然在构造函数之前)。
  • @Pacerier 因此,当有多个构造函数时,您可以使用通用代码,而不必使用 init() 方法(更新类的人可能会忘记调用它)
  • @Thomas wellerif 它在构造函数之前执行,它为什么允许 this 关键字 inisde 实例初始化块。 this 是当前类对象,它会在构造函数调用完成后完全构造,对吗?
【解决方案4】:

想添加到@aioobe 的答案

执行顺序:

  1. 超类的静态初始化块

  2. 类的静态初始化块

  3. 超类的实例初始化块

  4. 超类的构造函数

  5. 类的实例初始化块

  6. 类的构造函数。

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

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

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

【讨论】:

  • 所以如果我创建了 10 个 SomeClass 实例,步骤 1 和 2 只执行一次,直到某些事情导致类被卸载(我唯一能想到的就是重新启动程序,但如果有其他可能导致这种情况的事情,我想知道)。
  • 子类会继承实例初始化块吗?
  • 不,继承不适用于初始化块
  • The instance initialization block is actually copied by the Java compiler into every constructor the class has - 这并不总是正确的。如果构造函数显式调用另一个构造函数,它将不会被复制。
【解决方案5】:

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

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

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

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

【讨论】:

  • TL;DR OP 只是要求对初始化块进行解释,而不是对静态变量、构造函数或您的 IDE 首选项的基本原理进行冗长的解释。
  • 有时,这些冗长的解释会出人意料地流行起来。如果那些提出原始问题的人真的需要一个冗长的解释来弄清楚他们的基础。或者,如果人们自己阅读答案,就好像它是关于给定主题的博客一样。在这种情况下,我想说,两者都不是。
  • @nenito,我认为您对已接受答案的评论具有误导性。我鼓励您将其改写为“我有一个可能感兴趣的更细微的解释”。接受的答案似乎完全正确,只是没有你的那么详细。
  • @Glen Pierce:接受的答案在我发表评论后被修改。我的句子不仅给出了答案,还给出了一些我认为对初级和中级 Java 开发人员有用的附加信息。
【解决方案6】:

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

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

【讨论】:

    【解决方案7】:
    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
    

    【讨论】:

      【解决方案8】:

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

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

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

      我们在 Java 中使用静态块。

      【讨论】:

        【解决方案9】:

        只是为了补充 @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
        

        【讨论】:

          【解决方案10】:

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

          【讨论】:

            猜你喜欢
            • 2023-03-10
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多