为什么 Java 父类构造函数调用被重写的方法会调用到子类的

2022-11-17 13:45:05 +08:00
 movq
   public static void main(String[] args) {
        class Parent {

            Parent() {
                print();
            }

            void print() {
                System.out.println(10);
            }
        }

        class Child extends Parent {


            void print() {
                System.out.println(20);
            }
        }
        Parent parent = new Child();
    }

上面这段代码输出是 20.

感觉这样很奇怪,为什么父类的构造函数调用 print 给调用到子类了呢?这样难道不是一种反直觉的结果吗?

9684 次点击
所在节点    程序员
125 条回复
WhiteDragon96
2022-11-18 09:44:42 +08:00
编译看左,运行看右
duanguyuan
2022-11-18 09:49:32 +08:00
这里有几层逻辑不敢苟同。

( 1 )你在题目中说 java 这种继承行为反直觉,10 楼举例“哑巴不能说话”,java 的行为反而是符合直觉的。这个例子很直观,所以这么多人给 10 楼送感谢。

( 2 )不管哪个论坛社区,有规定看不懂题目的人不能回答问题吗?而且“看不懂题目”这还是个主观论断,或许题目不清楚,或许别人的回答是对的,只是你没 get 到

( 3 )关于浪费版面。在大佬看来,开这个贴就是浪费版面。但有人这么说你吗?还真有,说这个问题很 low 。那你心里爽吗?你不爽。所以,别双标。我是菜鸡,我认真回个帖还是浪费版面了?
llzzll1234
2022-11-18 09:50:08 +08:00
别吵了,这个 OP 不知道为什么就一股迷之傲慢,根本不是想和你们讨论问题的样子。
newmlp
2022-11-18 09:52:47 +08:00
@msg7086 但是我在子类里有时候想调父类的方法,有时候又要调用子类的方法
movq
2022-11-18 09:59:09 +08:00
@duanguyuan


@duanguyuan

( 1 )首先,10 楼说的内容和我的帖子没有任何关系。我是在问为什么构造函数里面调用被重写的函数,会调用到子类,然后我附了一个 C++的代码,说 C++里面,父类构造函数调用被重写的函数,调用到的是父类的方法。

10 楼回复的,是在说用父类静态类型,子类动态类型,调用静态类型被重写的方法,可以调用到子类里被重写的方法。和我说的完全没有关系。

不懂这个区别的人建议闭嘴,没必要一直争论。错就是错了,不会因为你们看不懂题目的人一直说就变成对的。

( 2 )我没说不能回答问题,我说 10 楼没看懂题。其它没也没看懂题的人不要一直来骚扰楼主,说 10 楼是对的。讨论也是要朝着正确的方向来进行的,而不是不能指出某些人带偏了方向。

( 3 )“在大佬看来,开这个贴就是浪费版面”。不要主观臆断,你说的没有根据。为什么楼里面 mind3x 和 geelaw 这两个大佬给出了专业性强,内容丰富的回答,而且没说这个帖子 low ,反而而有些根本没看懂题的人才会说本帖子 low 呢?是不是这些人太自恋了?还是说我指出他们看不懂题,说到他们的痛处了?真是黄钟毁弃,瓦釜雷鸣

( 4 )“别吵了,这个 OP 不知道为什么就一股迷之傲慢,根本不是想和你们讨论问题的样子。”

你说的根本就不属实。我在和能看懂题目的人讨论问题,和他们讨论是平和的讨论的。你所谓的傲慢,是因为有些人没看懂题还来攻击我。对于这些人,我对他们回应的态度是正常的。自己错了就是错了,还逼逼赖赖的攻击对的人。
movq
2022-11-18 10:00:52 +08:00
统一回复:85 楼已经说的很清楚了,看不懂题的人建议不要继续在这回复我,建议去检查一下自己的认知能力。没看懂题就在这 diss 我的人我不会再回复。
movq
2022-11-18 10:04:09 +08:00
@newmlp 子类调用父类已经被子类重写的方法应该可以反射吧
Maiiiiii
2022-11-18 10:10:38 +08:00
啊对对对
movq
2022-11-18 10:17:03 +08:00
有个猜想, 不知道是不是有些人智商不够,看不懂长文,也看不懂专业回答,所以不看本楼里面 mind3x 和 geelaw 这两个大佬发的真正专业的回答,而只能看 10 楼这种写给小学生看的东西,还奉为圭臬。

