Java类的加载和实例化流程

类的加载和实例化流程

类加载流程

符号引用

符号引用(Symbolic Reference)是一种用来描述引用目标的一组符号,可以是任何形式的字面量,比如类和接口的全限定名、字段的名称和描述符、方法的名称和描述符等。符号引用是在编译期或者运行期间生成的,它不依赖于具体的内存地址,而是在运行时根据上下文信息来定位目标。符号引用的作用是为了在程序运行时能够找到对应的目标。

直接引用

直接引用(Direct Reference)则是一种直接指向目标的内存地址或者偏移量,它可以是指向对象实例的指针、指向类的静态变量的指针、指向类的方法的指针等。直接引用是在程序运行时生成的,它依赖于具体的内存地址,可以直接被 CPU 所执行。

类加载实例

当Java的初始化类时,会优先初始化静态成员,如静态成员变量和静态代码块,随后初始化定义的变量,再初始化构造方法。

public class Bowl {
    Bowl(int marker) {
        System.out.println("Bowl(" + marker +")");
    }

    void f1(int marker) {
        System.out.println("f1(" + marker + ")");
    }
}
public class Table {
    static Bowl bowl1 = new Bowl(1);

    Table() {
        System.out.println("Table()");
        bowl2.f1(1);
    }

    void f2(int marker) {
        System.out.println("f2(" + marker + ")");
    }

    static Bowl bowl2 = new Bowl(2);
}
public class Cupboard {
    Bowl bowl3 = new Bowl(3);
    static Bowl bowl4 = new Bowl(4);

    Cupboard() {
        System.out.println("Cupboard()");
        bowl4.f1(2);
    }

    void f3(int marker) {
        System.out.println("f3(" + marker + ")");
    }

    static Bowl bowl5 = new Bowl(5);
}
public class StaticInitialization {
    public static void main(String[] args) {
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        System.out.println("Creating new Cupboard() in main");
        new Cupboard();
        table.f2(1);
        cupboard.f3(1);
    }

    static Table table = new Table();
    static Cupboard cupboard = new Cupboard();
}

上述代码执行结果如下

Bowl(1)
Bowl(2)
Table()
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
Creating new Cupboard() in main
Bowl(3)
Cupboard()
f1(2)
f2(1)
f3(1)

将StaticInitialization中其他代码注释掉,只保留Cupboard的实例化,输出结果如下

public class StaticInitialization {
    public static void main(String[] args) {
//        System.out.println("Creating new Cupboard() in main");
//        new Cupboard();
//        System.out.println("Creating new Cupboard() in main");
//        new Cupboard();
//        table.f2(1);
//        cupboard.f3(1);
    }

//    static Table table = new Table();
    static Cupboard cupboard = new Cupboard();
}
Bowl(4)
Bowl(5)
Bowl(3)
Cupboard()
f1(2)

要启动main方法,必须先加载StaticInitialization类,此时会先加载其静态域,静态域中Cupboard被实例化,会进入Cupboard类中进行加载,此时会加载Cupboard中的静态域中的代码,对Bowl类进行实例化。
Bowl类中没有静态域,也没有其他成员变量,直接调用构造方法。
bowl4和bowl5实例化完成,在Cupboard类中继续对成员变量bowl3进行初始化,再次加载Bowl类,调用构造方法,完成bowl3的实例化。
bowl3初始化完成后,调用Cupboard的构造方法,构造方法中完成控制台打印和对bowl4.f1()方法的调用,完成该静态域的初始化。
去掉注释,当再次实例化Cupboard类时,不再初始化静态成员,只初始化非静态成员变量和调用构造方法

Bowl(3)
Cupboard()
f1(2)

注释其他代码,只保留静态成员table的声明和实例化

public class StaticInitialization {
    public static void main(String[] args) {
//        System.out.println("Creating new Cupboard() in main");
//        new Cupboard();
//        System.out.println("Creating new Cupboard() in main");
//        new Cupboard();
//        table.f2(1);
//        cupboard.f3(1);
    }

    static Table table = new Table();
//    static Cupboard cupboard = new Cupboard();
}

执行结果如下

Bowl(1)
Bowl(2)
Table()
f1(1)

静态成员的初始化是在所有其他代码之前的,无论写在哪个位置,静态成员总是先初始化

非静态实例初始化

public class Mug {
    Mug(int marker) {
        System.out.println("Mug(" + marker + ")");
    }

    void f(int marker) {
        System.out.println("f(" + marker + ")");
    }
}
public class Mugs {
    Mug mug1;
    Mug mug2;

    {
        mug1 = new Mug(1);
        mug2 = new Mug(2);
        System.out.println("mug1 & mug2 initialized");
    }

    Mugs() {
        System.out.println("Mugs()");
    }

    Mugs(int i) {
        System.out.println("Mugs(int)");
    }

    public static void main(String[] args) {
        System.out.println("Inside main()");
        new Mugs();
        System.out.println("new Mugs() completed");
        new Mugs(1);
        System.out.println("new Mugs(1) completed");
    }
}

Mugs类中的代码段

{
    mug1 = new Mug(1);
    mug2 = new Mug(2);
    System.out.println("mug1 & mug2 initialized");
}

没有被static关键字修饰,其并不是一个显示静态代码块,但是在实例化过程中也作为静态代码被加载了

Inside main()
Mug(1)
Mug(2)
mug1 & mug2 initialized
Mugs()
new Mugs() completed
Mug(1)
Mug(2)
mug1 & mug2 initialized
Mugs(int)
new Mugs(1) completed

类初始化流程

请添加图片描述

继承中的类初始化

当Java要初始化一个子类时,编译器会自动加载其基类,若基类还有基类,会继续向上加载,直到找到最终的基类,再从上而下加载所有类,加载顺序与上述顺序相同

public class Insect {
    private int i = 9;
    protected int j;

    Insect() {
        System.out.println("i = " + i + ", j = " + j);
        j = 39;
    }

    private static int x1 = printInit("static Insect.x1 initialized");

    static int printInit(String s) {
        System.out.println(s);
        return 47;
    }
}
public class Beetle extends Insect {
    private int k = printInit("Beetle.k initialized");

    public Beetle() {
        System.out.println("k = " + k);
        System.out.println("j = " + j);
    }

    private static int x2 = printInit("static Beetle.x2 initialized");

    public static void main(String[] args) {
        System.out.println("Beetle constructor");
        Beetle beetle = new Beetle();
    }
}

上述代码中,要运行Beetle类的静态方法main,先加载该类,在加载时发现该类有一个基类Insect,因此先加载Insect类,先加载静态成员x1,打印信息并将返回值赋值给x1,完成对Insect的静态资源的加载。
随后执行main方法,执行控制台打印和beetle对象实例化,实例化对象时,基类的成员变量先被初始化,并对没有初始化赋值的变量赋值默认值,然后在子类中没有显示调用父类的构造方法,JVM会默认调用一次父类构造方法,而后完成对基类的初始化。
随后会初始化子类的成员变量,调用构造方法完成初始化。
代码输出结果如下

static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
i = 9, j = 0
Beetle.k initialized
k = 47
j = 39
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值