一个关于继承和多态的问题(思索篇)

博客围绕上一篇Blog留下的问题展开,先从Java角度,从语义和底层实现两方面解释答案。语义上,类C继承类B,方法g行为由类B确定;底层实现上,实例化时按继承层次调用构造函数。还给出对应C#代码,指出C#对继承体系定义更严格具体。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        在上一篇Blog中给大家留了一个问题,如果大家有上机去尝试一下,就会知道答案是1了。虽然代码用的是Java,但是这个问题是具有同性的,即使换了C#去写,也会让我们思索一番。下面先从Java的角度去阐述一下这个问题。
        对于这个答案的解释,我们可以从两个方面去看:一是从语义的角度;首先,g这个方法对于类C而言是继承自类B,那么就应该与类B中的方法g的行为完全一致,而方法g调用了方法f,其访问修饰符是private,也就是说不可以被覆盖(override)了,从这里就可以知道方法g的行为(调用类B中的方法f)就已经确定了。所以类C继承了类B,使用方法g就不能乱来了,就得乖乖的按照类B定义好的方法g办事了;
        二是从底层实现的角度;如果大家觉得上面的说法不够说服力的话,还是让我们来点更加细节的吧。我们都知道创建一个实例的时候,会根据继承层次相应地调用不同的构造函数。当类C实例化的时候,先调用类A的构造函数,接着是类B的,最后是类C的,由于我们这里并没有为这三个类定义特殊的构造函数,默认不带参数的构造函数就会被调用了。那么在类C的一个实例被创建的时候,到底发生了什么呢?首先是类A的构造函数被调用,也就是在heap中为这个实例分配了内存以存放实例变量(instance variable)和方法的指针,由于类A中没有实例变量,就只为存放方法g的指针分配了空间,同时这个指针的内容是可以被修改的;接着类B的构造函数被调用,由于方法g被override了,那么原来存放着方法g指针的地址就会被修改,指向类B的方法g,而方法g中调用的方法f由于其访问修饰符是private,所以这个被调用的方法f的指针值就已经固定了;接着类C的构造函数被调用,只是增加了方法f的指针,注意是增加。综上所述,由于在Java中,所有类方法都是虚函数,都是可以被继承的,因此,如果访问修饰符不为private,都会在类构造函数被调用的时候分配空间以初始化这些方法的指针。
        以下是上一篇blog中Java代码所对应的C#代码:

None.gifusing  System;
None.gif
namespace  DefaultNamespace
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public class Test dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
public static void Main(string[] args) dot.gif{
InBlock.gif            A ref1 
= new C();
InBlock.gif            B ref2 
= (B)ref1;
InBlock.gif            Console.WriteLine(ref1.g());
InBlock.gif            Console.WriteLine(ref2.g());
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public class A dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif       
private int f() dot.gif{
InBlock.gif            
return 0;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockStart.gifContractedSubBlock.gif       
public virtual int g() dot.gif{
InBlock.gif             
return 3;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public class B: A dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif       
private int f() dot.gif{
InBlock.gif            
return 1
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockStart.gifContractedSubBlock.gif       
public override int g() dot.gif{
InBlock.gif            
return f();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif    
ExpandedSubBlockStart.gifContractedSubBlock.gif    
public class C: B dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif       
public int f() dot.gif{
InBlock.gif            
return 2;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}


        从以上代码,我们可以发现,C#对于继承体系有着更为严格和具体的定义。譬如需要通过virtual关键字来说明该方法是虚函数,是可以被override的以及override的关键字的使用等。
        呼,终于都说完了这一串让自己战战兢兢的文字。还是一句老话,自己明白容易,要讲清楚就难了。关键还是认识不够深刻啊,请各位多多指点了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值