饿汉式单例类与Static变量加载顺序

来源:互联网 发布:淘宝网购物蜜腊 编辑:程序博客网 时间:2024/04/30 12:56
public class App {    private static final App appInstance=new App();    private static String test="final test";            private App(){    System.out.println("test final variable: "+test);    }        public static App getInstance(){    return appInstance;    }    public static void main( String[] args )    {        App app=App.getInstance();        System.out.println("test end...");    }}

上述程序,会打印出 test final variable: null, 原因是,在类装载过程中,当初始化appInstance变量时,对App构造函数进行初始化,初始化的内容又是要使用test变量,此时test变量被分配了空间,但未被赋值,所以是null。

如果去掉变量test的static修饰符,结果会是test final variable: final test,初始化顺序到对App构造函数前都一样,但在调用构造函数之前会去初始化本地变量,初始化之后再对构造函数中的打印语句进行调用。

而如果去掉static换成final或者保留static再加上final,结果还是能打印正确结果,这是因为作为被final修饰的原始类型的变量,在编译时被替换到使用它的地方。(如果是其他对象引用,可以在运行时被初始化)


附上java类初始化顺序:

1)遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这四条字节码指令最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。

2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。

3)当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。

这四种场景中的行为称为对一个类进行主动引用,除此之外所有引用类的方式,都不会触发类的初始化,被称为被动引用。以下是三个例子:

1)通过子类引用父类的静态字段,不会导致子类初始化。

2)通过数组定义来引用类,不会触发此类的初始化。

3)常量在编译阶段会存入调用类的常量池,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。