java 类初始化顺序总结和分析

  • 单个类时 静态变量/静态代码块(以程序定义顺序为准)-->main方法(静态方法)-->实例变量/实例代码块(同样以程序定义顺序为准)-->构造方法(constructor)

    public class ClassLoadTest {
    
        private static User user = new User();
        static {
            System.out.println("static code block");
        }
    
        {
            System.out.println("code block");
        }
        private Student student = new Student();
    
        public ClassLoadTest(){
            System.out.println("ClassLoadTest Constructor");
        }
    
        public static void main(String[] args) {
            System.out.println("main ");
            new ClassLoadTest();
        }
    
    }
    
    class Student{
        public Student(){
            System.out.println("student init");
        }
    }
    class User{
        public User(){
            System.out.println("user init");
        }
    }

    输出结果:
    user init (初始化静态user变量,加载user构造方法)
    static code block(ClassLoadTest静态代码块)
    main (ClassLoadTes静态main方法)
    code block(ClassLoadTest实例化代码块)
    student init(实例化student变量,加载student构造方法)
    ClassLoadTest Constructor(假造ClassLoadTest构造方法)

  • 继承类时,父类静态变量/静态代码块-->子类静态变量/子静态代码块-->父类变量/实例代码块-->父类构造方法-->子类变量/实例代码块-->子类构造方法
     

    //父类
    public class Parent {
    
        public static String p_StaticField = "父类--静态变量";
    
        public String p_Field = "父类--变量";
        protected int i = 9;
        protected int j = 0;
    
        static {
            System.out.println( p_StaticField );
            System.out.println( "父类--静态代码块" );
        }
    
        {
            System.out.println( p_Field );
            System.out.println( "父类--实例代码块" );
        }
    
        public Parent()
        {
            System.out.println( "父类--构造方法" );
            System.out.println( "i=" + i + ", j=" + j );
            j = 20;
        }
    }
    
    //子类
    public class SubClass extends Parent {
    
        public static String s_StaticField = "子类--静态变量";
    
        public String s_Field = "子类--变量";
    
        static {
            System.out.println( s_StaticField );
            System.out.println( "子类--静态代码块" );
        }
    
        {
            System.out.println( s_Field );
            System.out.println( "子类--实例代码块" );
        }
    
        public SubClass()
        {
            System.out.println( "子类--构造方法" );
            System.out.println( "i=" + i + ",j=" + j );
        }
    
        public static void main( String[] args )
        {
            System.out.println( "子类main方法" );
            new SubClass();
        }
    }

    输出结构:
    父类--静态变量
    父类--静态代码块
    子类--静态变量
    子类--静态代码块
    子类main方法
    父类--变量
    父类--实例代码块
    父类--构造方法
    i=9, j=0
    子类--变量
    子类--实例代码块
    子类--构造方法
    i=9,j=20

    分析:继承情况下,在装载SubClass类时,发现SubClass类有父类就会向上装载,如果父类还有父类会继续上线装载父类
    装载到顶层父类后,初始化父类静态变量/静态代码块 后再初始化子类静态变量/静态代码块,依次向下初始化类的静态变量/静态代码块。再执行运行的static main方法,后再执行new SubClass()实例化SubClass对象,实例化SubClass对象时也会向上查找从父类开始,实例变量和实例代码块也是跟静态变量和静态代码块一样。

  • static变量特殊情况

    public class Test {
        static {
            i = 0; // 给变量复制可以正常编译通过
            // System.out.print(i); // 这句编译器会提示“非法向前引用”
        }
        static int i = 1;
    
        static int j = 1;
    
        static{
            j = 2;
        }
    
        public static void main(String[] args){
            System.out.println(Test.i); //1
            System.out.println(Test.j); //2
        }
    }

    输出结果:
    1(以程序顺序为准,i=1在静态代码块下,所以i值为1)
    2(也是以程序顺序为准)

    分析:建议参考《深入理解Java虚拟机》第七章 7.3.5节
    方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问
    可以理解为,其实 i 是已经申明了,但是没的赋值,所以在 static block 中不能使用,但是可以对其赋值,即 a =1 中的 a = ... 没问题,但 a = a + 1 有问题(无值)
     

  • 不触发初始化实例

    public class NotInitialization {
        public static void main(String[] args) {
             System.out.println(SubClass1.value);
    
    
            /**
             *
             * 通过数组定义来引用类,不会触发此类的初始化
             **/
            SuperClass[] sca = new SuperClass[10];
        }
    }
    /**
     * 被动使用类字段演示一:
     * 通过子类引用父类的静态字段,不会导致子类初始化
     **/
    class SuperClass {
    
        static {
            System.out.println("SuperClass init!");
        }
    
        public static int value = 123;
    }
    
    class SubClass1 extends SuperClass {
    
        static {
            System.out.println("SubClass init!");
        }
    }

    输出结果:
    SuperClass init!(调用父类静态变量,初始化父类静态代码块,子类没有触发)
    123(输出父类静态变量值)

    分析:System.out.println(SubClass1.value);触发了父类的初始化,并没有触发子类的初始化
    SuperClass[] sca = new SuperClass[10];通过数组定义来引用类,不会触发此类的初始化
     

  • 常量调用不会触发定义常量的类的初始化
     

    public class MyClass {
        static {
            System.out.println("ConstClass init!");
        }
    
        public static final String HELLOWORLD = "hello world";
    }
    public class MyclassMain {
        public static void main(String[] args){
            System.out.println(MyClass.HELLOWORLD);
        }
    }

    输出结果:
    hello world

    分析:常量在编译阶段会存入调用类的常量池中【这里说的是main函数所在的类的常量池】,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。
     

  • 静态方法调用,不会触发该类的代码块或构造方法的执行
     

    public class ClassLoadTest {
    
        public static void main(String[] args) {
            // System.err.println(Handler.user);
            Handler.print();
        }
    
    }
    
    class Handler {
        public static User user = new User();
        static {
            System.err.println("static code block");
        }
        {
            System.err.println("code block");
        }
        public Handler(){
            System.err.println("Constructor");
        }
        public static void print(){
            System.err.println("static method");
        }
    }
    
    class User {
        public User() {
            System.err.println("user initing===>");
        }
    }

    输出结果:
    user initing===>
    static code block
    static method

    分析:调用Handler的静态方法,对Handler类进行了类初始化,但是不会触发该类的实例变量/实例代码块,和构造方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值