文章目录
- 一、概念
- 二、使用前提
- 三、实例
- 四、优缺点
- 4.1 优点
- 4.2 缺点
- 五、动态绑定和静态绑定
- 5.1 动态绑定
- 5.2 静态绑定
一、概念
多态是指类的多种形态,同一个接口,使用不同的实例而执行不同操作。
二、使用前提
- 有继承/实现关系
- 有父类引用指向子类对象
- 有方法的重写
三、实例
这是一个实现多态的例子,具体看注释信息:
public class Test {
public static void main(String[] args) {
// 创建对象 (多态方法)
Animal animal = new Dog();
// 访问成员变量:编译看左,运行也看左
// 编译看左:当程序编译时,系统会去父类查看是否存在当前变量,如果存在,编译成功,不存在,则编译报错
// 运行看左:当程序运行时,系统实际会去调用父类的成员变量
System.out.println("====== 成员变量 ======");
System.out.println(animal.name);
// 访问成员方法:编译看左,运行看右
// 编译看左:当程序编译时,系统会去父类查看是否存在当前方法,如果存在,编译成功,不存在,则编译报错
// 运行看右:当程序运行时,系统实际会去调用子类的成员方法
System.out.println("====== 成员方法 ======");
animal.show();
}
// 动物类
class Animal {
String name = "动物";
public void show() {
System.out.println("Animal === show()");
}
}
// 狗类
class Dog extends Animal {
String name = "狗";
@Override
public void show() {
System.out.println("Dog === show()");
}
}
// 猫类
class Cat extends Animal {
String name = "猫";
@Override
public void show() {
System.out.println("Dog === show()");
}
}
运行结果:
====== 成员变量 ======
动物
====== 成员方法 ======
Dog === show()
四、优缺点
4.1 优点
- 在多态的形式下,右边的子类对象可以实现解耦合,便于扩展和维护;
- 当定义方法时,你可以使用父类来作为方法的参数,这样就可以接受所有的子类对象,体现了多态的灵活性。
4.2 缺点
- 不能使用子类的特有方法,因为当使用多态的形式去调用方法,它会去看父类有没有这个方法,如果没有则报错。
注:可以通过强制类型转换解决,具体看下列代码
细节:强制转换时,转换的类型和创建时对象的类型必须保持一致,不然会报错,可以使用
instanceof
关键字,判断对象的类型。
强制类型装换
public class Test {
public static void main(String[] args) {
// 创建对象 (多态方法)
Animal animal = new Dog();
// 调用狗类特有方法 (强制类型转换)
Dog dog = (Dog) animal;
dog.eat();
}
}
// 动物类
class Animal {
String name = "动物";
public void show() {
System.out.println("Animal === show()");
}
}
// 狗类
class Dog extends Animal {
String name = "狗";
@Override
public void show() {
System.out.println("Dog === show()");
}
// 特有方法
public void eat() {
System.out.println(name + "吃食物");
}
}
instance 关键字
public class Test {
public static void main(String[] args) {
// 创建对象 (多态方法)
Animal animal = new Dog();
// 调用狗类特有方法 (instance 关键字)
if (animal instanceof Dog dog) {
dog.eat();
}
}
}
// 动物类
class Animal {
String name = "动物";
public void show() {
System.out.println("Animal === show()");
}
}
// 狗类
class Dog extends Animal {
String name = "狗";
@Override
public void show() {
System.out.println("Dog === show()");
}
// 特有方法
public void eat() {
System.out.println(name + "吃食物");
}
}
// 猫类
class Cat extends Animal {
String name = "猫";
@Override
public void show() {
System.out.println("Dog === show()");
}
}
运行结果:
狗吃食物
五、动态绑定和静态绑定
5.1 动态绑定
动态绑定是多态的基础,也叫运行时绑定,动态绑定的关键在于,当我们通过一个引用调用一个方法时,JVM会检查对象的实际类型,然后决定调用哪个方法。如果在父类和子类中都定义了相同的方法(即方法重写),那么将调用的是子类中的方法。
5.2 静态绑定
静态绑定,也称为早期绑定或编译时绑定,是在编译阶段就确定方法调用的过程。与动态绑定不同,静态绑定在编译时就能确定将要调用哪个方法,不需要等到运行时。
在Java中,以下几种情况会使用静态绑定:
- 成员变量访问:当访问类的成员变量时,无论引用是什么类型,都会直接访问对象的实际类型中的变量,但是这个访问在编译期就已经确定了,所以属于静态绑定。
- static静态方法调用:静态方法属于类而不属于对象,所以在调用静态方法时,不会发生动态绑定,而是直接调用该类的静态方法,这属于静态绑定。
- final方法调用:如果一个方法被声明为final,那么这个方法不能被子类重写,所以在调用这个方法时,会直接调用父类的final方法,这属于静态绑定。
- 构造器调用:构造器的调用也是在编译时期就确定的,属于静态绑定。