目录
- 1、多态的概念
- 2、多态的条件
- 3、向上转型
- 3.1 概念
- 3.2 使用场景
- 4、向下转型
- 5、多态的优缺点
1、多态的概念
多态,通俗来讲就是多种形态,即对于同样的行为,不同的对象去完成会产生不同的状态。比如动物都会吃东西,小狗和小猫都是动物,但是小狗是吃狗粮,而小猫是吃猫粮。
2、多态的条件
在 Java 中实现多态需要满足下述条件,缺一不可:
- 必须在继承体系下。
- 子类对基类的方法实现重写(即方法原型相同,但实现体不同)。
- 通过基类的引用调用重写的方法。
多态的体现:在代码运行时,当传递不同类的对象时,会调用对应类中的方法。
举例说明:Animal 类是父类,Dog 类和 Cat 类是子类,继承自 Animal 类,同时 Dog 类和 Cat 类对 Animal 类的方法进行了重写。
// 父类
public class Animal {protected String name;protected int age;public Animal(String name, int age) {this.name = name;this.age = age;}public void eat(){System.out.println(name + "在吃饭");}public void bark(){System.out.println(name + "在大叫");}
}// Dog 类:继承自 Animal 类
// 1. 在继承体系中
public class Dog extends Animal{protected String color;public Dog(String name, int age, String color) {super(name, age);this.color = color;}// 2. 对父类方法进行重写@Overridepublic void eat() {System.out.println(name + "在吃狗粮");}@Overridepublic void bark() {System.out.println(name + "汪汪汪~");}public void sleep(){System.out.println(name + "在睡觉");}
}// Cat 类:继承自 Animal 类
// 1. 在继承体系下
public class Cat extends Animal {protected String temper;public Cat(String name, int age, String temper) {super(name, age);this.temper = temper;}// 2. 对父类方法进行重写@Overridepublic void eat() {System.out.println(name + "在吃猫粮");}@Overridepublic void bark() {System.out.println(name + "喵喵喵~");}public void drink(){System.out.println(name + "在喝水");}
}
根据上述类,利用 Animal 类的引用调用被重写的方法从而实现多态。
public class Test {public static void method(Animal animal){// 3. 通过基类的引用调用被重写的方法// 在 method() 方法中利用 Animal 对象的引用调用被重写的方法// 在程序的编译阶段并不知道会调用哪个子类的对象所对应的方法// 只有在运行阶段给 method() 方法传参时才知道。animal.eat();animal.bark();}public static void main(String[] args) {Dog dog = new Dog("毛毛", 5, "白色");method(dog);Cat cat = new Cat("小七", 1, "乖巧");method(cat);}
}
运行结果:
由上述代码可知,虽然在 main 方法中调用的是同一个 method() 方法,但是运行的结果却不同。这是因为 Animal 类是 Dog 类和 Cat 类的基类,基类可以引用不同子类的对象,因此传递给形参 animal的值会出现不同,当传递的是 Dog 类的对象时,会调用 Dog 类中的方法,当传递的是 Cat 类的对象时,会调用 Cat 类中的方法。
当程序员在编写 method() 方法时并不知道也不关心形参 animal 指的是 Cat 类还是 Dog 类的对象,只有当代码运行起来,实参传递给形参值的时候才知道。形参 animal 调用方法会有不同的表现,这种行为就称作多态。
3、向上转型
3.1 概念
向上转型,实际上是创建一个子类对象,当作父类对象来使用。
Animal animal = new Dog(“毛毛”, 5, “白色”);
animal 是父类类型,可以引用子类对象,因为狗和猫都是动物,这是从小范围到大范围的转换,是安全的。但是向上转型之后,无法通过父类的引用调用子类新增的成员方法。
3.2 使用场景
向上转型有以下三种使用场景:
- 直接赋值:子类对象赋值给父类对象。
Animal animal = new Dog("毛毛", 5, "白色");
- 方法传参:形参为父类型引用,可以接收任意子类的对象。
public class Test {// 将 Dog 类和 Cat 类对象的引用传参给 Animal 类的引用public static void method(Animal animal){animal.eat();animal.bark();}public static void main(String[] args) {Dog dog = new Dog("毛毛", 5, "白色");method(dog);Cat cat = new Cat("小七", 1, "乖巧");method(cat);}
}
- 方法返回:方法返回类型是父类类型,可以返回任意子类对象。
public Animal buyCat(){// 创建 cat 对象并返回Cat cat = new Cat("小七", 1, "乖巧");return cat;}
4、向下转型
将子类对象转换为父类时,会无法调用子类中特有的方法,但是有时候需要调用,那么此时将父类引用还原为子类对象即可,即向下转换。
public static void main(String[] args) {Animal animal = new Dog("毛毛", 5, "白色");// 将 Animal 类型对象强转为 Dog 类型,此时可以调用 sleep() 方法Dog dog = (Dog) animal;dog.sleep();}
但是向下转型很容易出现问题。
Java 中为了提高向下转型的安全性,引入了 instanceof。
instanceof 类名N:实际引用的是否为 N 类的对象,如果是返回true,否则返回false
如果该表达式为真,则可以安全转换,否则不能进行安全转换。
public static void main(String[] args) {Dog dog = new Dog("毛毛", 5, "白色");Cat cat = new Cat("小七", 1, "乖巧");Animal animal = cat;animal = dog; // 此时 animal 实际指向的是 Dog 类的对象if(animal instanceof Cat){cat = (Cat)animal;cat.drink();}if(animal instanceof Dog){dog = (Dog)animal;dog.sleep();}}
运行结果:
由上述结果可知,由于 animal 实际指向的是 Dog 类的对象,因此执行第二个 if 语句块,将 animal 强转为 Dog 类型对象,此时就可以调用 Dog 类中新增的成员方法。
5、多态的优缺点
【优点】
- 降低代码的“圈复杂度”,即避免大量使用 if-else。
- 可扩展能力更强。
【缺点】
- 代码的运行效率降低。
- 属性没有多态性:当子类和父类有同名属性时,通过父类引用,只能引用父类自己的成员属性。
- 构造方法没有多态性。