从给他们的点赞也能看出来,真正专业的回答只有一两个赞,10 楼这种毫无意义的回答却点赞很多。
ccppgo
2022-11-18 10:34:12 +08:00
楼主估计是看到别人回答的东西简单明了, 然后看客都赞同, 他觉得看客们可能会因此觉得他水平很低 所以恼羞成怒了, 有点搞笑的
JerryV2
2022-11-18 10:35:12 +08:00
@movq

C++ 的构造顺序其实是很基础的知识了,会 C++ 的人一眼就能看懂问题的所在,你说他们不懂,他们自然认为你傲慢

#72 #73 是大佬,懂 C++ 的人真是越来越少了
hez2010
2022-11-18 10:36:29 +08:00
因为 Java 中的方法默认都是 virtual 的,于是 Child 的 print 把 Parent 的 print 重写了;而 C++ 的方法则默认不是 virtual 的。
duanguyuan
2022-11-18 10:41:28 +08:00
抱歉,确实智商不够,看了 72 层 mind3x 的回答才明白 op 要问的是什么,10 楼回答确实文不对题。

打个比方,一个知识点,大家被考了 N 遍了,一遇到类似的题就知道怎么答,惯性思维。但这次的题看起来一样,问的却是另一个更深入的东西,不仔细审题自然答错。

ps: 楼主说话是真难听。
movq
2022-11-18 10:44:08 +08:00
@duanguyuan 专门难听给那些没看懂题就说难听话 diss 我的人看的
movq
2022-11-18 10:45:06 +08:00
@ccppgo 你的分析一派胡言
reallittoma
2022-11-18 10:48:22 +08:00
这帖子看下来,我认为 Java 程序员更加傲慢,绝不接受别人指出的半点 [看似] 是在说 Java [有毛病] 的言论。
movq
2022-11-18 10:49:59 +08:00
@reallittoma 而且我也没说 Java 是劣质语言,我只是说在这一点上我觉得不符合我的直觉,结果就有些人逼逼赖赖的说什么我在秀 C++优越
hez2010
2022-11-18 10:59:10 +08:00
@movq 其实 OOP 语言里也几乎只有 Java 没有写非 virtual 方法的能力。
像 C++、C# 的方法默认都是非 virtual 的,因此调用时不需要去查找虚表,反而有性能优势,只有需要 virtual 的时候才手动标记上 virtual 允许方法被重写。
Opportunity
2022-11-18 11:16:16 +08:00
你说的这个反直觉的结果其实是因为你类比的有问题。

可以先从 C#/Java 的“析构”来看。C# 里用的是 `~Foo(){}`,但是可以用 `GC.SuppressFinalize(Object)` 方法来阻止这个函数被调用。Java 里这个函数就直接叫“finalize()”了。可见,这些托管语言里是不存在真正的析构函数( Destructor )的,或者说,不允许用户自定义析构的逻辑。替代品是所谓的终结器( finalizer )。

同样的,这些语言的“构造函数”其实也只是初始化器( inintializer ),虚表这些在 VM 内真正的构造阶段已经完成了,调用这些 inintializer 的时候在 VM 看来你使用的已经是一个完整的对象了。

要用 cpp 类比的话你也应该保持构造和析构函数是 trival 的,使用 inintializer 和 finalizer 来模拟构造和析构:

```cpp
class Parent {
public:
void virtual inintialize() { print(); }

void virtual print() { std::cout << "parent" << std::endl; }
};

class Child : public Parent {
public:
void virtual inintialize() {
Parent::inintialize();
print();
}

void print() { std::cout << "child" << std::endl; }
};

int main() {
Parent *p = new Child();
p->inintialize();
p->print();
return 0;
}
```

输出

child
child
child

和 Java/C# 表现一致
TArysiyehua
2022-11-18 12:12:57 +08:00
首先构造方法一定会调用父类的,所以 new Child () 的时候就会调用父类的没毛病,因为构造方法没有虚的,所以必须调用,不然没法继承父类的特征。

其次方法就不是,你不手动调父类的方法就相当于是覆盖了父类的方法,那自然输出的是子类的

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

https://tanronggui.xyz/t/895919

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

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

© 2021 V2EX