题目
什么是多态?运行时多态和编译时多态?重载和重写的区别
答案
@[toc] --- ## 什么是多态? 多态就是**使得同一个行为具有多个不同表现形式或形态的能力**。 **多态发生的三个必要条件**: - 继承 - 方法重写/覆盖(Override) - 父类引用指向子类对象(子类转成父类,向上转型):`Parent p = new Child();` 当把一个子类对象直接赋给父类引用变量时,例如 `Parent p = new Child()`,这个 `p` 引用变量的「编译时类型」是 `Parent` ,而「运行时类型」是 `Child`,**当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态**。 ![](https://cs-wiki.oss-cn-shanghai.aliyuncs.com/img/20210203234209.png) ## 运行时多态和编译时多态 在 Java 中,多态分为两种类型:运行时多态和编译时多态。 1. **运行时多态(Runtime Polymorphism)**:也称为动态绑定或后期绑定。它是指在程序运行时,根据对象的实际类型确定使用哪个方法的过程。这种多态性是通过继承和方法重写(Overriding)实现的。 > 举个例子,假设有一个父类 Animal 和它的两个子类 Cat 和 Dog。这三个类都有一个叫做 makeSound() 的方法,但是 Cat 和 Dog 类重写了这个方法,分别实现了不同的声音。当程序执行时,如果使用父类 Animal 的引用指向 Cat 或 Dog 类的对象,那么调用 makeSound() 方法时,会根据对象的实际类型(Cat 或 Dog)来确定调用哪个方法。 2. **编译时多态(Compile-time Polymorphism)**:也称为静态绑定或早期绑定。它是指在程序编译时,根据方法的名称、参数类型和返回值类型确定使用哪个方法的过程。这种多态性是通过方法重载(Overloading)实现的。 > 举个例子,假设有一个类 Calculator,它有两个方法 add(int a, int b) 和 add(double a, double b)。在程序编译时,当调用 add() 方法时,编译器会根据参数的类型(int 或 double)确定调用哪个方法。 ## 重载(Overloading)和重写(Overriding)的区别 方法的重写 Overriding 和重载 Overloading **都是 Java 多态性的表现** - **方法重写是父类与子类之间多态性的表现**。其子类和父类方法的名字相同,参数个数相同,返回类型也相同,并且子类的访问权限不能比父类的严格,比如父类是 public,那么子类也只能是 public,不能比 public 更严格。也就是说,**方法重写,只有方法体是不一样的,访问权限可以有限制的修改**。 ```java class Shape { public void draw() {} } class Circle extends Shape { public void draw() { System.out.println("Circle.draw()"); } } ``` > 其实,上面说的返回类型完全相同并不严格正确。下面我们来解释一下。 > > 首先,我们需要知道**方法的名字和参数列表称为方法的签名**。例如,`draw()` 和 `draw(String) ` 是两个具有相同名字, 不同签名的方法。如果在子类中定义了一个与超类签名相同的方法, 那么子类中的这个方法就覆盖/重写了超类中的这个相同签名的方法。 > > 不过,**返回类型不是签名的一部分**, 因此,在覆盖/重写方法时, 一定要保证返回类的兼容性。 **允许子类将覆盖方法的返回类型定义为原返回类型的子类型**。 > > 例如, 假设 `Shape` 类有 > > ```java > class Shape { > public Shape draw() { > ...... > } > } > ``` > > 在后面的子类 `Circle` 中, 可以按照如下所示的方式覆盖这个方法 > > ```java > class Circle extends Shape { > public Circle draw() { > ...... > } > } > ``` > > 用专业术语来说,这两个 `draw` 方法具有**可协变的返回类型**。 - **方法重载并非多态的必要条件**,不过可以理解成**某个类的多态性的表现**。所谓方法重载,就是**一个类中定义了多个方法名相同,但是参数的数量或者类型不同的方法。方法的返回类型和访问权限可以任意修改,不以它俩作为方法重载的标志**。 ```java class Circle extends Shape { public void draw() { System.out.println("Circle.draw()"); } public void draw(int i) { System.out.println("Circle.draw()" + i); } } ``` 总结一下方法重载和重写: | | 方法重载 | 方法重写 | | :--------: | :------: | :------------------------------------------------: | | 方法名 | 相同 | 相同 | | 参数列表 | 必须不同 | 必须相同 | | 返回类型 | 可以不同 | 子类方法的返回类型可以是原父类方法返回类型的子类型 | | 访问修饰符 | 可以不同 | 子类不能做更严格的限制(可以降低限制) | 再来俩张图配合理解 ![](https://cs-wiki.oss-cn-shanghai.aliyuncs.com/img/20210204142236.png) ![](https://cs-wiki.oss-cn-shanghai.aliyuncs.com/img/20210203232313.png)