一、多态的概述
什么是多态?
答:多态指的是同一个方法调用,由于对象不同可能会有不同的行为。
现实生活中,同一个方法,具体实现会完全不同。
多态的必要条件:
- 继承是多态的前提
- 子类重写父类方法
- 父类引用指向子类对象
多态的使用场合:
1)使用父类做方法的形参,实参可以是任意子类类型
2)使用父类做方法的返回值类型,返回值可以是任意子类的对象
二、 多态的实现
多态的实现主要表现在父类和继承该父类的一个或多个子类对某些方法的重写。
多个子类对同一方法的重写可以表现出不同的行为。
【示例】基于继承实现的多态——管理员给动物喂食
class Animal { public void eat() { System.out.println("动物 eat"); } } class Dog extends Animal { @Override public void eat() { System.out.println("dog 吃骨头"); } } class Pig extends Animal { @Override public void eat() { System.out.println("Pig 吃饲料"); } } class Cat extends Animal { @Override public void eat() { System.out.println("cat 吃鱼儿"); } } // 管理员 class Admin { /** * 多态满足条件 * 1.Cat、Dag和Pig继承Animal * 2.Cat、Dag和Pig重写了Animal的eat()方法 * 3.父类引用指向子类对象(实参赋值给形参) * 此处如果没有使用多态,管理员需要分别给每一个动物添加一个喂食的方法!!! */ public void feedAnimal(Animal a) { a.eat(); // 动态绑定 } } public class PolymorphismDemo { public static void main(String[] args) { // 初始化一个管理员对象 Admin admin = new Admin(); // 管理员给动物喂食 admin.feedAnimal(new Dog()); admin.feedAnimal(new Pig()); admin.feedAnimal(new Cat()); } } |
三、引用数据类型转换
【示例】基本数据类型转换
// 整数(int)转换成小数(double) int x = 10; double y = x; // 小数(double)转换成整数(int) double n = 2.2; int m = (int)n; |
引用数据类型转换:
向上转型:父类引用指向子类对象,属于自动类型转换。
格式:父类类型 变量名 = 子类对象;
向下转型:子类引用指向父类对象,属于强制类型转换。
格式:子类类型 变量名 = (子类类型) 父类对象;
【示例】 引用数据类型的转换
// 父类 class Person { public void eat() { System.out.println("person eat ..."); } } // 子类 public class Student extends Person { public void study() { System.out.println("Student study ..."); } } // 测试类 public class PolymorphismDemo { public static void main(String[] args) { // 向上转型:父类引用指向子类对象 Person p = new Student(); p.eat(); // 调用Person的eat()方法 // p.study(); // 编译失败,不能调用Student的study()方法
// 向下转型:子类引用指向父类对象 Student stu = (Student)p; stu.eat(); // 调用Person的eat()方法 stu.study(); // 调用Student的study()方法 } } |
向上转型:
优点:隐藏了子类类型,提高了代码的扩展性,多态本身就是向上转型的过程。
缺点:只能使用父类共性的内容,不能调用子类特有方法!
向下转型:
优点:向下转型之后,可以调用子类特有方法
缺点:但是向下转型有风险,容易发生ClassCastException异常!
Q:向上转型多用于多态,那还有什么补充?and向下转型一般用来干啥?
1)当要使用子类特有功能时,用到向下转型
四、instanceof运算符
什么时候用向下转型?
答:当要使用子类特有功能时,就需要使用向下转型。
使用向下转型时的注意事项。
面对具体的子类对象,在向下转型时容易发生ClassCastException类型转换异常,在转换之前必须做类型判断。
如何在转换前做类型判断?
java中的instanceof运算符,用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
语法格式:
boolean result = object instanceof class
注意:class可以是类,也可以是接口!
instanceof在编译状态和运行状态的区别:
在编译状态中:
右边的“类或接口”是左边“对象”的父类、本身类和子类时,编译通过,否则编译失败!
本身类:此处的本身类指的就是“编译时”对应的类。
例如:Animal animal = new Tiger(); -->本身类就是Animal类
在运行转态中:
a)当左边“对象”不为null的时候
右边的“类或接口”是左边“对象”的父类、本身类时,这是返回结果就是true。
右边的“类或接口”是左边“对象”的兄弟类、子类时,这是返回结果就是false。
本身类:此处的本身类指的就是“运行时”对应的类。
例如:Animal animal = new Tiger(); -->本身类就是Tiger类
b)当左边“对象”就是null的时候
如果左边“对象”为null,那么instanceof运算的结果就是false!
【示例】 instanceof使用示例
class Animal { public void eat() { System.out.println("Animal eat ..."); } } class Dog extends Animal { public void lookHome() { System.out.println("Dog look home ..."); } } class ShepherdDog extends Dog { public void introduction() { System.out.println("Shepherd dog introduction ..."); } } class Cat extends Animal { public void catchMouse() { System.out.println("Cat catch mouse ..."); } } public class InstanceofDemo { public static void main(String[] args) { Animal animal = new Dog(); // 判断animal是否是Animal类的实例 if(animal instanceof Animal) { // 返回true Animal an = (Animal)animal; an.eat(); } // 判断animal是否是Dog类的实例 if(animal instanceof Dog) { // 返回true Dog dog = (Dog)animal; dog.lookHome(); } // 判断animal是否是ShepherdDog类的实例 if(animal instanceof ShepherdDog) { // 返回false ShepherdDog shepherdDog = (ShepherdDog)animal; shepherdDog.introduction(); } // 判断animal是否是Cat类的实例 if(animal instanceof Cat) {// 返回false Cat cat = (Cat)animal; cat.catchMouse(); } // boolean flag = animal instanceof String; // 编译失败 } } |
五、多态中成员变量的特点
无论编译和运行,都参考等号左边(引用类型变量所属的类)。
【示例】多态中成员变量的特点
class Father { int num = 10; } class Son extends Father { int num = 20; String name = "小明"; } public class InstanceDemo { public static void main(String[] args) { Father father = new Son(); // 多态中的成员变量:无论编译和运行,都参考等号左边的类 System.out.println("num:" + father.num); // 编译通过,输出父类中的num值 // System.out.println("name:" + father.name); 编译错误 } }
|
六、多态中成员方法的特点
编译看左边,参考等号左边引用对象所属的类是否有该方法。
运行看右边,在执行期间判断引用对象的实际类型,然后根据其实际的类型调用其相应的方法,又称为动态绑定。
class Father { public void eat() { System.out.println("父类中的eat方法"); } } class Son extends Father { public void study() { System.out.println("子类中的show方法"); } public void eat() { System.out.println("子类中的eat方法"); } } public class InstanceDemo { public static void main(String[] args) { Father father = new Son(); father.eat(); // 编译通过,调用子类中的eat方法 // father.study(); 编译错误 } } |