java类的加载顺序题目_Java 类的加载顺序(题)

引例

public class A extends B {

public int a = 100;

public A() {

super();

System.out.println(a);

a = 200;

}

public static void main(String[] args) {

System.out.println(new A().a);

}

}

class B {

public B() {

System.out.println(((A) this).a);

}

}

例子代码很简单,不多做解释了,直接看输出:

0

100

200

对照这个输出,我们来详细分析一下对象的初始化顺序:

为A类分配内存空间,初始化所有成员变量为默认值,包括primitive类型(int=0,boolean=false,…)和Reference类型。

调用A类构造函数。

调用B类构造函数。

调用Object空构造函数。(java编译器会默认加此构造函数,且object构造函数是个空函数,所以立即返回)

初始化B类成员变量,因为B类没有成员变量,跳过。

执行sysout输出子类A的成员变量小a。// 此时为0

初始化A类成员变量,将A类成员变量小a赋值100。

执行sysout输出当前A类的成员变量小a。// 此时为100

赋值当前A类的成员变量小a为200。

main函数中执行sysout,输出A类实例的成员变量小a。// 此时为200

加粗的那两行描述是重点,结论是成员变量初始化是在父类构造函数调用完后,在此之前,成员变量的值均是默认值。 Dzone作者就是栽在这里,没有仔细分析成员变量初始化在对象初始化中的顺序,造成了程序未按原意执行。

其实这类问题,熟悉原理是一方面,本质上只要不在构造函数中插入过多的业务逻辑,出问题的概率也会低很多。

050adc323908

类加载简单时序图

程序的输出是什么?

public class Base {

private String baseName = "base";

public Base() {

callName();

}

public void callName() {

System.out.println(baseName);

}

static class Sub extends Base {

private String baseName = "sub";

@Override

public void callName() {

System.out.println(baseName);

}

}

public static void main(String[] args) {

Base b = new Sub();

}

}

有了前面的引例,这道题答案就比较简单了

答案: null

public class Demo {

class Super {

int flag = 1;

Super() {

test();

}

void test() {

System.out.println("Super.test() flag=" + flag);

}

}

class Sub extends Super {

// int flag;

Sub(int i) {

flag = i;

System.out.println("Sub.Sub()flag=" + flag);

}

@Override

void test() {

System.out.println("Sub.test()flag=" + flag);

}

}

public static void main(String[] args) {

new Demo().new Sub(5);

}

}

答案:

Sub.test()flag=1

Sub.Sub()flag=5

在我刚做这道题的时候也是有不少疑惑

为什么输出会是 1, 5 不应该是 0, 5

因为覆写了,没有指明 super 调用父类方法,父类的方法怎么都不可能被调用

所以自己打断点查找原因得到一个规律

我将构造函数的执行化为 三个阶段

class Super {

int flag = 1; // ②

Super() { // ①

test(); // ③

}

void test() {

System.out.println("Super.test() flag=" + flag);

}

}

通过这个规律,我将注释写在先前的代码上

public class Demo {

class Super {// ③ 除 Object 无继承,开始执行构造函数

int flag = 1; // ⑤ 加载成员变量完毕

Super() { // ④ 程序运行到这里的时候,开始加载成员变量

test(); // ⑥ 调用子类方法

}

void test() {

System.out.println("Super.test() flag=" + flag);

}

}

class Sub extends Super {// ② Sub 类有父类,继续寻找

Sub(int i) { // ⑧ 开始加载子类的构造函数

flag = i; // ⑨ 重新对父类变量 flag 赋值

System.out.println("Sub.Sub()flag=" + flag); // ⑩ 对父类变量进行输出

}

@Override

void test() {

System.out.println("Sub.test()flag=" + flag); // ⑦ 输出父类的变量 flag

}

}

public static void main(String[] args) {

new Demo().new Sub(5); // ① 开始寻找 Sub 类

}

}

PS: 这些都是简单题,还有静态变量,静态类,内部类,内部静态类,内部静态类静态变量 try catch finally return 各种混合...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值