V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
Newyorkcity
V2EX  ›  问与答

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

  •  
  •   Newyorkcity · 2021-07-28 08:51:23 +08:00 · 1868 次点击
    这是一个创建于 1242 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 ?

    谢谢

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

    =2 的那个 this 是谁?是 Father 实例,那接下来那个 showMoney 的调用者也应该是 Father 实例,它却用了重写的函数?那为啥字段赋值的时候它不用重写的字段?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2616 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 10:16 · PVG 18:16 · LAX 02:16 · JFK 05:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.