1. 多态的概念
通俗来讲,就是多种形态。就是完成某一个行为时,如果不同对象去完成就会产生不同的状态。
总的来讲,同一件事情,发生在不同对象的身上,就会产生不同的结果。
2. 多态的实现条件
- 1、必须在继承类下 (继承)
- 2、子类必须要对父类中的方法进行重写 (重写)
- 3、 通过父类的引用调用重写方法 (向上转型)
下面们,我们来详细解析下讲下这三个条件
2.1 必须在继承类下
这里我们创建 一个父类 Animal 和 两个子类 Dog 和 Cat
class Animal{
public String name;
public int age;
public Animal(String name,int age){ // 带有两个参数的构造方法
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭");
}
}
class Dog extends Animal{
public Dog(String name ,int age){
super(name,age);
}
@Override
public void eat() {
System.out.println("吃狗粮");
}
}
class Cat extends Animal{
public Cat(String name ,int age){
super(name,age);
}
@Override
public void eat() {
System.out.println("吃猫粮");
}
}
2.2 子类必须对父类中的方法进行重写
Dog类重写 eat() 方法
Cat类重写 eat() 方法
2.3 通过父类的引用调用重写方法
在测试类中,我们通过父类的引用了重写的方法
public class Test {
public static void func(Animal animal){
animal.eat();
}
public static void main(String[] args) {
Animal animal1 = new Dog("大黄",1);
func(animal1);
Animal animal2 = new Cat("喵喵",2);
animal2.eat(); // 通过父类的引用 调用重写方法
}
}
最终的输出结果就是:
2.4 总结一下
我们来画个图分析下, 下面这个例子就形成了多态的条件
那么,程序是如何运行的呢?我们继续来看一个图,来分析下
在将 class文件,进行反汇编以后,也可以看出 程序在编译的时候,也是调用的 父类的 eat() 方法
而在运行的时候,却是调用了重写以后的方法,这就是动态绑定,也是发生多态的基础!
- 动态绑定:也称后期绑定(晚绑定),在编译时,不能确定方法的行为,需要等到程序运行时,才能够确认具体调用哪个类的方法
- 静态绑定:也称前期绑定(早绑定),在编译时,根据用户所传递的实参类型就确定了具体调用哪个方法,例如:函数重载
3. 什么是重写
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写。 有以下几条规则
既然讲到重写,那么我们就来对比下重写和重载的区别
方法的重载是一个类的多态的表现
方法重写是子类和父类的一种多态表现
4. 向上转型和向下转型
4.1 向上转型
创建一个子类对象,将其当成父类对象来使用
语法格式: 父类类型 对象名 = new 子类类型()
Animal animal = new Cat();
使用场景有三种:
- 直接赋值
Animal animal = new Cat("旺财",5);
- 方法传参
public class Test {
public static void func(Animal animal){
animal.eat();
}
public static void main(String[] args) {
Animal animal1 = new Dog("大黄",1);
Animal animal2 = new Cat("喵喵",2);
func(animal1);
func(animal2);
}
}
- 方法的返回
public static Animal funciton(String name){
return new Dog("大黄",2);
}
向上转型的优点:让代码更加灵活,可以引用更多的类型
向上转型的缺点:不能调用子类特有的方法(除非重写,发生动态绑定)
4.2 向下转型
语法格式:
父类类型 父类对象名 = new 子类类型()
子类类型 子类对象名 = (子类类型)父类对象名
Animal animal1 = new Dog("黑咯破",1);
Dog dog = (Dog) animal1;
向下转型用的比较少,不安全,可以从下图中看出不安全。
Java 为了提高向下转型的安全性,引入 instance关键字,如果该表达式 为 true,则可以安全转换。
5.1 使用多态的好处和缺陷
好处:
- 能够降低代码的 圈复杂度,避免使用大量的 if-else
- 可扩展能力更强
多态也有缺陷:
- 属性没有多态
- 构造方法没有多态
我们要避免在构造方法中调用重写的方法
class B {
public B() {
// do nothing
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
private int num = 1;
@Override
public void func() {
System.out.println("D.func() " + num);
}
}
public class Test {
public static void main(String[] args) {
D d = new D();
}
}
上述代码执行的结果是 D.fun() 0 , 通过下图来看执行步骤
当在父类的构造方法中 去调用父类和子类的重写方法时,此时会调用子类的!