欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

深刻理解java中new一个对象的执行过程及类的加载顺序

程序员文章站 2022-07-11 20:58:52
...

深刻理解java中new一个对象的执行过程及类的加载顺序

重点看下面的例子 看不懂再看上面的知识

构造函数

public HelloA(){//构造函数
    }

关于构造函数,以下几点要注意:
1.对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的。
2.构造函数的作用是用于给对象进行初始化。
3.一个对象建立,构造函数只运行一次,而一般方法可以被该对象调用多次。

构造代码块

{//构造代码块    
}

关于构造代码块,以下几点要注意:

  1. 构造代码块的作用是给对象进行初始化。

  2. 对象一建立就运行构造代码块了,而且优先于构造函数执行。这里要强调一下,有对象建立,才会运行构造代码块,类不能调用构造代码块的,而且构造代码块与构造函数的执行顺序是前者先于后者执行。

  3. 构造代码块与构造函数的区别是:构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。

静态代码块

关于静态代码块,要注意的是:

  1. 它是随着类的加载而执行,只执行一次,并优先于主函数。具体说,静态代码块是由类调用的。类调用时,先执行静态代码块,然后才执行主函数的。

  2. 静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的。

  3. 静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别。

  4. 一个类中可以有多个静态代码块。

    main函数

    main函数详解

总结:

  1. new对象时,会先加载改对象所属类中的静态初始化块(只执行一次),然后执行super(), 最后才执行该类的构造代码块和构造器。

  2. 静态代码块:用static声明,jvm加载类时执行,仅执行一次
    构造代码块:类中直接用{}定义,每一次创建对象时执行。

  3. 执行顺序优先级:静态块,main(),构造块,构造函数。

  4. 对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器。

    注意:

    1. 静态变量和静态代码块的初始化顺序:谁在前面先初始化谁

    2. 如果先加载父类:父类静态初始化块 > 子类静态初始化块 > 父类实例初始化块 > 父类构造器 > 子类实例初始化块 > 子类构造器

    3. 类中的静态方法只随类的加载而加载,只有在调用时才执行

以下代码中的注释是对先加载父类的描述

public class EXA {
        private static EXA A=new EXA();//1
        static {
            System.out.println("父类--静态代码块");//2
        }
        public EXA() {
            System.out.println("父类--构造函数");//1.2  3.2.1.2  5.2
        }
        {
            System.out.println("父类--非静态代码块");//1.1  3.2.1.1  5.1
        }
        //3
        public static void main(String[] args) {
            System.out.println(666);//3.1
                new EXB();//3.2 加载类 就会执行子类静态初始化块 然后super() 再执行EXB类中构造代码块和执行构造器
        }
    }
public class EXB extends EXA {
        private static EXB B=new EXB();//3.2.1 先super() 这里就不会加载EXB类中的静态初始化块了 因为只会被加载一次
        static {
        System.out.println("子类--静态代码块");//4
        }
        {
        System.out.println("子类--非静态代码块");//3.2.1.3  6.1
        }
        public EXB(){
            System.out.println("子类--构造函数");//3.2.1.4  6.2
        }
}

先加载父类的运行结果:

加载父类(EXA)的静态部分 按顺序即先加载new EXA() (这里的super() 是调用Object类中的方法)再先后执行EXA中的构造代码块和构造函数

父类–非静态代码块
父类–构造函数
父类–静态代码块 按顺序继续执行父类的静态代码块
666 到这之前还没有出现子类的加载 所以在加载完父类的静态部分后就执行 main函数
父类–非静态代码块 执行new EXB() 按顺序加载子类(EXB)中的静态部分 即new EXB()注意这里执行的是子类中的 执行super() 父类已经加载过了 所以先后执行父类的构造代码块和构造函数
父类–构造函数
子类–非静态代码块 再执行子类中的构造代码块和构造函数
子类–构造函数
子类–静态代码块 按顺序继续执行子类的静态代码块
父类–非静态代码块 按顺序加载完EXB中的静态部分后继续执行main函数中new EXB()的super() 即先后执行父类的构造代码块和构造函数
父类–构造函数
子类–非静态代码块 执行完super() 后再执行EXB中的构造代码块和构造函数
子类–构造函数

先加载子类的运行结果:

加载子类(EXB)的静态部分 按顺序即先加载new EXB()

父类–非静态代码块 (new EXB() 加载父类的静态部分 按顺序先加载new EXA() (这里的super() 是调用Object类中的方法) 再先后执行EXA中的构造代码块和构造函数 )
父类–构造函数
父类–静态代码块 按顺序加载父类的静态部分
父类–非静态代码块 new EXB() 执行super() 再次执行父类的构造代码块和构造函数
父类–构造函数
子类–非静态代码块 new EXB() 执行该类(EXB)的构造代码块和构造函数
子类–构造函数
子类–静态代码块 加载完 new EXB() 后继续加载静态代码块
666 main函数优先级比静态代码块低 所以加载完相关类的所有静态部分后最后才执行main函数部分
父类–非静态代码块 main函数里的new EXB() 还是先执行super() 进而执行EXA中的构造代码块和构造函数
父类–构造函数
子类–非静态代码块 执行完super()后 才执行EXB中的构造代码块和构造函数
子类–构造函数

总之:

  1. 静态初始化块只是在类加载时执行一次,对类实例化时并不会再执行

  2. 静态初始化块,在类被JVM的类加载器加载时就被执行了,

    而实例初始化块和构造器是在类实例化对象(例如new)时才被执行的,并且每new一次就执行一次。