原作 :zosatapo
我在这里公开和我dennisboys的学习交流信件,我们主要在谈java中多态性问题。
这里感谢我的好朋友能给我一个结合自己能力解释问题的机会,在解释中有出错
或者需要讨论的部分希望能通知我一声。
上篇关于多态性的文章请参阅我前面写的<
>.
*******************************************************************************
网友dennisboys的提问部分开始
*******************************************************************************
从头到尾把你的代码看了n次,画了n个图(关于父类和子类的函数地址图),有以下一些迷惑
test t=new test();
base b=new base();
b=t;
/*
问题一:
这里是把父类的引用指向子类,那是不是说调用父类的方法就等于调用子类的方法呢?就是
如果b.display2()不是调用子类的display2()方法吗?而子类有display2()方法,为什么不能
调用呢?(我试过写代码了,果然如你所说是出错的。)
对于以上的问题,你有一段文字好像是对其作解译的。不过我不太清楚。不知是不是以下这
段。
引用原文:
同时可能有人要问,子类中那个函数地址不是也在虚拟函数表中吗?
很高兴你问这样的问题,但是父类引用看到的虚拟函数表是没有
那个函数项目的,因为他对于父类引用绝对是不可见。
我现在理解是如果在子类中如对方法作了更改或新增的方法,对父类来说是不可见的?理解
对吗??
*/
问题二:
((test)b).display();
另一个迷感就是你说的强制转换类型的语法到底是怎样的??我不明白为((test)b)代表什么
?
不过说回来(我怎么觉得这个例子是在说继承??),这个例子使我对继承有了很深的认识
,我倒是觉得对多态性还是一知半解,(别说我笨笨)可能我还没清楚到底哪用到了多态性,
不过请先回复了我以上两个问题,希望你回复了我以上两个问题(结合我研究你的代码)使我
对多态性有更好更深的认识。。
*******************************************************************************
网友dennisboys的提问部分结束
*******************************************************************************
*******************************************************************************
zosatapo的解释部分开始
*******************************************************************************
你真的应该谢谢我的,因为我辛辛苦苦打了篇文章,
因为网络原因没有能成功保存,害了我重写一次,
没有办法,谁叫你是我的好朋友呢。
首先谢谢你耐心看完我的文章。
下面我就我上次写的那篇文章和你的问题作简单的说明但是又比较复杂的说明。
我上次写的那篇文章存在一处写作错误,我在网上已经修正了,这里告诉你一下:
上次的原文中有这样的几句话
****************************************
// 下面调用会出错的
file://t.display2();
file://应该象下面那样
((test)b).display2();
******************************************
这几句话写作上有错误:
应该改成成下面这样:
**********************************************
// 下面调用[不]会出错的 (这里多了一个[不]字)
file://t.display2();
#########################################
实际上我上面这两行跟本文没有关系的
但是为了你很好的理解动态性,你可以
把上面的代码与下面的进行比较。
#########################################
file://下面这行调用不会出错
file://b.display2(); 这里加了一行
file://应该象下面那样
((test)b).display2();
**********************************************‘
下面正式开始我们今天的话题,正对你的问题我进行解释,
这里我尽可能的解释详细一点让你明白,实际上这里太复杂
涉及到OOP的具体实现问题,这个问题又不得不涉及到编译器
问题,主要的又是对象内存布局问题。
由于第二个问题比较简单,我把回答问题的次序颠倒一下。
*********
问题二
*********
你说的很对,我这里解的是继承,但是我这里解的不仅仅是继承的
问题。可以这样是你说的继承只是我解释动态性问题的一个途径和
手段而已,因为继承和多态性一样是面向对象中很重要的概念,不
是写一点文章就可以说明白的。
简单一点和不精确的说,动态性与是继承不可分割的,如果没有继承
根本就谈不上多态性的。所以我说你说的对,但是你没有真正明白我
例子的作用(55555555~~~~~~~~~~~~~我的心血呀)
*********
问题一
*********
首先需要说明的是你对这个问题的理解是不正确的。
这个问题更是复杂的一塌糊涂,我尽量用一些不标准的词汇来说明这个问题。
因为这样便于理解。我这里不解太多的理论,因为我自己现在也正在研究jvm
规范的,因为很多东西java和c++不同,虽然两者实现很相似。上面这点,
我是根据我个人的一些实践,包括理论方面和程序编写实践得到的。但是
我也不敢全部拿c++的那套实现讲给你听,实际上讲了你也不一定听得懂的。
我这里主要讲一些基本的知识,你记住就可以了的,等你学习深入的时候
我再给解释,也许那时候你自然就懂了。
实际上在继承以后,子类会重新设置自己的虚拟函数表,
这个虚拟函数表中的项目有由两部分组成。从父类继承的虚拟函数和子类自己
的虚拟函数。
记住一个很简单又很复杂的规则,一个类型引用只能引用引用类型自身含有的
方法和变量。你可能说这个规则不对的,因为父类引用指向子类对象的时候,
引用是子类的方法的。我告诉你这个规则对于这样的情况依然是成立的。放松
你的大脑,不要想一些乱七八糟的事情,仔细听我分析。
对了,到这里的时候我假设你对于上面的规则除了虚拟函数调用的情况下,
其他的静态函数引用,以及变量引用都明白了。
下面我们开始我们的重量级说明。虚拟函数引用。
下面是jvm规范中关于对象内存布局的说明,我没有翻译
我想你可以看明白,反正我现在看英文没有问题,
如果不明白就查字典。
The Java Virtual Machine does not require any particular internal
structure for objects. In Sun's current implementation of the Java
Virtual Machine, a reference to a class instance is a pointer
to a handle that is itself a pair of pointers: one to a table
containing the methods of the object and a pointer to the
Class object that represents the type of the object, and
the other to the memory allocated from the Java heap for
the object data.
根据这里我就知道实际上jvm关于多态性支持解决方法是和c++中几乎一样的,
只是c++中编译器很多是把类型信息和虚拟函数信息都放在一个虚拟函数表中,
但是利用某种技术来区别。
所以当你使用父类引用指向子类的时候,其实jvm已经使用了编译器产生的类型
信息调整转换了。这里你可以这样理解,相当于把不是父类中含有的函数从虚拟
函数表中设置为不可见的。注意有可能虚拟函数表中有些函数地址由于在子类中
已经被改写了,所以对象虚拟函数表中虚拟函数项目地址已经被设置为子类中完成
的方法体的地址了。
上面这一段就是为什么父类引用指向子类对象时候,有的方法可以调用,有的方法
却不能调用。
虚拟函数调用是经过虚拟函数表间接调用的,所以才得以实现多态的。