下雨了,跑不跑衣服都会被淋湿,还不如慢慢地走,结局已定
—— 24.4.25
多态
1.面向对象三大特性:封装 继承 多态
2.怎么学:
a、不要从字面意思上理解多态这两个字,要从使用形式上掌握
b、要知道多态的好处
c、要知道多态的前提(什么样的情况下才能形成多态)
一、多态的介绍和基本使用
1.前提:
a、必须有子父类继承或者接口实现关系
b、必须有方法的重写(没有重写,多态没有意义),多态主要是调用重写方法
c、new对象:父类引用指向子类对象
Fu fu = new Son() -> 理解为大类型接收了一个小类型的数据 -> 比如:double b = 10
2.注意:
多态下不能直接调用子类特有功能
3.基本使用
抽象类
public abstract class Animal { public abstract void eat(); }
子类1
public class Dog extends Animal{ @Override public void eat() { System.out.println("狗啃骨头"); } // 特有方法 public void lookDoor(){ System.out.println("狗会看门"); } }
子类2
public class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼"); } // 特有方法 public void catchmouse(){ System.out.println("猫会抓老鼠"); } }
测试类
public class Demo122Test { public static void main(String[] args) { // 原始方法 Dog dog = new Dog(); // 重写的方法 dog.eat(); // 特有的方法 dog.lookDoor(); Cat cat = new Cat(); // 重写的方法 cat.eat(); // 特有的方法 cat.catchmouse(); System.out.println("——————————————————————"); // 多态形式new对象 Animal animal = new Dog();// 相当于double b = 10 // 重写的animal,接受的是dog对象,所以调用的是dog里的eat方法 animal.eat(); // animal.loolDoor(); 多态前提下,不能直接调用子类特有成员 Animal animal1 = new Cat(); // cat重写的方法 animal1.eat(); } }
二、多态的前提下成员访问特点
成员变量
父类
public class Fu { int num = 1000; }
子类
public class Son extends Fu{ int num = 100; }
测试类
public class Demo123Test { public static void main(String[] args) { Fu fu = new Son(); System.out.println(fu.num); } }
成员变量看等号左边是谁,先调用谁中的成员变量
成员方法
父类
public class Fu { int num = 1000; public void method(){ System.out.println("父类中的method方法"); } }
子类
public class Son extends Fu{ int num = 100; public void method(){ System.out.println("我是子类中重写的method方法"); } }
测试类
public class Demo123Test { public static void main(String[] args) { Fu fu = new Son(); System.out.println(fu.num);// 父类中的num fu.method();// 我是子类中重写的method方法 } }
成员方法看new的是谁,先调用谁中的成员方法,子类没有找父类
三、多态的好处
1.问题描述:
如果使用原始方式new对象(等号左右两边一样),既能调用重写的,还能调用继承的,还能调用自己特有的成员
但是多态方式new对象,只能调用重写的,不能直接调用子类特有的成员,那为啥还要用多态呢
2.多态方式和原始方式new对象的优缺点:
原始方式:
a、优点:既能调用重写的,又能调用继承的,还能调用自己特有的成员
b、缺点:扩展性差,尤其是方法的参数传递
多态方式:
a、优点:扩展性强(父类可以接收其任意的子类)
b、缺点:不能直接调用子类特有的功能
Fu fu = new Son();
double b = 10;
b = 100L;
形参传递父类类型,调用此方法父类型可以接收任意它的子类对象
传递哪个子类对象,就指向哪个子类对象,就调用哪个子类对象重写的方法示例:
animal
public abstract class Animal { public abstract void eat(); }
cat
public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } // 特有方法 public void catchmouse(){ System.out.println("猫会抓老鼠"); } }
dog
public class Dog extends Animal { @Override public void eat() { System.out.println("狗啃骨头"); } // 特有方法 public void lookDoor(){ System.out.println("狗会看门"); } }
test1
public class Demo124Test1 { public static void main(String[] args) { Dog dog = new Dog(); // 重写的方法 dog.eat(); // 特有的方法 dog.lookDoor(); System.out.println("————————————————————"); method(dog); Cat cat = new Cat(); method(cat); } public static void method(Dog dog){ dog.eat(); dog.lookDoor(); } public static void method(Cat cat){ cat.eat(); cat.catchmouse(); } }
test2
public class Demo125Test2 { public static void main(String[] args) { // 父类接收子类类型 Animal animal = new Dog(); animal.eat(); animal = new Cat(); animal.eat(); // 传统方式接受类型 Dog dog = new Dog(); method(dog); Cat cat = new Cat(); method(cat); } // 形参传递父类类型,调用此方法父类型可以接收任意它的子类对象 // 传递哪个子类对象,就指向哪个子类对象,就调用哪个子类对象重写的方法 public static void method(Animal animal){ animal.eat(); } }
四、多态中的转型
1.向上转型
① 父类引用指向子类对象
好比是:double b = 1;
2.向下转型
① 好比强转,将大类型强制转换成小类型
② 表现方式:
父类类型 对象名1 = new 子类对象() ——> 向上转型 ——> double b = 1
子类类型 对象名2 = (子类类型)对象名1 ——> 向下转型 ——> int i = (int)b
③ 想要调用子类特有的功能,我们就需要向下转型
3.案例
父类animal
public abstract class Animal { public abstract void eat(); }
子类cat
public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } // 特有方法 public void catchmouse(){ System.out.println("猫会抓老鼠"); } }
子类dog
public class Dog extends Animal { @Override public void eat() { System.out.println("狗啃骨头"); } // 特有方法 public void lookDoor(){ System.out.println("狗会看门"); } }
测试类
public class Demo126Test01 { public static void main(String[] args) { // 多态new法 Animal animal = new Dog(); animal.eat();// dog重写的 /* animal.lookDoor();// 多态不能调用子类特有功能 */ // 向下转型 强转 就可以调用子类的特有功能 Dog dog = (Dog) animal; dog.eat(); dog.lookDoor(); } }
五、转型中可能出现的问题
1.类型转换异常(ClassCastException)
如果等号左右两边类型不一致,则会出现类型转换异常(ClassCastException)
原因:当调用method传递Cat对象时,animal代表的就是cat对象
此时,我们将代表cat对象的animal强转成了animal
此时等号左右两边类型不一致了,所以出现了类型转换异常public class Demo128Test { public static void main(String[] args) { Dog dog = new Dog(); method(dog); System.out.println("——————————————————————————"); Cat cat = new Cat(); method(cat); } public static void method(Animal animal){ // animal = dog // animal = cat animal.eat(); /* 这里会出现类型转换异常 ClassCastException 原因:当调用method传递Cat对象时,animal代表的就是cat对象 此时,我们将代表cat对象的animal强转成了animal 此时等号左右两边类型不一致了,所以出现了类型转换异常 */ Dog dog = (Dog) animal; dog.lookDoor(); } }
2.解决:
在向下转型之前,先判断类型
3.判断类型的方法:
instanceof
判断结果是boolean型
4.使用
对象名 instanceof 类型 ——> 判断的是关键字前面的对象是否符合关键字后面的类型
5.示例
public class Demo128Test { public static void main(String[] args) { Dog dog = new Dog(); method(dog); System.out.println("——————————————————————————"); Cat cat = new Cat(); method(cat); } public static void method(Animal animal){ // animal = dog // animal = cat // animal.eat(); /* 这里会出现类型转换异常 ClassCastException 原因:当调用method传递Cat对象时,animal代表的就是cat对象 此时,我们将代表cat对象的animal强转成了animal 此时等号左右两边类型不一致了,所以出现了类型转换异常 */ // Dog dog = (Dog) animal; // dog.lookDoor(); if (animal instanceof Dog){ Dog dog = (Dog) animal; dog.eat(); dog.lookDoor(); } else if (animal instanceof Cat) { Cat cat = (Cat) animal; cat.eat(); cat.catchmouse(); } } }
六、综合练习
案例要求:
定义笔记本类,具备开机,关机和使用USB设备的功能。具体是什么USB设备,笔记本并不关心,只要符合USB规格的设备都可以。鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,不然鼠标和键盘的生产出来无法使用;进行描述笔记本类,实现笔记本使用USB鼠标、USB键盘
USB接口,包含开启功能、关闭功能
笔记本类,包含运行功能、关机功能、使用USB设备功能
鼠标类,要符合USB接口
键盘类,要符合USB接口定义USB接口:
public interface USB { // 开启功能 public abstract void Open(); // 关闭功能 public abstract void Close(); }
笔记本类:
接收USB接口和鼠标类键盘类对接口的具体实现,然后进行方法的定义,私有方法对接收的类用instanceof进行判断,然后不同类型不同实现
public class NoteBook { // 开机 public void Start(){ System.out.println("笔记本在运行"); } // 关机 public void Close(){ System.out.println("笔记本关机"); } // 关机 /* USB usb = mouse 多态 USB usb = keyBoard 多态 */ public void UseUSB(USB usb){ if(usb instanceof Mouse){ Mouse mouse = (Mouse) usb; mouse.Open(); mouse.click(); mouse.Close(); } else if (usb instanceof KeyBoard) { KeyBoard keyBoard = (KeyBoard) usb; keyBoard.Open(); keyBoard.input(); keyBoard.Close(); } // usb.Open(); // usb.Close(); // System.out.println("使用USB功能"); } }
定义鼠标类,符合USB接口
继承USB父类接口进行鼠标类Mouse具体的实现
public class Mouse implements USB { @Override public void Open() { System.out.println("鼠标开启"); } @Override public void Close() { System.out.println("鼠标关闭"); } // 特有方法 public void click(){ System.out.println("点击鼠标"); } }
定义键盘类,符合USB接口
继承USB父类接口进行键盘类KeyBoard具体的实现
public class KeyBoard implements USB{ @Override public void Open() { System.out.println("键盘开启"); } @Override public void Close() { System.out.println("键盘关闭"); } // 特有功能 public void input(){ System.out.println("敲击键盘"); } }
测试类
public class Demo129Test { public static void main(String[] args) { NoteBook noteBook = new NoteBook(); Mouse mouse = new Mouse(); noteBook.Start(); // 多态的使用 noteBook.UseUSB(mouse); noteBook.Close(); System.out.println("——————————————————"); KeyBoard keyBoard = new KeyBoard(); noteBook.Start(); // 多态的使用 noteBook.UseUSB(keyBoard); noteBook.Start(); } }
运行结果
七、总结
1.接口
概述
是一种引用数据类型,是一个标准,规则
⭐定义
a、定义接口 —— public interface 接口名{}
b、实现接口 —— public class 实现类 implements 接口{}
⭐抽象方法
a、定义 —— public abstract 返回值类型 方法名(形参);
b、使用 —— 实现类实现接口
—— 重写抽象方法
—— 创建实现类对象,调用重写方法 —— 接口不能new对象
默认方法
a、定义 —— public default 返回值类型 方法名(形参){
方法体;
return;
}
b、使用 —— 在实现类中可重写,可不重写
—— 创建实现类对象,调用默认方法
静态方法
a、定义 —— public static 返回值类型 方法名(形参){
方法体;
return 结果;
}
b、使用 —— 接口名直接调用
成员变量
a、定义 —— public static final 数据类型 变量名 = 值
b、使用 —— 接口名直接调用
⭐接口的特点
a、接口可以多继承
b、接口可以多实现
c、一个类可以继承一个父类的同时实现一个或多个接口
接口的抽象类的区别
a、相同点
① 都位于继承顶端,用于被其他类继承或者实现
② 都不能new
③ 都包含抽象方法,而且必须重写
b、不同点
① 抽象类:一般作为父类使用,可有成员变量,构造方法,成员方法,抽象方法等
② 接口:成员单一,一般抽取接口,都是方法
③ 类不能多继承,接口可以
⭐2.多态
前提
a、必须有子父类继承关系或者接口实现关系
b、必须有方法的重写
c、父类引用指向子类对象
成员访问特点
a、成员变量 —— 看等号左边是谁
b、成员方法 —— 看new的是谁
特点
a、优点 —— 拓展性强
b、缺点 —— 不能直接调用子类特有功能
转型
a、向上转型 —— 父类引用指向子类对象
b、向下转型 —— 将父类类型转成子类类型 —— 调用子类特有功能
c、类型转换异常 —— ClassCastException
d、类型判断 —— 对象名 instanceof —— 判断对象是否属于指定的类型