Java单继承实现原理
一 . 多态的分类
首先在聊多态的原理之前我们先对多态分个类
- 编译时多态
- 运行时多态
1. 什么是编译时多态
编译时多态是静态的 , 主要是指重载方法 , 简单来说就是同一个方法名称不同的参数列表
public class PolymorphismExample {
// 方法1:无参数
public void display() {
System.out.println("Display without parameters");
}
// 方法2:一个整数参数
public void display(int number) {
System.out.println("Display with one integer parameter: " + number);
}
// 方法3:两个整数参数
public void display(int number1, int number2) {
System.out.println("Display with two integer parameters: " + number1 + " and " + number2);
}
// 方法4:一个字符串参数
public void display(String str) {
System.out.println("Display with one string parameter: " + str);
}
public static void main(String[] args) {
PolymorphismExample obj = new PolymorphismExample();
// 调用无参数的方法
obj.display();
// 调用一个整数参数的方法
obj.display(10);
// 调用两个整数参数的方法
obj.display(10, 20);
// 调用一个字符串参数的方法
obj.display("Hello, World!");
}
}
2. 什么是运行时多态
运行时多态是面向对象编程中的一个重要概念,它允许在运行时根据对象的实际类型来调用相应的方法。这种多态性是通过动态绑定(也称为晚期绑定)来实现的,意味着方法调用的解析是在程序运行时进行的,而不是在编译时。
在Java中,运行时多态主要依赖于以下几个方面:
- 继承:子类可以继承父类的方法,并且可以重写这些方法。
- 方法重写(Override):子类可以提供一个具有相同名称和参数列表的方法实现,这将覆盖父类中的方法。
- 动态方法调度:在运行时,Java虚拟机(JVM)会根据对象的实际类型来决定调用哪个方法。
class Animal {
public void makeSound() {
System.out.println("Some generic sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Bark");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow");
}
}
public class RuntimePolymorphismExample {
public static void main(String[] args) {
Animal myAnimal = new Dog();
myAnimal.makeSound(); // 输出 "Bark"
Animal myOtherAnimal = new Cat();
myOtherAnimal.makeSound(); // 输出 "Meow"
// 使用多态性,我们可以将不同类型的对象赋值给同一引用类型
makeSound(new Dog()); // 输出 "Bark"
makeSound(new Cat()); // 输出 "Meow"
}
public static void makeSound(Animal animal) {
animal.makeSound(); // 这里会根据传入对象的实际类型调用相应的makeSound方法
}
}
二. 底层原理
RTTI
多态实现的技术基础是 RTTI,即 Run-Time Type Identification(运行时类型判定) , RTTI在多态中的作用是,它允许程序在运行时确定对象的实际类型,并根据这个类型信息来调用相应的方法。这使得多态成为可能,因为程序可以在不知道对象确切类型的情况下,通过RTTI来动态地调用正确的方法实现。
Java 源码编译器将 Java 文件编译成 .class 文件,然后通过类装载器将 .class 文件装载到 JVM 的方法区中 并建立对应类的类型信息类型信息包括了方法代码,类变量、成员变量、以及方法表。
而在方法区的类型信息中会创建一个指针 , 该指针会指向一张记录该类方法入口的表也就是方法表, 那么什么是方法表?
方法表
由于Java的单继承机制,一个类只能继承一个父类,而所有的类又都继承自Object类。方法表中最先存放的是Object类的方法,接下来是该类的父类的方法,最后是该类本身的方法。
这里关键的地方在于,如果子类改写了父类的方法,那么子类和父类的那些同名方法共享一个方法表项,都被认作是父类的方法。
而对于方法表 , 其中的所有项都是通过指针指向其实现方法.
一旦JVM知道了实际对象的类型,它会查找该类型对应的方法表(vtable)。方法表是一个包含指向方法实现的指针的结构。
如何实现?
在Java中,当通过父类的引用调用一个方法时,JVM(Java虚拟机)会使用动态分派来确定实际调用的方法。这个过程是自动的,不需要程序员显式地查看引用指向的实际对象类型。以下是这个过程的详细说明:
- 类型信息存储:
- 在Java中,每个对象都包含一个指向其类(Class)对象的引用。这个引用存储在对象的头部,通常被称为“对象头”。
- 类对象包含了类的元数据,包括类的名称、字段、方法等信息,以及一个指向该类方法表的引用。
- 动态分派:
- 当一个方法调用发生在继承体系中时,JVM会使用动态分派来确定应该执行哪个方法。
- JVM会查看对象头中的类引用,找到实际对象的类对象 , 获取其对应的类型。
- 然后,JVM会根据类对象中的方法表来查找应该调用的方法。
- 方法表查找:
- JVM会在实际对象的方法表中查找与调用的方法名和参数匹配的方法。如果子类重写了父类的方法,那么子类的方法表中会包含指向子类方法实现的指针, 子类的方法表中会包含一个指向子类方法实现的引用,而不是父类的方法实现。
- 方法调用:
- 一旦找到正确的方法引用,JVM就会调用这个方法。
- 这个过程是透明的,对程序员来说是不可见的,JVM自动处理了所有的分派逻辑。