面向对象可以说是各大语言的一个重要特征,但是如果换个角度来看对象在内存中的布局,就会发现根本就没有面向对象,只有面向过程.我们先从一个简单的Shape类开始,这个类有两个字段intx,inty,它们是这样存储在内存中的:很容易理解,对吧?我们再看继承,Circle类继承Shape,增加了一个字段radius,Circle对象在内存中是这样的:这个没什么大不了的,不过这里只是字段(x,y,radius),如果Shape类有一个方法:draw(),应该怎么放在内存中呢?首先,不能在每个对象上都放draw()方法,这样需要复制很多份,太浪费了。我们可以在内存中生成这个draw()方法的一个拷贝,然后给每个对象添加一个指针指向这个draw()方法。(三个Shape对象都指向同一个代码)但是这样也有一个问题。如果Shape类再增加一个方法move(),那么每个对象都需要记录move方法的指针:如果方法多,对象就多,还是浪费!显然,我们需要一个中间层,通过这个中间层来记下所有的函数指针。这个中间层就是所谓的虚函数表:每个类只需要维护一个虚函数表。对于每一个对象,只记录一个虚函数表的地址。当然你也可以在虚函数表中记录这个类的一些相关信息,这不是本文的重点,就不展开了。为什么叫虚函数表呢?这个概念可能来自C++。C++中有一个关键字virtual。当一个函数被修改时,该函数将成为一个虚函数,在调用Behavior时具有多态性。(注:在Java中,一个类的函数默认都是虚函数。)多态是如何实现的?很简单,只要设置好虚函数表即可。假设子类Circle也定义了一个move函数,它重写了父类Shape的move函数,那么在内存中会是这样的:当你调用circle.draw()的时候,Shape类还在虚函数表中绘图()方法。但是在调用circle.move()时,会从Circle类的虚函数表中查找Circle.move(),而不是Shape.move(),多态性已经出现!仔细看上图,在内存中,三个方法和两个对象是分开的。这里没有Class的概念,多态是通过虚函数表来实现的。如果我们写一个程序,写下Shape_draw()、Shape_move()、Circle_move()这样的函数,然后写下Shape、Circle等数据结构,然后用一个虚函数表把它们连接起来。它也是面向对象的。记忆中,“面向对象”已经褪去它美丽的包装,退化为“面向过程”,退化为最基本的公式:程序=数据结构+算法。当然,在大多数情况下,程序员不需要手动去实现这个虚函数表,这个事情应该交给机器去做。对于C++,编译器可以在编译过程中生成虚函数表。对于Java,没有编译后的字节码,只有invokevirtual等指令,虚函数表是在类加载到虚拟机时创建的。
