1.什么是多态?
多态(Polymorphism)是面向对象(Object-Oriented,OO)思想"三大特征"之一,其余两个分别是封装(Encapsulation)和继承(Inheritance)。多态是一种机制、一种能力,而非某个关键字。它在类的继承中得以实现,在类的方法调用中得以体现。
简单理解:一个行为(方法)可以表现出多种形态。
2.简单代码展示:
这是一个程序员上班的出行的故事:深吸一口气,买不起四驱车还不允许我自己new一个吗?🐲
程序员panyq要上班了☹️
-
Programmer类,拥有一个标识programmerCount,并且接受一个交通工具类对象,用来展示上班的方式,直接利用了多态的特性
package com.chapter08.example2;
/**
* @author panyq
* @since: 2022/3/19 16:27
* #Description Programmer
*/
public class Programmer {
//region 变量
/**
* 计数基数
*/
private static int count = 0;
/**
* 程序员id
*/
private final int programmerCount = count++;
//endregion
//region 上班出行
/**
* 上班出行
* 这里我们看到,我们入参的是基类交通工具的引用,而非具体的电动车还是四驱车的引用。
* 即 类型向上转型,不需要担心转型导致的信息丢失
* @param transportation
*/
public void GoToWork(BaseTransportation transportation){
transportation.run(programmerCount);
}
//endregion
}
-
交通工具:小电驴,它继承了交通工具基类,并重写了交通工具基类的run()方法,表现出和基类run()方法不同的形态
package com.chapter08.example2;
/**
* @author panyq
* @since: 2022/3/19 15:58
* #Description ElectricBicycle
*/
public class ElectricBicycle extends BaseTransportation {
//region 构造器
public ElectricBicycle(String name, int wheelCounts) {
// tips:当使用super关键字调用父类构造器初始化时,该行代码必须放在构造器方法的第一行,否则编译无法通过
super(name, wheelCounts);
}
//endregion
//region 重写方法
/**
* 重写run方法,这里我先引用了父类run()方法
*/
@Override
public void run(int programmerCount) {
super.run(programmerCount);
System.out.println("程序员" + programmerCount + "上班的交通工具:" + name + "通过" + wheelCounts + "轮驱动的最大速度可以达到50Km/h");
}
//endregion
public static void main(String[] args) {
Programmer programmer1 = new Programmer();
programmer1.GoToWork(new ElectricBicycle("小电驴", 2));
Programmer programmer2 = new Programmer();
programmer2.GoToWork(new Car("四驱车", 4));
}
/**
* 结果:
* 程序员0上班的交通工具:小电驴通过2轮驱动
* 程序员0上班的交通工具:小电驴通过2轮驱动的最大速度可以达到50Km/h
* 程序员1上班的交通工具:四驱车通过4轮驱动的最大速度可以达到200Km/h
*/
}
-
交通工具:四驱车,它继承了交通工具基类,并重写了交通工具基类的run()方法,表现出和基类run()方法不同的形态
package com.chapter08.example2;
/**
* @author panyq
* @since: 2022/3/19 16:09
* #Description Car
*/
public class Car extends BaseTransportation {
//region 构造器
public Car(String name, int wheelCounts) {
super(name, wheelCounts);
}
//endregion
//region 重写方法
/**
* 重写run方法
*/
@Override
public void run(int programmerCount) {
System.out.println("程序员" + programmerCount + "上班的交通工具:" + name + "通过" + wheelCounts + "轮驱动的最大速度可以达到200Km/h");
}
/**
* 在四驱车类中新增了和基类私有的同名方法explode()
* 自爆
*/
public void explode(){
System.out.println("public()方法中的交通工具" + name + "自爆了");
}
//endregion
}
-
交通工具基类,拥有run()方法,在run()方法中展示了交通工具的名字和驱动轮数
package com.chapter08.example2;
/**
* @author panyq
* @since: 2022/3/19 15:45
* #Description BaseTransportation
*/
public class BaseTransportation {
//region 构造器
public BaseTransportation(String name, int wheelCounts) {
this.name = name;
this.wheelCounts = wheelCounts;
}
//endregion
//region 变量
/**
* 交通工具的名称,子类可共享变量
*/
protected String name;
/**
* 交通工具的车轮数量,子类可共享变量
*/
protected int wheelCounts;
//endregion
//region 公共方法
/**
* 交通工具驱动方式
*/
public void run(int programmerCount){
System.out.println("程序员" + programmerCount + "上班的交通工具:" + name + "通过" + wheelCounts + "轮驱动");
}
//endregion
//region 私有方法
/**
* 自爆
*/
private void explode(){
System.out.println("private()方法中的交通工具" + name + "自爆了");
}
//endregion
public static void main(String[] args) {
BaseTransportation car = new Car("四驱车", 4);
car.explode();
}
/**
* 结果:
* private()方法中的交通工具四驱车自爆了
*
* 结果显示car引用没有调用到子类的explode()方法,而是直接调用了父类private修饰的explode()方法,说明子类和基类的explode()方法完全没有关系,在子类中它就是一个新的方法
*/
}
问题1:编译器怎么识别出来在方法调用的时候需要的是自行车还是四驱车交通工具呢?——运用了方法的绑定
-
什么是绑定——将一个方法和调用该方法的主体关联起来被称作绑定
-
前期绑定:程序执行前进行绑定,即编译器编译的时候,已经知道准确的类型传参
-
后期绑定:又称动态绑定或运行时绑定,是程序运行期间,根据类型判断(2022.3.19日备注:该地方只进行了简单了解,需要继续深入补充,目前只停留在:由于java编译器进行编译时,通过extends继承后的子类重写基类的方法,在class文件中的顺序在父类方法前)
java动态绑定网上参考说明:JAVA动态绑定的内部实现机制 - 知乎
-
tips:Java中除了static方法、final方法(private方法属于final方法),其余方法都是动态绑定的。并且如果父类中有private方法,在子类中编写该同名方法,编译器虽然不会报错,但这个和父类该方法完全没有关系,在子类中这个完全是一个新方法。如上述代码的explode()方法