因为多态需要通过动态绑定来实现,而绑定就是不同的对象调用同一个函数,或者反过来,同一个函数绑定到不同的对象。因此,实现多态的一个大前提是编程语言必须是面向对象的。同时,函数和对象是相互绑定的,也就是说函数也是对象的一部分,具有封装的特性。因为封装,所以有对象。一个函数同时可以绑定多个对象,也就是说不同的对象有相同的行为,这就是继承的意义。所以,面向对象的三个特点缺一不可。其实封装和继承都是为多态准备的。换句话说,封装和继承是多态的,多态最大化了封装和继承的意义。
C++如何实现多态性
现在几乎所有的编程语言都是基于虚拟表,英文vtable。
C++的虚表在哪里?由new创建的对象的头部。
虚拟表中存储了什么?是一个虚函数。
因为hotshot主要是用C++写的,而且讲的是C++的虚表,这个图你应该能看懂。
不然总有朋友问我:为什么Java的类对应的C++对象会有C++级别的虚表?我在任何地方都没有看到这样的代码。
了解了虚拟表之后,理解虚拟表的分布就容易多了。虚拟表分布,其实就是通过虚拟表内存地址获取虚拟表记录,然后通过函数名+包含的参数信息和返回值信息的签名在虚拟表中查找。因为是从前到后,如果子类覆盖了父类的方法,就会调用子类的方法。
所以Java虽然好,但是底层也很重要。对了,虚拟表是用数组实现的,没有有些朋友想的那么复杂。
JVM中的虚拟表
JVM的虚拟表和C++的不太一样。有什么区别?研究虚拟表研究三件事:虚拟表在哪里,虚拟表是用什么结构实现的,虚拟表的分发机制是什么。JVM的虚拟表分布等。接下来,JVM的虚拟表也是用数组实现的,那么区别在哪里呢?
Java类,而JVM中对应的C++对象是klass模型。Java对象,JVM中对应的C++对象是oop模型。C++中的虚表在对象头,而JVM的虚表在klass模型的头,也就是Java类对象的头。必须记住这种区别,这样才能理解Java对象的内存布局。
问一个问题:对于一个我们随便定义的类,有没有JVM虚拟表?其实是有的。那些方法的内存地址是什么?在回答这个问题之前,你得弄清楚:虚拟表中会存储什么样的方法?只有不被static和final修改的public和protect类型的方法才能被称为多态的,并进入虚拟表。因为Java中所有的类都是Object的子类,所以Object中满足这个条件的方法都会在每个类的虚表中。
小伙伴们不服气的时候到了。没事的。去拿证据。具体怎么查就不说了。有点复杂。我对热点没有一定的技能,也没什么概念。
Java是如何实现虚拟表分布的?
用JVM实现虚拟表分发,有两个对应的字节码指令:invokevirtual和invokeinte***ce。在上一篇文章中,我们深入解释了invokeinte***ce。在本文中,我们继续用这个指令来讲这个知识点。让我们看看JVM是如何分布的。其实你看看执行invokeinte***ce时的堆栈,应该就能明白了。
虽然invokeinte***ce后面的操作数是接口方法信息。但是真正的对象会像这样传递。所以在调用时,从操作数栈中获取真实对象,然后通过对象头中的类型指针获取TestDuotai对应的C++类对象,即klass模型。如前所述,虚拟表位于该对象的头部。然后通过函数名的签名+包含的参数信息和返回值信息在虚拟表中查找。因为是从前到后,如果子类覆盖了父类的方法,就会调用子类的方法。这是JVM虚拟表分布的基本原则。这一块有点难懂,需要的基础可能更深。
本文来自止步投稿,不代表舒华文档立场,如若转载,请注明出处:https://www.chinashuhua.cn/24/621934.html