【问题标题】:Why do static and instance init blocks in Enums behave differently from those in Classes为什么枚举中的静态和实例初始化块的行为与类中的不同
【发布时间】:2014-01-04 09:02:33
【问题描述】:

在学习 Java 认证测试时,我了解到静态初始化块在加载类时运行一次,按照在源代码中出现的顺序,实例初始化块在每次创建实例时运行,而构造函数中的代码之后每次创建实例时运行。为了测试我创建了一个带有一些静态和实例初始化块的类以及一个带有打印内容的构造函数。一切都按预期工作——除了我认为“加载”只是在运行时,但我猜它发生在创建第一个实例时,因为除非我创建至少一个类的实例,否则我根本没有得到任何输出。然后我用枚举尝试了同样的操作,订单全部关闭。首先,初始化块在代码中第一次引用枚举时为枚举所具有的每个值运行一次,其次——标记为静态的初始化块在我假设是实例初始化块之后运行!这与我的预期相反。这是我的问题的细分。

  1. 为什么标记为静态的初始化块在枚举中最后运行?
  2. 枚举可以有实例初始化块吗?
  3. 为什么我认为是实例初始化块的块在加载枚举时只运行一次,而不是每次引用新的枚举值时运行?
  4. 类静态初始化块在“加载”类时运行。加载是什么意思?在类中实例化对象时是否只发生一次?

谢谢!这让我很困惑。

public class EnumInit {
public static void main(String[] args) {
    System.out.println(Color.RED.toString() + " Main");
    MyInit myInit = new MyInit();
    System.out.println(Color.BLUE.toString() + " Main");
    MyInit mySecondInit = new MyInit();

}
}

enum Color {    
RED, BLUE, GREEN;
String instanceVar = "Enum Instance Variable Text";
static { System.out.println("Enum Static init block 1"); }
{ System.out.println("Enum Instance init block 1"); }
static { System.out.println("Enum Static static init block 2"); }
Color() { 
    System.out.println(instanceVar);
    System.out.println("Enum String Literal"); 
}
{ System.out.println("Enum Instance init block 2"); }   
}

class MyInit {
String instanceVar = "Class Instance Variable Text";
static { System.out.println("Class Static init block 1"); }
{ System.out.println("Class Instance init block 1"); }
static { System.out.println("Class Static static init block 2"); }
MyInit() { 
    System.out.println(instanceVar);
    System.out.println("Class String Literal"); 
}
{ System.out.println("Class Instance init block 2"); }  
}

【问题讨论】:

  • for #4:类在有意义的使用之前不会加载。这可能是调用类上的静态方法,或创建实例,或调用 Class.forName() 或其他。我相信这是为了将当前加载到永久生成中的类数量保持在最低限度。

标签: java enums initialization scjp open-closed-principle


【解决方案1】:

Java Language Specification says this about enum constants

除了枚举类型 E 继承自 Enum 的成员之外, 对于每个声明的名为 n 的枚举常量,枚举类型都有一个 隐式声明的名为 n 的类型 E 的公共静态最终字段。这些 字段被视为以与 相应的枚举常量,在任何静态字段显式之前 在枚举类型中声明。 每个这样的字段都被初始化为枚举 对应的常数。

所以

enum Color {    
    RED, BLUE, GREEN;
    ...
}

其实是

public static final Color RED = new Color();
public static final Color BLUE = new Color();
public static final Color GREEN = new Color();

这将在您拥有的 static 块之前得到评估。

为什么标记为静态的初始化块在枚举中最后运行?

见上文。

枚举可以有实例初始化块吗?

是的,编译你的代码,你会看到的。

为什么我认为是实例初始化块的块在加载枚举时只运行一次,而不是每次引用新的枚举值时?

一旦枚举类型被初始化,枚举常量就会被创建(实例化)。任何时候都不会创建新的enum

Color color = Color.RED;

您只是在引用一个已经创建的现有对象。

类静态初始化块在“加载”类时运行。加载是什么意思?在类中实例化对象时是否只发生一次?

当一个类在JVM中第一次被引用时,它被ClassLoader加载并初始化。 Read more about it here.

【讨论】:

  • 那么当每个隐式构造函数运行时,没有标记为静态的init块是否运行,然后静态块最后运行?
  • @paniclater 是的,这些实例初始化程序块在隐式调用 new Color(); 时运行。由于enum 常量是您在enum 的主体中声明的第一件事,因此这些常量将始终发生在enum 中声明的任何static 块之前。
  • 谢谢,这实际上使这个过程非常清楚。
  • @paniclater 不客气。考虑接受答案,除非有什么不清楚的地方。
【解决方案2】:

enum 中,static init block 最后执行。

Enums
    1st  - instance init block 
    2nd  - constructor block
    last - static init block or any other declared static (only once and will be always last executed)

Classes
    1st - static init block or any other declared static (only once, always first to be executed)
    2nd - instance init block
    3rd - constructor

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多