简体   繁体   English

Java初始化顺序问题,静态vs实例字段

[英]Java initialization order issue, static vs instance fields

The program below prints: 以下程序打印:

my name is:null

my name is:null

Someclass static init

AFAIK when a class is first loaded static blocks and fields are always initialized first, instance blocks and fields second. 首次加载类时的AFAIK静态块和字段始终首先被初始化,实例块和字段被初始化。 Therefore variables "objectName1" and "objectName2" should be initialized first, instance variable "list" second...but the ouput obviously contradicts this theory... Can anyone explain the program behavior (I'm not looking for a critique of the design in itself btw) ? 因此变量“objectName1”和“objectName2”应该首先被初始化,实例变量“list”第二......但是输出明显与这个理论相矛盾...任何人都可以解释程序行为(我不是在寻找对它的批评设计本身btw)?

import java.util.ArrayList;
import java.util.List;

public class Main2{
    public static void main (String[] args){
        SomeClass.getInstance();
    }
}

class SomeClass {
    private static final SomeClass instance = new SomeClass();

    public static SomeClass getInstance(){
        return instance;
    }

    static {
        System.out.println ("Someclass static init");
    }
    private  static String objectName1  ="test1";
    private  static String objectName2  ="test2";

    @SuppressWarnings("serial")
    private  List<SomeObject> list=
        new ArrayList<SomeObject> ()  { {
 add (new SomeObject(objectName1));
 add (new SomeObject(objectName2));
    }};
}

class SomeObject {
    String name;
    SomeObject (String name){
        this.name = name;
        System.out.println ("my name is:" +name);
    }
}

Static blocks are initialized in order (so you can rely on the ones above in the ones below). 静态块按顺序初始化(因此您可以依赖上面的那些)。 By creating an instance of SomeClass as your first static initializer in SomeClass , you're forcing an instance init during the static init phase. 通过创建实例SomeClass作为你的第一个静态初始化SomeClass ,你在静态初始化阶段迫使初始化一个实例。

So the logical order of execution of your code is: 因此,代码执行的逻辑顺序是:

  • Load class SomeClass , all static fields initially defaults ( 0 , null , etc.) 加载类SomeClass ,所有静态字段最初默认为( 0null等)
  • Begin static inits 开始静态
  • First static init creates instance of SomeClass 第一个静态init创建SomeClass实例
  • Begin instance inits for SomeClass instance, using current values for static fields (so objectName1 and objectName2 are null ) 使用静态字段的当前值开始SomeClass实例的实例内容(因此objectName1objectName2null
  • Load SomeObject class, all static fields initially default (you don't have any) 加载SomeObject类,所有静态字段最初默认(你没有)
  • Do SomeObject static inits (you don't have any) SomeObject静态SomeObject (你没有)
  • Create instances of SomeObject using the passed-in null values 使用传入的null值创建SomeObject实例
  • Continue static inits of SomeClass , setting objectName1 and objectName2 继续SomeClass静态内容,设置objectName1objectName2

To make this work as you may expect, simply put the inits for objectName1 and objectName2 above the init for instance . 为了使这项工作,你可能期望的那样,简单地把对inits objectName1objectName2初始化以上instance

As suggested moving this line: 正如建议移动这一行:

private static final SomeClass  instance    = new SomeClass();

after these: 在这之后:

private  static String objectName1  ="test1";
private  static String objectName2  ="test2";

should fix the problem. 应该解决问题。

On first look I was pretty surprised about the behavior myself, but on second thought, it is quite trivial to explain: 初看起来我对自己的行为感到非常惊讶,但是第二个想法,解释起来是非常简单的:

private static final SomeClass instance = new SomeClass();

is part of the static initialization of SomeClass . SomeClass的静态初始化的一部分。 As you create an instance before initialization has completed, the class is not yet completely initialized. 在初始化完成之前创建实例时,该类尚未完全初始化。 When you replace the System.out.println(...); 当您替换System.out.println(...); with something like new Exception().printStackTrace(); 有一些像new Exception().printStackTrace(); you get this (note that I put all classes as static nested classes into Main) 你得到这个(注意我把所有类作为静态嵌套类放入Main)

at Main$SomeObject.<init>(Main.java:37) // new Exception().printStackTrace();
at Main$SomeClass$1.<init>(Main.java:26) // add(new SomeObject(...))
at Main$SomeClass.<init>(Main.java:23) // list = new ArrayList()
at Main$SomeClass.<clinit>(Main.java:10) // instance = new SomeClass()
at Main.main(Main.java:6) // SomeClass.getInstance();

As you see, execution still is inside Main$SomeClass.<clinit> (the class initialization), hence SomeClass is not completely initialized. 如您所见,执行仍然在Main$SomeClass.<clinit> (类初始化)中,因此SomeClass未完全初始化。

As a side note: the best way to implement Singleton pattern is to avoid it completely. 作为旁注:实现Singleton模式的最佳方法是完全避免它。 The second best most likely is using enum (at least it's Josh-Bloch-approved) 第二个最有可能是使用enum (至少它是Josh-Bloch批准的)

class enum SomeClass {
    instance;

    // snip
}

The first thing that executes is probably the static initializer of the instance variable. 执行的第一件事可能是instance变量的静态初始化程序。 This causes the list to be initialized using the (uninitialized) objectName1 and objectName2 variables. 这会导致使用(未初始化的) objectName1objectName2变量初始化列表。 After that, it proceeds to initialize objectName1 and objectName2 . 之后,它继续初始化objectName1objectName2

If you move the declaration of instance to the end of SomeClass it will probably do what you're expecting. 如果将instance的声明移动到SomeClass的末尾,它可能会执行您期望的操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM