当前位置: 首页 > 知识库问答 >
问题:

类中的静态字段初始化序列

韦睿
2023-03-14

当一个类本身有一个静态实例时,我很难理解初始化顺序。另外,为什么字符串的这种行为似乎有所不同。

请看以下示例:

public class StaticCheck {
    private static StaticCheck INSTANCE = new StaticCheck();    

    private static final List<String> list =
        new ArrayList<String>(Arrays.asList("hello"));
    private static final Map<String, String> map =
        new HashMap<String, String>();  
    private static final  String name = "hello";

    public static StaticCheck getInstance() {
        return INSTANCE;
    }

    private StaticCheck() {
        load();     
    }

    private void load() {
        if(list != null) {
            System.out.println("list is nonnull");
        } else {
            System.out.println("List is null");
        }
        if(name != null) {
            System.out.println("name is nonnull");
        } else {
            System.out.println("name is null");
        }
        if(map != null) {
            System.out.println("Map is nonnull");
        } else {
            System.out.println("Map is null");
        }
    }

    public static void main(String[] args) {
        StaticCheck check = StaticCheck.getInstance();
    }
}

输出:

List is null
name is nonnull
Map is null

我完全不清楚为什么name字段不为空。静态字段在以下情况下初始化,如类初始化中所述:http://javarevisited.blogspot.in/2012/07/when-class-loading-initialization-java-example.html

看看上面的例子,我的想法:

>

  • 如前所述,静态字段在Java的实例初始化之前初始化。在这里,当我调用静态方法getInstance()时,它将导致类初始化,这意味着静态字段的初始化。在这种情况下,字段mapandlist不应该为空。

    在上面的示例中,由于字段实例是静态的,因此当其他字段未初始化时,它的对象初始化发生,并且它的构造函数调用load()。因此,listmap字段为空。那么为什么name被初始化了呢?我有点困惑。


  • 共有3个答案

    米丰
    2023-03-14

    编译时只会分配基元类型和字符串,并且仅当字段是final并用文本或常量表达式初始化时才会分配。所有其他静态字段和块稍后将按顺序进行计算。看这个例子:

    public class StaticExample {
    
        private static StaticExample instance1 = new StaticExample(1);
    
        public static void main(String[] args) {
            new StaticExample(3);
        }
    
        public static boolean b = true;
    
        public static final boolean fb = true;
    
        public static Boolean B = true;
    
        public static final Boolean fB = true;
    
        public static String S = "text";
    
        public static final String fS = "text";
    
        public static final String cS = "te" + "xt"; // constant expression
    
        public static final String xS = fS.substring(0, 2) + fS.substring(2, 4);
    
        private static StaticExample instance2 = new StaticExample(2);
    
        private StaticExample(int no) {
            System.out.println("## " + no + ": ##");
            System.out.println(" b: " + b);
            System.out.println("fb: " + fb);
            System.out.println(" B: " + B);
            System.out.println("fB: " + fB);
            System.out.println(" S: " + S);
            System.out.println("fS: " + fS);
            System.out.println("cS: " + cS);
            System.out.println("xS: " + xS);
            System.out.println();
        }
    
    }
    

    输出:

    ## 1: ##
     b: false
    fb: true
     B: null
    fB: null
     S: null
    fS: text
    cS: text
    xS: null
    
    ## 2: ##
     b: true
    fb: true
     B: true
    fB: true
     S: text
    fS: text
    cS: text
    xS: text
    
    ## 3: ##
     b: true
    fb: true
     B: true
    fB: true
     S: text
    fS: text
    cS: text
    xS: text
    

    fb是最终原语,fScS是常量最终字符串,只有这三个字段是预先分配的。

    壤驷心思
    2023-03-14

    常量静态变量在初始化任何非静态变量之前初始化。JLS第12.4.2节规定了类的初始化过程:

    (此处的其他步骤)

    因此,实例首先以文本形式列出,然后是列表映射,以及名称。为什么三个都不是空的?这是因为name由一个常量表达式初始化;它是一个常量变量。它首先被初始化,在实例之前,因为它是一个常量变量。

    请注意,您可以将初始化实例的行移动到列表映射之后,使列表映射实例之前初始化。

    钱均
    2023-03-14

    Stringtypename变量是一个编译时常量,编译器在编译时将其内联。所以,条件是:

    if (name != null)
    

    汇编后将成为:

    if ("hello" != null)
    

    这当然是真的。

    至于为什么maplistnull,那是因为在初始化类时,INSTANCE字段被初始化,调用构造函数,构造函数反过来调用load()方法。请注意,此时,其他静态初始值设定项尚未运行。所以,maplist仍然是null。因此,在load()方法中打印它们将是null

     类似资料:
    • 静态字段何时初始化?如果我从未实例化过一个类,但我访问了一个静态字段,那么用于实例化私有静态字段的所有静态块和私有静态方法(按顺序)都在那一刻被调用了吗?或者什么时候启动应用程序?

    • 问题内容: 我尝试了解通过引用同一封闭类对象初始化静态字段时初始化顺序的行为。 上面这段代码的输出是: 如果我将变量修改为除plain之外的其他任何内容: 输出为: 为什么会这样呢? 请注意,即使同时声明了两者,输出也是如此,在这种情况下,声明之前 问题答案: 静态最终成员先于其他静态成员初始化。 非最终静态成员按出现顺序初始化 因此,在您的第一种情况下: 构造函数在初始化之前首先被调用,因此被显

    • 我想将一些对象实例绑定到使用Javassist创建的类。该对象是从某个源读取的,数据是预先未知的。 但当我检查API时,Javassist似乎创建了一个真正的字节码,它以“调用这个”或“实例化那个”或“使用这个常量”的形式存储初始化。 有没有办法让Javassist向运行时给定的现有实例添加一个初始化的静态字段?

    • 问题内容: 好吧,所以说我有一个看起来像这样的课: 我可以依靠类加载器来按顺序初始化那些字段,以便我可以依靠SERVLET_LOGGER在signupObservableAgent之前实例化吗? 问题答案: 是的,它们按照它们在源中出现的顺序进行了初始化。您可以在Java语言规范§12.4.2中阅读所有详细内容。请参阅步骤9,内容为: …以文本顺序执行类的类变量初始值设定项和静态初始值设定项,或者

    • 问题内容: 在此代码中,我得到一个编译器错误,请参见注释: 有没有一种方法可以使这项工作有效,可以通过其中一个字段从a获取枚举值? 问题答案: 该地图在这里可能是过大的。除非您打算使用四个以上的枚举值,否则可以通过简单地遍历有效字符串并返回正确的字符串来实现getByTracName(String tn)。如果映射键始终是枚举名称,则可以执行以下操作: } 或者您可以执行以下操作:

    • 问题内容: 我正在尝试发现初始化发生的顺序,或者更确切地说,为什么要按此顺序进行初始化的原因。给定代码: 输出: 但是,将的声明移动到初始化块之前会产生: 而且我完全不知道为什么会以这种顺序发生。此外,如果我在的声明中消除了关键字,则init块和构造函数均不会触发。谁能帮我这个忙吗? 问题答案: 我认为您只是缺少JLS的12.4.2节,其中包括: 接下来,以文本顺序执行类的类变量初始化器和静态初始