面向对象的特征之三:继承性
目录
1. 理解多态性:可以理解为一个事物的多种形态
2. 何为多态性:
1. 对象的多态性,父类的引用指向子类对象(或子类的对象赋值给父类的引用)
3. 多态性的使用,虚拟方法调用
1. 有了对象的多态性以后,我们在编译器,只能调用父类中声明的方法,但在运行期间,我们实际上调用的是子类重写父类的方法。
2. 总结:编译,看左边 运行,看右边
4. 多态性的使用前提:
1. 要有类的继承性
2. 方法的重写
5. 对象的多态性:只适用于方法,不适用于属性(编译和运行都看左边)
一、多态
1、为什么使用多态
它能使同一个操作当作用于不同的对象,产生不同的执行结果。
使用多态可以提高代码的可维护性和可扩展性。
2、什么是多态?
生活中的多态
不同类型的打印机打印效果不同程序中的多态(父类引用指向子类对象)
同一个引用类型,使用不同的实例而执行不同操作,同一种操作,由于条件不同,产生的结果也不同。3、如何实现多态?
实现多态的三要素
继承关系的父类和子类(继承是多态的基础)
子类重写父类方法
父类的引用指向子类的对象 Crop crop=new Corn(); crop=new AppleTree();
多态的类型向上转型——子类到父类的转换:自动类型转换
向下转型——父类到子类的转换:强制类型转换
二、向上转型
将一个父类的引用指向一个子类对象称为向上转型
语法:
<父类型 > < 引用变量名 > = new < 子类型 >(); 父类 变量名 =new 子类()
示例:
Crop crop = new AppleTree("富士"); //自动类型转换 crop.print(); //调用AppleTree类重写的print()方法
系统会自动进行类型转换
通过父类引用变量调用的方法是子类覆盖或继承的子类方法,不是父类的方法
通过父类引用变量无法调用子类特有的方法
三、多态的使用案例
注意: 为了保证数据的一致性,要将父类对象定义为类的全局变量,使用父类对象作为方法形参时,通过父类引用变量,可调用子类中重写的方法
总结: 实现多态的2种方式
1. 父类引用指向子类对象
2. 父类为形参,子类为实参,以此实现多态设计模式:
简单工厂设计模式——>抽象工厂
四、向下转型
问题:
星沐生态农场”种植的作物,在生长以及采摘过程中,由于特殊情况,需要处理一些变化,要求:
使用嫁接技术,将已种植的其他品种苹果转换为苹果新品种“红粉佳人”,后续也可以完成其他品种的嫁接
如果玉米等作物赶上丰收年,需申请换用联合收割机,且变更收割费用为100元
分析:
AppleTree类
添加grafting(String newBread)方法,实现果树的嫁接功能
Corn类
添加reHarvester(double cost)方法,实现收割机变更功能
测试类
多态形式创建对象并调用
示例:
AppleTree苹果树类嫁接实现的关键代码://嫁接新品种 public void grafting(String newBrand) { if(this.brand == newBrand) { System.out.println("同品种果树无需嫁接。"); } else { this.brand = newBrand; System.out.println("经过嫁接,"+super.getName() + "的品种变为"+this.brand+"。"); } }
Corn玉米类关键代码:
//更换收割机 public void reHarvester(double cost) { if(this.harvestCost == cost) { System.out.println("更换收割机后,费用不变!"); } else { this.harvestCost = cost; System.out.println("更换收割机后,费用变为"+this.harvestCost+"。"); } }
Test类关键代码:
问题: 如何才能使父类对象访问其子类特有的属性和方法呢
方案: 向下转型
将一个指向子类对象的父类引用赋给一个子类的引用,即将父类类型转换为子类类型,称为向下转型向下转型必须进行强制类型转换
将父类类型转换为它的某个子类类型后,才能调用其子类特有的属性
语法:<< 子类型 > < 引用变量名 > = (< 子类型 >)< 父类型的引用变量 >
示例:
AppleTree appleTree =(AppleTree) crop; //将crop转换为AppleTree 类型 appleTree.grafting("粉红佳人"); //调用苹果树的嫁接方法
五、instanceof运算符
从父类到子类的向下转型,可以实现多态,即执行不同子类中定义的特定方法
但事实上,父类对象的引用可能指向某一个子类对象
如果在向下转型时,不是转换为真实的子类类型,就会出现转换异常
示例:/** * ClaName:Test6 * Description:TODO * date:2022/3/31 9:55 * * @author Reincarnation * @version 1.0 * @since JDK 1.8 */ public class Test6 { public static void main(String[] args) { //创建一个农作物的数组 Crop[] crops = new Crop[2]; Crop crop = null; //在数组中初始化了两种农作物 crops[0] = new AppleTree("富士");//苹果 crops[1] = new Corn(50);//玉米 //循环输出农作的个性化信息 for (Crop model : crops) { model.print(); AppleTree appleTree = (AppleTree) model; appleTree.grafting("蛇果"); } } }
JVM检测到两个类型间不兼容时,引发的运行时异常ClassCastException
注:构造函数没有数据
问题:如何通过代码避免这种异常的发生?
使用instanceof运算符
用于判断一个对象是否属于一个类或者实现了一个接口
语法:对象 instanceof 类 | 接口 //运行结果为true或false
作用
避免引发类型转换异常,提高代码的健壮性
应用场合
向下转型之前,先使用instanceof进行类型判断/** * ClaName:Test6 * Description:TODO * date:2022/3/31 9:55 * * @author Reincarnation * @version 1.0 * @since JDK 1.8 */ public class Test6 { public static void main(String[] args) { //创建一个农作物的数组 Crop[] crops = new Crop[2]; Crop crop = null; //在数组中初始化了两种农作物 crops[0] = new AppleTree("富士");//苹果 crops[1] = new Corn(50);//玉米 //crops[0].grafting("红粉佳人"); //解决问题 for (Crop model : crops) { model.print(); if (model instanceof AppleTree) { AppleTree appleTree = (AppleTree) model; appleTree.grafting("蛇果"); } else if (model instanceof Corn) { Corn corn = (Corn) model; corn.reHarvester(100); } } } }
注:构造函数没有数据
注意事项:
对象的类型必须与instanceof参数后所指定的类或接口在继承树上具有上下级关系;否则会出现编译错误
六、多态的优势
七、面向对象编程
面向对象的三大特性:封装、继承、多态
封装是隐藏对象的属性和实现细节
将类的成员属性声明为私有的,同时提供公有的方法实现对该成员属性的存取操作
继承是软件可重用性的一种表现
新类可以在不增加自身代码的情况下,通过从现有的类中继承其属性和方法充实自身内容
多态是具有表现多种形态的能力的特征
在程序设计的术语中,意味着一个特定类型的变量可以引用不同类型的对象,自动地调用引用的对象的方法
即根据作用到的不同对象类型,响应不同的操作