内容:
1、多态概述
2、多态代码体现
3、多态调用注意事项
4、多态的好处与弊端
5、向上向下类型转换
6、综合案例
1、多态概述
面向对象三大特性:封装(安全性)、继承(拓展性)、多态(灵活性)
多态是继封装、继承之后,面向对象的第三大特性,广义的多态是指对象的多态,一个对象的多种状态
现实事物经常会体现出多种形态,比如:
- 一只狗是狗,是动物,是生物
- 一个人是人,是动物,是生物
- 你 在学校里是学生,在家里便是儿子
狭义的多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用
在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,
该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为
在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的
类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所
绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
2、多态代码体现
Java多态的前提:
- 必须有子父类关系(继承)
- 必须有方法的重写
多态在Java中的表现形式:
Java中多态体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,
又可以给这个子类(实现类对象)的父类(接口)变量赋值
如Student类可以为Person类的子类,那么一个Student对象既可以赋值给一个Student类型的引用,
也可以赋值给一个Person类型的引用
具体格式如下:
- 父类类型 变量名 = new 子类类型();
- 变量名.方法名();
父类引用指向子类对象就是多态的定义格式。同一个父类的方法会被不同的子类重写为各自的具体实现。
而在调用方法时,调用的为各个子类重写后的方法,此时,虽然该变量指向的是子类对象,但表现为
一个父类的形态,可以调用一切父类的方法,子类特有的方法将不能调用
多态实例:
1 class Animal { 2 int age = 100; 3 public void eat(){ 4 System.out.println("动物在吃饭"); 5 } 6 } 7 8 class Dog extends Animal{ 9 int age = 1000; 10 11 public void eat() { 12 // TODO Auto-generated method stub 13 System.out.println("狗在吃饭"); 14 } 15 16 } 17 18 19 public class DuoTaiDemo { 20 public static void main(String[] args) { 21 // 使用多态创建一个狗 22 Animal an = new Dog(); 23 an.eat(); // 狗在吃饭 24 } 25 }
3、多态调用注意事项
- 成员变量编译看父类中是否存在,不存在编译失败
- 成员变量运行父类中的变量
- 成员方法编译看父类中是否存在,不存在编译失败
- 成员方法运行子类重写的方法
总结:多态使用变量,编译运行都看父类,而多态使用方法,编译看父类,运行看子类
实例如下:
1 class Animal { 2 int age = 100; 3 public void eat(){ 4 System.out.println("动物在吃饭"); 5 } 6 } 7 8 class Dog extends Animal{ 9 int age = 1000; 10 11 public void eat() { 12 // TODO Auto-generated method stub 13 System.out.println("狗在吃饭"); 14 } 15 16 } 17 18 19 public class DuoTaiDemo { 20 public static void main(String[] args) { 21 // 使用多态创建一个狗 22 Animal an = new Dog(); 23 an.eat(); // 狗在吃饭 => 子类 => 看右边 24 System.out.println(an.age); // 100 => 父类 => 看左边 25 } 26 }
另外静态成员和多态无关!
1 /* 2 * 多态调用: 3 * 非静态成员变量: 4 * 编译看父类 5 * 运行看父类 6 * 7 * 非静态成员方法:XXXXXXX 为什么?很简单 8 * 编译看父类 9 * 运行看子类 10 11 静态成员变量: 12 * 编译看父类 13 * 运行看父类 14 * 静态成员方法: 15 * 编译看父类 16 * 运行看父类 17 18 * 19 * 一句话解释 :我们说的多态是指对象的多态性: 静态不属于对象,属于类 20 */ 21 public class DuoTaiDemo { 22 public static void main(String[] args) { 23 Fu f = new Zi(); 24 //System.out.println(f.a); 25 f.show(); 26 } 27 } 28 public class Fu { 29 static int a = 1; 30 31 public static void show(){ 32 System.out.println("静态方法父类"); 33 } 34 } 35 public class Zi extends Fu{ 36 static int a = 2; 37 public static void show(){ 38 System.out.println("静态方法子类"); 39 } 40 }
4、多态的好处与弊端
多态:当变量名指向不同的子类对象时,由于每个子类重写父类方法的内容不同,所以会调用不同的方法
在Boss类中,有叫员工去工作的方法,当该方法的参数定义为接口时,可以传入任意的子类对象。
相比定义多个子类参数,定义多个方法,这样大大提高了代码复用性与扩展性:
1 class Boss{ 2 public void goToWork(Empolyee e){ 3 e.work(); 4 } 5 }
而多态也有缺点,实例如下:
1 class Animal { 2 int age = 100; 3 public void eat(){ 4 System.out.println("动物在吃饭"); 5 } 6 } 7 8 class Dog extends Animal{ 9 int age = 1000; 10 11 public void eat() { 12 // TODO Auto-generated method stub 13 System.out.println("狗在吃饭"); 14 } 15 16 public void lookHome(){ 17 System.out.println("狗在看门"); 18 } 19 20 } 21 22 23 public class DuoTaiDemo { 24 public static void main(String[] args) { 25 // 使用多态创建一个狗 26 Animal an = new Dog(); 27 an.eat(); // 狗在吃饭 28 System.out.println(an.age); // 100 29 an.lookHome(); // 报错 无法调用子类特有方法 30 } 31 }
综上述所:
- 多态的优点:配合继承与方法重写提高代码的复用性与扩展性
- 多态的弊端:多态只能调用子父类共有的方法,不能调用子类的特有方法
5、向上向下类型转换
多态本身是子类类型向父类类型向上转型的过程
多态的转型分为向上转型与向下转型两种:
向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal p = new Cat();
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,
这个过程是向下转型。如果是直接创建父类对象就无法向下转型的!
使用格式:
子类类型 变量名 = (子类类型) 父类类型的变量;
如:Cat c = (Cat) a; // 变量a实际上指向Cat对象
向下转型可以解决多态的弊端(不能调用子类的特有方法),实例如下:
1 class Animal { 2 int age = 100; 3 public void eat(){ 4 System.out.println("动物在吃饭"); 5 } 6 } 7 8 class Dog extends Animal{ 9 int age = 1000; 10 11 public void eat() { 12 // TODO Auto-generated method stub 13 System.out.println("狗在吃饭"); 14 } 15 16 public void lookHome(){ 17 System.out.println("狗在看门"); 18 } 19 20 } 21 22 23 public class DuoTaiDemo { 24 public static void main(String[] args) { 25 // 使用多态创建一个狗 26 Animal an = new Dog(); 27 an.eat(); // 狗在吃饭 28 System.out.println(an.age); // 100 29 // an.lookHome(); // 报错 30 Dog d = (Dog)an; // 向下转型 31 d.lookHome(); 32 } 33 }
6、综合案例
案例描述 - 笔记本电脑案例:
定义USB接口(具备开启功能、关闭功能),笔记本要使用USB设备,即笔记本在生产时需要预留
可以插入USB设备的USB接口,即就是笔记本具备使用USB设备的功能,但具体是什么USB设备,
笔记本并不关心,只要符合USB规格的设备都可以。鼠标和键盘要想能在电脑上使用,那么鼠标和
键盘也必须遵守USB规范,不然鼠标和键盘的生产出来无法使用
进行描述笔记本类,实现笔记本使用USB鼠标、USB键盘
- l USB接口,包含开启功能、关闭功能
- l 笔记本类,包含运行功能、关机功能、使用USB设备功能
- l 鼠标类,要符合USB接口
- l 键盘类,要符合USB接口
思路:
USB接口功能抽象成接口 =》开启USB、关闭USB、使用USB
笔记本类使用USB设备功能其实就是在调用USB接口中的方法
代码如下:
1 // USB接口 2 public interface USBInterface { 3 public abstract void open(); 4 public abstract void use(); 5 public abstract void close(); 6 }
1 // USB实现类及Computer类代码 2 class Computer { 3 public void start() { 4 System.out.println("电脑开机"); 5 } 6 7 public void end() { 8 System.out.println("电脑关机"); 9 } 10 11 // 使用USB设备 12 public void useUSB(USBInterface usb){ 13 usb.open(); 14 usb.use(); 15 usb.close(); 16 } 17 18 } 19 20 class KeyBoard implements USBInterface{ 21 22 public void open() { 23 System.out.println("键盘打开"); 24 } 25 26 public void use() { 27 System.out.println("使用键盘"); 28 } 29 30 public void close() { 31 System.out.println("键盘关闭"); 32 } 33 34 } 35 36 class Mouse implements USBInterface{ 37 public void open() { 38 System.out.println("鼠标打开"); 39 } 40 41 public void use() { 42 System.out.println("使用鼠标"); 43 } 44 45 public void close() { 46 System.out.println("鼠标关闭"); 47 } 48 } 49 50 public class ComputerDemo { 51 public static void main(String[] args) { 52 Computer t = new Computer(); 53 Mouse ms = new Mouse(); 54 KeyBoard kb = new KeyBoard(); 55 t.start(); 56 t.useUSB(ms); 57 t.useUSB(kb); 58 t.end(); 59 } 60 }