Java-子父类初始化顺序

子父类初始化顺序

Java基础知识科普:

Java中的对象有两种类型:编译时类型和运行时类型。编译时类型指在声明对象时所采用的类型,运行时类型指为对象赋值所才采用的类型。

在如下代码中,person对象的编译时类型为Person,运行时类型为Student,因此无法在编译时获取在Student类中定义的方法:

Person person = new Student();

原题

背景:在头条上看到如下的一道题。请问输出的结果是什么?

public class Test {

    static class Father {
        public int money = 1;

        public Father() {
            money = 2;
            showMoney();
        }

        public void showMoney() {
            System.out.println("F money:" + money);
        }
    }


    static class Son extends Father {
        public int money = 3;

        public Son() {
            money = 4;
            showMoney();
        }

        @Override
        public void showMoney() {
            System.out.println("S money:" + money);
        }
    }

    public static void main(String[] args) {
        Father son = new Son();
        System.out.println("main money:" + son.money);
    }
}

输出的结果:

S money:0
S money:4
main money:2

要想知道为什么会输出这个结果,需要熟练知道子父类初始化顺序。

分析

public class Test {

    static class Father {
        public int money = 1;

        public Father() {
            System.out.println("=======Father Construction Method Start========");
            money = 2;
            showMoney();
            System.out.println("=======Father Construction Method End========\n");
        }

        public void showMoney() {
            System.out.println("F money:" + money);
        }
    }


    static class Son extends Father {
        public int money = 3;

        public Son() {
            System.out.println("=======Son Construction Method Start========");
            money = 4;
            showMoney();
            System.out.println("=======Son Construction Method End========\n");
        }

        @Override
        public void showMoney() {
            System.out.println("S money:" + money);
        }
    }

    public static void main(String[] args) {
        System.out.println("=======Main Method Start========");
        Father son = new Son();
        System.out.println("main money:" + son.money);
        System.out.println("=======Main Method End========");
    }
}

输出的结果如下。我们按输出内容进行解析:

  1. Main Method Start:表示进入了main方法体
  2. Father Construction Method Start:当我们通过Son的构造方法创建Son对象时,需要先执行父类的构造方法。进入了父类的构造方法有此输出
  3. S money:0:执行父类构造方法时,其中执行了showMoney方法;此方法被子类重写了,根据运行时类型,实际创建的对象是子类,所以调用此方法时会去调用子类中重写的showMoney方法;执行子类的showMoney方法时,子类的money属性还没有初始化(构造方法执行完毕才会初始化属性)
  4. Father Construction Method End:当父类构造方法中showMoney方法执行完毕后,输出此行,表示父类构造方法执行完毕
  5. Son Construction Method Start:通过子类的构造方法创建对象时,将父类构造方法执行完毕后,就进入了子类的构造方法
  6. S money:4:在执行了money = 4;后,son对象中的money属性的值就为4了,调用showMoney方法时使用的是重写的方法.
  7. Son Construction Method End:执行完showMoney方法后,输出此行信息,表示子类的构造方法也执行完毕
  8. main money:2:son对象指向的Father类,这是编译时声明,通过对象.属性即son.money输出的就是son对象指向类中的属性。(如果指向Son类,此处输出的就是4)
  9. Main Method End:main方法执行完毕
=======Main Method Start========
=======Father Construction Method Start========
S money:0
=======Father Construction Method End========

=======Son Construction Method Start========
S money:4
=======Son Construction Method End========

main money:2
=======Main Method End========

总结

这道题中考到了:

  1. 多态情况下,通过构造方法创建对象时,是先执行子类还是父类的构造方法?
  2. 创建子类对象时,父类构造方法中,调用了被子类重写的方法,是执行子类还是父类中的方法?
  3. 先执行构造方法还是先初始化类中的属性?
  4. 通过对象.属性来获取属性值时,获取到的是子类还是父类的属性值?

通过实践证明:

  1. 通过构造方法创建对象时,先判断初始化的类对象是否是有父类;有父类,则先执行父类的构造方法。
  2. 执行方法时,总是根据运行时类型来判断到底是执行父类还是子类中的方法
  3. 在一个类中,先初始化属性再执行构造方法。存在继承关系时,先执行父类构造方法,父类构造方法执行完毕后才会初始化子类中的属性。
  4. 通过对象.属性来获取属性值时,总是根据编译时类型来判断输出父类还是子类中的属性值

验证结论2

将main方法中改造如下:

public static void main(String[] args) {
        System.out.println("=======Main Method Start========");
        Father son = new Father();
        System.out.println("main money:" + son.money);
        System.out.println("=======Main Method End========\n");
    }

输出结果:可以看到父类构造方法中调用showMoney方法时输出的是F money:2,我们调用Father构造方法时,运行时类型是Father,执行showMoney也就是Father类中的方法了。

所以这条结论是正确的。

=======Main Method Start========
=======Father Construction Method Start========
F money:2
=======Father Construction Method End========

main money:2
=======Main Method End========

验证结论4

将main方法改造如下:

public static void main(String[] args) {
    System.out.println("=======Main Method Start========");
    Son son = new Son();
    System.out.println("main money:" + son.money);
    System.out.println("=======Main Method End========\n");
}

输出结果如下,和我们原题中输出结果的差距就是main money:4,我们的改动就是将son对象指向了Son类。当Son类的构造方法执行完毕时,Son类中的money属性值为4,Father类中的money属性值为2,输出的是4。表示我们的结论是正确的。

=======Main Method Start========
=======Father Construction Method Start========
S money:0
=======Father Construction Method End========

=======Son Construction Method Start========
S money:4
=======Son Construction Method End========

main money:4
=======Main Method End========

综上所述

综上结论,我们只需要知道:父类的构造方法优先于子类构造方法创建,父类构造方法优先于子类属性加载,执行方法时使用的是运行时类型,对象.属性使用的是编译时类型。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值