既然多态是面向对象的三大本质特征之一(其它两个是数据抽象和继承),那么C++为什么不将方法调用的默认方式设置为动态绑定,而要通过关键字virtual进行标记呢?Bruce Eckel在《Thinking in C++》中提到,这是由于历史原因造成的,C++是从C发展而来的,而C程序员最为关心的是性能问题,由于动态绑定比静态绑定多几条指令,性能有所下降,如果将动态绑定设定为默认方法调用方式,那么很多C程序员可能不会接受,因此,C++就将动态绑定定位成可选的,并且作出保证:If you don't use it, you don't pay for it(Stroustrup)。
但是,Java作为一个全新的完全面向对象的语言,并不存在向下兼容的问题,同时,Java的设计者也认为多态作为面向面向对象的核心,面向对象语言应该提供内置的支持,因此,Java将动态绑定作为方法调用的默认方式。
下面,我们就详细地来了解一下Java是如何为多态提供支持的。 与C++一样,Java中也有一个存放实例方法地址的数据结构,在C++中,我们把它叫做VTable,而在java中方法表(Method Table),但是两者有很多相同之处:
1、它们的作用是相同的,同样用来辅助实现方法的动态绑定。
2、同样是类级别的数据结构,一个类的所有对象共享一个方法表。
3、都是通过偏移量在该数据结构中查找某一个方法。
4、同样保证所有派生类中继承于基类的方法在方法表中的偏移量跟该方法在基类方法表中的偏移量保持一致。
5、方法表中都只能存放多态方法(Java中的实例方法,C++中是vitual方法)。
但是归根结底,C++是一门编译型的语言,而Java更加偏向于解析型的,因此上述数据结构的生成和维护是有所不同的,表现在:
1、C++中VTable和vptr是在编译阶段由编译器自动生成的,也就是说,在C++程序载入内存以前,在.obj(.o)文件中已经有这些结构的信息;Java中的方法表是由JVM生成的,因此,使用javac命令编译后生成的.class文件中并没有方法表的信息。只有等JVM把.class文件载入到内存中时,才会为该.class文件动态生成一个与之关联的方法表,放置在JVM的方法区中。
2、C++中某个方法在VTable的索引号是在编译阶段已经明确知道的,并不需要在运行过程中动态获知;Java中的方法初始时都只是一个符号,并不是一个明确的地址,只有等到该方法被第一次调用时,才会被解析成一个方法表中的偏移量,也就是说,只有在这个时候,实例方法才明确知道自己在方发表中的偏移量了,在这之前必须经历一个解析的过程。
此外,Java中不支持多重继承,也就不会像C++那样在这个泥潭中纠缠不清了,但Java也引入了新的概念,那就是接口,Interface。使用Interface调用一个实例方法跟使用一个Class来调用的过程是不一样的:
public class Zoo
{
public static void main(String[] args)
{
Pet p1 = new Dog();
Pet p2 = new Dog();
p1.say(); //首先解析一次,得到偏移量,调用方法
p2.say(); //不用解析,直接使用上次的得到的偏移量,调用
Cute c1 = new Dog();
Cute c2 = new Dog();
c1.cute(); //这里使用接口来调用实例方法,首先同样会解析一次,得到偏移量,调用相应方法
c2.cute(); //这里虽然上次已经解析过了,但是还是得重新跟上次一样重新解析一次,得到偏移量,调用
}
}
interface Cute
{
public void cute();
}
class Pet
{
public void say(){ System.out.println("Pet say"); }
}
class Dog extends Pet implements Cute
{
public void cute(){ System.out.println("Dog cute"); }
public void say(){ System.out.println("Dog say"); }
}
为什么会有这样的区别呢?这是因为实现同一个接口的类并不能保证都是从同一个超类继承的,而且这个超类也同样实现相同的接口。因此,该接口声明的方法并不能都保证处于方法表中的同一个位置上。如,可以定义下面的类:
class Cat implements Cute
{
public void cute(){ System.out.println("Cat cute"); }
}
那么,Dog跟Cat同样都实现了接口Cute,因此都能够用Cute接口进行调用,但是方法cute在Dog方法表中的位置并不能保证该方法在Cat方法表中的位置是一样的。因此,对于接口调用方法,我们只好每次都重新解析一道,获得准确的偏移量,再进行调用了。这也导致了使用接口调用方法的效率要比使用类调用实例方法低。当然,这仅仅是相对而言,JVM在实现上会予以优化,我们不能说因为接口效率低就不使用了,相反由于在面向对象作用中接口的强大作用,java是提倡使用接口的,这一点我们是需要注意的。
还有一点,虽然java不支持类的多重继承,但是是可以实现多个接口的,那么,在Java中会不会要像C++的多重继承那样进行必要的转换呢?这个问题,我们只需想一下两者调用的具体过程,就能知道,Java的接口方法每次调用前都是需要解析的,在这里才会取得真正的偏移量,这跟C++中编译期间取得偏移量是不一样,因此,在Java中是不需要进行所谓的转换的。
分享到:
相关推荐
### 深入Java核心:Java中多态的实现机制 多态性是面向对象编程中的一个重要概念,它使得程序代码具有高度的灵活性和可扩展性。本文将围绕Java中的多态实现机制展开讨论,包括多态的基本概念、实现原理以及具体的...
在Java编程语言中,多态性(Polymorphism)是面向对象设计的核心概念之一,它允许我们编写更加灵活、可扩展的代码。多态性体现在两个主要方面:静态多态(编译时多态)和动态多态(运行时多态)。在Java中,动态多态...
在Java编程语言中,多态(Polymorphism)是面向对象设计的重要特性之一,它允许我们使用一个接口来表示多种不同的类型。通过多态,我们可以编写出更加灵活、可扩展的代码,使得程序能以统一的方式处理不同类型的对象...
### Java多态机制详解 #### 一、引言 多态是面向对象编程的重要特性之一,它使得程序设计更加灵活且易于扩展。Java作为一种广泛使用的面向对象编程语言,其多态机制是理解Java编程的关键所在。本文将详细介绍Java...
在Java编程语言中,多态(Polymorphism)是一种核心特性,它允许我们设计更加灵活、可扩展的代码。多态使得一个接口可以有多种不同的实现或者表现形式,也就是说,同一段代码可以处理不同类型的数据。这主要通过继承...
多态(Polymorphism)源自希腊语,意为“多种形式”。在Java中,多态主要体现在两个方面:方法重载(Overloading)和方法重写(Overriding)。方法重载是指在一个类中可以有多个同名方法,但参数列表不同;方法重写...
在给定的“java多态作业”中,可以看到多态性的应用主要体现在实现多个接口(MouseMotionListener、MouseListener和ActionListener),这些接口代表了不同的事件监听器,每个接口都定义了一系列方法来处理特定类型的...
Java语言中的覆盖(Override)和重载(Overload)以及多态(Polymorphism)是面向对象编程的重要概念,它们是实现代码复用和灵活性的关键。 **多态性(Polymorphism)** 多态性是Java中一种允许一个接口有多种实现的方式。...
在Java编程语言中,多态(Polymorphism)是面向对象编程的三大特性之一,另外两个是封装和继承。多态性是指一个接口或者类可以表现出多种形态,使得程序在运行时能够根据实际的对象类型执行相应的方法。这种特性大大...
Java 继承和多态是面向对象编程中的核心概念,它们是Java中实现代码复用和灵活设计的关键机制。在Java中,继承允许一个类(子类)从另一个类(父类)继承特性,而多态则使得不同类型的对象能够共享相同的操作。 ###...
《Code-Demo-java-书籍源代码.rar》是一个包含Java编程示例代码的压缩包,它很可能是为了辅助学习和理解Java编程语言而提供的资源。这个压缩包中的"Code-Demo-java"目录很可能包含了多个子目录和Java源代码文件(....
**多态(Polymorphism)** 多态允许我们使用一个引用类型来调用不同的实现,这取决于实际对象的类型。在继承的上下文中,多态允许我们定义一个通用接口,然后由不同类型的对象来实现这个接口的不同行为。例如,`...
在Java中,面向对象的基本概念包括类(Class)、对象(Object)、封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)。 1. 类与对象:类是现实世界中具有共同属性和行为的对象的抽象,是创建对象的...
在Java编程语言中,多态性(Polymorphism)是面向对象编程的三大特性之一,另外两个是封装和继承。多态性允许我们使用一个接口来代表多种类型的对象,这大大提高了代码的灵活性和可扩展性。在这个"day10-code_java_...
3. **多态的实现机制**:考虑以下示例代码: ```java Parent p = new Child(); ``` 当使用多态的方式调用方法时,会遵循以下规则: - 编译器检查父类中是否存在此方法。如果没有,则编译失败;如果存在,则继续...
多态(polymorphism) “多态”理解 再论向上转型 多态内部机制 正确使用,私有、静态方法及域与多态关系 构造器-多态 协变的返回类型(Covariant return types) 用继承进行设计(纯继承与扩展、向下转型)
在Java中,多态则是通过接口和继承来实现的,由于所有方法都是虚拟的,所以多态性在Java中更为明显。 接着,我们来看多态的两种主要形式:编译时多态和运行时多态。编译时多态通常通过函数重载和运算符重载实现,...
在Java中,多态主要通过继承和重写实现。多态允许我们使用父类类型的引用变量来指向其子类的对象,这样就可以调用子类中重写的方法。例如: ```java Shape shape = new Triangle(); System.out.println(shape....