因为多态需要通过动态绑定来实现,而绑定一般来说就是让不同的对象调用同一个函数,或者说就是让同一个函数成为绑定到不同的对象,所以实现多态的一大前提是编程语言必须是面向对象的。同时,函数和对象是相互绑定的,也就是说函数也是对象的一部分,具有封装的特点。因为有封装,才有对象。同时,一个函数可以绑定多个对象,也就是说它对于不同的对象具有相同的行为,这就是继承的意义。因此,面向对象的三大特性缺一不可。封装和继承实际上是为多态做准备,或者说,封装和继承实现了多态,多态将封装和继承的意义发挥到了极致。C++是如何实现多态和多态的?现在几乎所有的编程语言都是基于虚表实现的,英文vtable。这里我就不说了,因为我不是所有语言都懂,不敢乱说,免得被喷。^_^C++的虚表在哪里?在new创建的对象的头部。虚拟表中存储了什么?它是一个虚函数。C++知识我就不多说了。很多朋友不懂C++,没必要多说。作为一个Java程序员,这个层次的理解就足够了。因为hotshot主要是用C++写的,讲的是C++的虚表,这张图大家应该能看懂。不然总会有朋友问我:为什么Java类对应的C++对象会有一个C++级别的虚表。我看不出哪里有这样的代码。搞清楚虚表之后,理解虚表分布就容易多了。虚表分发,其实就是通过虚表的内存地址获取虚表记录,然后通过包含参数信息和返回值信息的函数名+签名在虚表中查找。因为是从前向后查找,如果子类重写了父类的方法,就会调用子类的方法。刚才简单说了下C++的虚表分布,说多了大家就没概念了。JVM的虚表分布,后面会详细讲。很多现象,如果不了解它的底层,是不是很难理解。有那么多为什么?为什么?^_^所以Java虽然好,但是底层也很重要。对了,虚表是用数组实现的,没有有些朋友想的那么复杂。JVM中的虚表JVM的虚表和C++的虚表不一样。区别在哪里?研究虚表要研究三件事:虚表在哪里,用什么结构来实现虚表,虚表的分布机制是什么。JVM的虚表分布等等,JVM的虚表也是用数组实现的,那么这个区别体现在虚表在哪里?JVM中的Java类和对应的C++对象就是klass模型。Java对象,JVM中对应的C++对象都是oop模型。C++中的虚表在对象头,而JVM虚表在klass模型的头部,也就是Java类对象的头部。必须牢记这种区别,以便您了解Java对象的内存布局。问一个问题:我们随机定义的一个类有没有JVM虚表?事实上,确实如此。这些方法的内存地址是什么?在回答这个问题之前,首先要明白:虚拟表中会存储什么样的方法。只有没有被static或final修饰的public和protect类型的方法,才能被多态调用,进入虚表。因为Java中所有的类都是Object的子类,所以Object中满足这个条件的方法都会在每个类的虚表中。又到了小伙伴们不服气的时候了。好吧,这就是证据。具体怎么查就不说了,有点复杂。如果你对热点没有一定的技能,你就没有概念。Java是如何实现虚表分布的有朋友不理解:我只会Java,干嘛要学底层?那你想进大厂和优秀的人做同事吗?你想成为别人眼中的人吗?你是老板吗?是不是希望在某个领域有一定的知名度……这些都需要实力来支撑。有朋友说:我为什么要手写一个JVM?那我就用我手写的JVM来讲解这个知识点。这就是拥有手写JVM的意义之一。JVM实现了虚表分配,对应的字节码指令有两条:invokevirtual和invokeinterface。在上一篇文章中,我们深入讲解了invokeinterface。在本文中,我们将继续使用这条指令来讲解这个知识点。让我们看看JVM是如何分布的。其实在执行invokeinterface的时候看栈应该就能明白了。invokeinterface后面的操作数虽然是接口方法信息。但真正的对象将作为this传递。所以调用时,从操作数栈中获取真正的对象,然后通过对象头中的类型指针,即klass模型,获取TestDuotai对应的C++类对象。如前所述,虚拟表位于此对象的头部。然后通过包含参数信息和返回值信息的函数名+签名在虚表中找到。因为是从前向后查找,如果子类重写了父类的方法,就会调用子类的方法。这就是JVM虚表分配的底层原理。
