Java类的加载顺序进阶 例子

引用1个网上的经典例子并做稍许改动,直接把顺序这块的点都考了。
例子引用自:https://blog.csdn.net/zfx2013/article/details/89453482

public class Animal {
private int i = test();
private static int j = method();
static {
System.out.println(“a”);
}
Animal(){
System.out.println(“b”);
}
{
System.out.println(“c”);
}
public int test(){
System.out.println(“d”);
return 1;
}
public static int method(){
System.out.println(“e”);
return 1;
}
}
public class Dog extends Animal{
{
System.out.println(“h”);
}
private int i = test();
static {
System.out.println(“f”);
}
private static int j = method();

Dog(){
    System.out.println("g");
}
public int test(){
    System.out.println("i");
    return 1;
}
public static int method(){
    System.out.println("j");
    return 1;
}
public static void main(String[] args) {
    Dog dog = new Dog();
    System.out.println();
    Dog dog1 = new Dog();
}

}

执行这段main程序,会输出什么?
答案是
eafjicbhig
icbhig

为了方便大家一个个细节去理解, 我换一种方式去提问。
Q: 什么时候会进行静态变量的赋值和静态代码块的执行?
A:

第一次创建某个类或者某个类的子类的实例
访问类的静态变量、调用类的静态方法
使用反射方法forName
调用主类的main方法(本例子的第一次静态初始化其实属于这个情况,调用了Dog的main方法)
注: 类初始化只会进行一次, 上面任何一种情况触发后,之后都不会再引起类初始化操作。
Q:初始化某个子类时,也会对父类做静态初始化吗?顺序呢?
A:如果父类之前没有被静态初始化过,那就会进行, 且顺序是先父类再子类。 后面的非静态成员初始化也是如此。
所以会先输出eafj。

Q: 为什么父类的method不会被子类的method重写?
A: 静态方法是类方法,不会被子类重写。毕竟类方法调用时,是必定带上类名的。

Q: 为什么第一个输出的是e而不是a?
A: 因为类变量的显示赋值代码和静态代码块代码按照从上到下的顺序执行。
Animal的静态初始化过程中,method的调用在static代码块之前,所以先输出e再输出a。
而Dog的静态初始化过程中,method的调用在static代码块之后,因此先输出f,再输出j

Q: 没有在子类的构造器中调用super()时,也会进行父类对象的实例化吗?
A: 会的。会自动调用父类的默认构造器。 super()主要是用于需要调用父类的特殊构造器的情况。
因此会先进行Animal的对象实例化,再进行Dog的对象实例化

Q: 构造方法、成员显示赋值、非静态代码块(即输出c和h的那2句)的顺序是什么?
A:

成员显示赋值、非静态代码块(按定义顺序)
构造方法
因此Animal的实例化过程输出icb(如果对输出i有疑问,见下面一题)
接着进行Dog的实例化,输出hig
Q: 为什么Animal实例化时, i=test()中输出的是i而不是d?
A:因为你真正创建的是Dog子类,Dog子类中的test()方法由于签名和父类test方法一致,因此test方法被重写了。
此时即使在父类中调用,也还是用使用子类Dog的方法。除非你new的是Animal。

Q: 同上题, 如果test方法都是private或者final属性, 那么上题的情况会有变化吗??
A:
因为private和final方法是不能被子类重写的。
所以Animal实例化时,i=test输出d。

总结一下顺序:

父类静态代变量显式赋值、父类静态代码块(按定义顺序)
子类静态变量显式赋值、子类静态代码块(按定义顺序)
父类非静态变量显式赋值(父类实例成员变量)、父类非静态代码块(按定义顺序)
父类构造函数
子类非静态变量(子类实例成员变量)、子类非静态代码块(按定义顺序)
子类构造函数。
§ 类加载过程
Q:类加载的3个必经阶段是:
A:

加载(类加载器读取二进制字节流,生成java类对象)
链接(验证,分配静态域初始零值)
初始化(前面的题目讲的其实就是初始化时的顺序)
更详细的如下:
在这里插入图片描述
§ 被动引用中和类静态初始化的关系
Q:new某个类的数组时,会引发类初始化吗?
像下面输出什么

public class Test {
static class A{
public static int a = 1;
static{
System.out.println(“initA”);
}
}

public static void main(String[] args) {
    A[] as = new A[5];
}

}
A:
new数组时,不会引发类初始化。
什么都不输出。

Q:引用类的final静态字段,会引发类初始化吗?
像下面输出什么?

public class Test {
static class A{
public static final int a = 1;
static{
System.out.println(“initA”);
}
}

public static void main(String[] args) {
    System.out.println("A.a=" + A.a);
}

}

A: 不会引发。
不会输出initA。 去掉final就会引发了。
(注意这里必须是基本类型常量, 如果是引用类型产量,则会引发类初始化)

Q:
子类引用了父类的静态成员,此时子类会做类初始化嘛?
如下会输出什么

public class Test {
static class A{
public static int a = 1;
static{
System.out.println(“initA”);
}
}

static class B extends A{
    static {
        System.out.println("initB");
    }
}

public static void main(String[] args) {
    System.out.println("B.a=" + B.a);
}

}
A:
子类不会初始化。
打印initA,却不会打印initB。

§ 双亲委派
类加载时的双亲委派模型,不知道能怎么出题。。。反正就记得优先去父类加载器中看类是否能加载。
就贴个图吧
image.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值