day18_面向对象的三大特征之多态
多态概述
多态是继封装、继承之后,面向对象的第三大特性。生活中,比如求面积的功能,圆、矩形、三角形实现起来是不一样的。跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态
定义
- 多态: 是指同一行为,对于不同的对象具有多个不同表现形式。
- 程序中多态: 是指同一方法,对于不同的对象具有不同的实现.
前提条件
- 继承或者实现父类
- 引用指向子类对象\接口引用指向实现类对象
- 方法的重写
实现多态
代码示例
class Animal{//父类 public void eat(){ System.out.println("吃东西"); } } //子类 class Dog extends Animal{ @Override public void eat() {//方法重写 System.out.println("狗吃骨头"); } } //子类 class Cat extends Animal{ @Override public void eat() {//方法重写 System.out.println("猫吃鱼"); } } public class Test { public static void main(String[] args) { /* 多态: 同一种行为,不同的事物具有不同的表现形态 实现多态: 1.继承或者实现 2.父类引用指向子类对象\接口引用指向实现类对象 3.方法重写 */ // 父类引用指向子类对象 Animal dog = new Dog(); dog.eat();//狗吃骨头 Animal cat = new Cat(); cat.eat();//猫吃鱼 } }
编译时类型与运行时类型不一致问题
- 编译时,看“父类”,只能调用父类声明的方法,不能调用子类扩展的方法;
- 运行时,看“子类”,一定是执行子类重写的方法体;
多态时访问成员的特点
class Fu { int a = 10; public static void method1() { System.out.println("我是父类静态方法"); } public void method2() { System.out.println("我是父类非静态方法"); } } class Zi extends Fu { int a = 20; public static void method1() { System.out.println("我是子类静态方法"); } public void method2() { System.out.println("我是子类非静态方法"); } } public class Test { public static void main(String[] args) { //多态 Fu Demo = new Zi(); System.out.println(Demo.a);//10 Demo.method1();//我是父类静态方法 Demo.method2();//我是子类非静态方法 } }
多态时成员变量的访问特点
- 编译看左边,运行看左边:简而言之:多态的情况下,访问的是父类的成员变量
简单记多态时成员方法的访问特点
- 非静态方法:编译的时候去父类中查找方法,运行的时候优先去子类中查找方法来执行
- 静态方法:编译的时候去父类中查找方法,运行的时候去父类中查找方法来执行
多态的应用
如果变量的类型为父类类型,该变量就可以接收该父类类型的对象或者其所有子类对象
Object obj = new Car();
多态应用在形参实参:参数类型为父类类型,该参数就可以接收该父类类型的对象或者其所有子类对象
public class Test { public static void main(String[] args) { // 形参多态:参数类型为父类类型,该参数就可以接收该父类类型的对象或者其所有子类对象 Dog d = new Dog(); method(d); System.out.println("==============================="); Cat c = new Cat(); method(c); } // 需求: 定义一个方法,带有一个参数,该参数可以接收Animal类对象以及Animal类的所有子类对象 // method(d); ====实参赋值给形参的时候==> Animal anl = new Dog(); // method(c); ====实参赋值给形参的时候==> Animal anl = new Cat(); public static void method(Animal anl){ anl.eat(); } }
多态应用在返回值:如果返回值类型为父类类型,那么就可以返回该父类类型的对象或者其所有子类对象
/* * 设计一个方法,可以购买各种动物的对象,此时不确定是那种具体的动物 * * 返回值类型是父类的对象 * * 多态体现在 返回值类型 Animal ,实际返回的对象是子类的new Cat(),或new Dog() */ public static Animal buy(String name){ if("猫咪".equals(name)){ return new Cat(); }else if("小狗".equals(name)){ return new Dog(); } return null; }
多态应用在数组:数组元素类型声明为父类类型,可以存储父类类型和其子类类型
Animal[] arr = new Animal[2]; //在堆中开辟了长度为5的数组空间,用来装Animal或它子类对象的地址 arr[0] = new Cat();//多态引用 左边arr[0] 是Animal类型,右边是new Cat() arr[1] = new Dog();
一个对象在new的时候创建是哪个类型的对象,它从头至尾都不会变。即这个对象的运行时类型,本质的类型用于不会变。这个和基本数据类型的转换是不同的。但是,把这个对象赋值给不同类型的变量时,这些变量的编译时类型却不同。
多态的好处和弊端
- 好处:实际开发的过程中,父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展性与便利。
- 弊端:多态的情况下,只能调用父类的共性内容,不能调用子类的特有内容。
代码示例
class Animal{//父类 public void eat(){ System.out.println("吃东西"); } } //子类 class Cat extends Animal{ @Override public void eat() {//方法重写 System.out.println("猫吃鱼"); } public void catchFish(){ System.out.println("猫会抓鱼"); } } public class Test { public static void main(String[] args) { /* 多态: 同一种行为,不同的事物具有不同的表现形态 实现多态: 1.继承或者实现 2.父类引用指向子类对象\接口引用指向实现类对象 3.方法重写 */ // 父类引用指向子类对象 Animal cat = new Cat(); cat.eat();//猫吃鱼 //cat.catchFish(); 编译报错,因为多态成员访问的特点是,编译看父类,而父类中没有子类独有的功能 } }
解决弊端的方式:
因为多态,就一定会有把子类对象赋值给父类变量的时候,这个时候,在编译期间,就会出现类型转换的现象。但是,使用父类变量接收了子类对象之后,我们就不能调用子类拥有,而父类没有的方法了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做类型转换。
向上转型:当左边的变量的类型(父类) > 右边对象/变量的类型(子类),我们就称为向上转型
- 此时,编译时按照左边变量的类型处理,就只能调用父类中有的变量和方法,不能调用子类特有的变量和方法了
- 但是,运行时,仍然是对象本身的类型
- 此时,一定是安全的,而且也是自动完成的
// 向上转型 Animal a = new Cat();
向下转型:当左边的变量的类型(子类)<右边对象/变量的类型(父类),我们就称为向下转型
- 此时,编译时按照左边变量的类型处理,就可以调用子类特有的变量和方法了
- 但是,运行时,仍然是对象本身的类型
- 此时,不一定是安全的,需要使用(类型)进行强制类型转换
- 不是所有通过编译的向下转型都是正确的,可能会发生ClassCastException,为了安全,可以通过isInstanceof关键字进行判断、
Aniaml anl = new Cat(); Cat c = (Cat)anl;//向下转型
为了避免ClassCastException的发生,Java提供了 instanceof`关键字,给引用变量做类型的校验,只要用instanceof判断返回true的,那么强转为该类型就一定是安全的,不会报ClassCastException异常。
执行流程:
- 判断前面变量指向的对象类型是否是后面的数据类型:
- 如果前面变量指向的对象类型是属于后面的数据类型,那么就返回true
- 如果前面变量指向的对象类型不是属于后面的数据类型,那么就返回false
所以,转换前,我们最好先做一个判断,代码如下:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 if (a instanceof Cat){ Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } else if (a instanceof Dog){ Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse } } }
多态的应用场景综合案例
package demo07; class Animal{ public void eat(){ System.out.println("吃东西..."); } } class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头..."); } // 特有的功能 public void lookHome(){ System.out.println("狗在看家..."); } } class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼..."); } // 特有的功能 public void catchMouse(){ System.out.println("猫抓老鼠..."); } } public class Test { public static void main(String[] args) { Dog d = new Dog(); method(d); System.out.println("=========================="); Cat c = new Cat(); method(c); } // 形参多态: 如果父类类型作为方法的形参类型,那么就可以接收该父类类型的对象或者其所有子类的对象 public static void method(Animal anl){ anl.eat(); if (anl instanceof Dog){ Dog d = (Dog)anl;// 向下转型 Dog类型 d.lookHome(); } if (anl instanceof Cat){ Cat c = (Cat)anl;// 向下转型 Cat类型 c.catchMouse(); } } }


浙公网安备 33010602011771号