请问下如何解释这段 Java 程序第一次输出时 I have $0?

2021-07-28 08:51:23 +08:00
 Newyorkcity
public class Main {

    static class Father {
        private int money = 1;

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

        public void showMoney() {
            System.out.println("I am father, I have $" + this.money + ".");
        }
    }

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

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

        @Override
        public void showMoney() {
            System.out.println("I am son, I have $" + this.money + ".");
        }
    }

    public static void main(String[] args) {
        final Son son = new Son();
        System.out.println("I am son, I have $" + son.money + ".");
    }
}

输出结果

I am son, I have $0.
I am son, I have $4.
I am son, I have $4.

后面两行我都能理解,但是第一次输出时为什么 money 字段是 0 ?

谢谢

1696 次点击
所在节点    问与答
12 条回复
Hurriance
2021-07-28 09:11:04 +08:00
执行子类的初始化函数的时候,先会去执行父类的初始化函数,其中就会执行 showMoney(),其实父类、子类也有 showMoney(),但是因为你是子类调用栈过来的,所以直接调用子类的 showMoney(),但是此时子类的 money 还没初始化,默认值为 0,即输出 0
qping
2021-07-28 09:14:54 +08:00
先初始化父亲, 调用了 showMoney() , showMoney 方法被子类重写了, 调用的是儿子的 showMoney , 儿子的 money 还没赋值,所以是 0
zhangshine
2021-07-28 09:19:47 +08:00
搜索“Java 类的初始化顺序”
AlkTTT
2021-07-28 09:22:09 +08:00
你这两个类都是静态的,
执行顺序为: 父类构造方法 -> 父类 showMoney -> 子类重写了 showMoney,所以调用子类的 showMoney(此时子类的参数还没执行构造方法,money 为 0)
dinghmcn
2021-07-28 09:28:26 +08:00
如果子类没有重写 showMoney()输出的是什么?
Aruforce
2021-07-28 09:32:03 +08:00
尽管 Son 和 Father 都有 money 。。但这是两个 money 。。。

第一个输出的 money 是在 Father 类初始化时尚未初始化的 Son 的 money...

而这个值应该是 0 虽然你直觉可能觉得是 3...

在 Father 的初始话代码执行的时候... 实际上 son 的 money=3 尚未执行。。所以默认值 0
NeroKamin
2021-07-28 09:34:08 +08:00
第一个 money 是执行 Father 构造方法时打印的 Son 类实例中的 money 字段,此时其值为初始零值
des
2021-07-28 09:44:21 +08:00
@Aruforce 这个确实是反直觉,除非说是分配成员变量空间和初始化赋值是分两步做的,而且可以被打断,是这样的吗?
admol
2021-07-28 10:09:05 +08:00
在父类的构造方法、showMoney 方法、子类的构造方法、showMoney 方法里面分别打上断点,可以观察到执行输出顺序;
songkaizong
2021-07-28 10:22:07 +08:00
这道题出自《深入理解 Java 虚拟机》 8.3 小节。
为了加深理解,笔者又编撰了一份“劣质面试题式”的代码片段,请阅读代码清单 8-10,思考运行后会输出什么结果。
输出两句都是“I am Son”,这是因为 Son 类在创建的时候,首先隐式调用了 Father 的构造函数,而
Father 构造函数中对 showMeTheMoney()的调用是一次虚方法调用,实际执行的版本是
Son::showMeTheMoney()方法,所以输出的是“I am Son”,这点经过前面的分析相信读者是没有疑问的
了。而这时候虽然父类的 money 字段已经被初始化成 2 了,但 Son::showMeTheMoney()方法中访问的却
是子类的 money 字段,这时候结果自然还是 0,因为它要到子类的构造函数执行时才会被初始化。
Cusmate
2021-07-28 14:34:05 +08:00
楼主所有发的帖子都是在问问题,而且挺频繁的。
Newyorkcity
2021-07-28 19:12:44 +08:00
@Hurriance
@qping
@songkaizong

=2 的那个 this 是谁?是 Father 实例,那接下来那个 showMoney 的调用者也应该是 Father 实例,它却用了重写的函数?那为啥字段赋值的时候它不用重写的字段?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/792173

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX