7. 面向对象特征三:多态性
概念
多态是面向对象程序设计(OOP)的一个重要特征,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
变色龙
女朋友养宠物
王者荣耀英雄
7.1 多态的形式和体现
7.1.1 对象的多态性
多态性,是面向对象中最重要的概念,
在 Java 中如何体现的:对象的多态性:父类的引用指向子类的对象
格式:父类 变量名 = new 子类;(Father a = new Son();)
2 . 特点
多态的前提1:是继承
多态的前提2:要有方法的重写
练习:多态入门案例
创建包: cn.tedu.oop
创建类: TestDemo.java
package cn.tedu.oop2;
/*本类用作多态的入门案例*/
public class TestDemo {
public static void main(String[] args) {
//6.创建“纯纯的”对象用于测试
Animal a = new Animal();
Cat c = new Cat();
Dog d = new Dog();
a.eat();//小动物Animal吃啥都行~调用的是父类自己的功能
c.eat();//小猫爱吃小鱼干~调用的是子类重写后的功能
d.eat();//小狗爱吃肉骨头~调用的是子类重写后的功能
/*2.父类对象不可以使用子类的特有功能*/
//a.jump();//报错,Animal类里并没有这个方法
//a.run();//报错,Animal类里并没有这个方法
c.jump();//小猫Cat跳的老高啦~,子类可以调用自己的功能
d.run();//小狗Dog跑的老快啦~,子类可以调用自己的功能
//7.创建多态对象进行测试
/*3.口诀1:父类引用指向子类对象
* 解释:创建出来的子类对象的地址值,交给父类类型的引用类型变量来保存*/
Animal a2 = new Cat();//Cat类对象的地址值交给父类型变量a2来保存
Animal a3 = new Dog();//Dog类对象的地址值交给父类型变量a3来保存
//8.测试多态对象
/*4.口诀2:编译看左边,运行看右边
* 解释:必须要在父类定义这个方法,才能通过编译,把多态对象看作是父类类型
* 必须要在子类重写这个方法,才能满足多态,实际干活的是子类*/
a2.eat();//小猫爱吃小鱼干~,多态对象使用的是父类的定义,子类的方法体
}
}
/*1.多态的前提:继承+重写*/
//1.创建父类
class Animal{
//3.创建父类的普通方法
public void eat(){
System.out.println("小动物Animal吃啥都行~");
}
}
//2.1创建子类1
class Cat extends Animal{
//4.1添加重写的方法
public void eat(){
System.out.println("小猫爱吃小鱼干~");
}
//5.1添加子类的特有功能
public void jump(){
System.out.println("小猫Cat跳的老高啦~");
}
}
//2.2创建子类2
class Dog extends Animal{
//4.2添加重写的方法
@Override
public void eat(){
System.out.println("小狗爱吃肉骨头~");
}
//5.2添加子类的特有功能
public void run(){
System.out.println("小狗Dog跑的老快啦~");
}
}
父类引用指向子类对象,如:Animal a = new Cat();
多态中,编译看左边,运行看右边
- 练习:多态成员使用测试
创建包: cn.tedu.oop
创建类: TestDemo2.java
package cn.tedu.oop2;
/*本类用于测试多态成员的使用情况*/
public class TestDemo2 {
public static void main(String[] args) {
//7.创建纯纯的子类对象
Dog2 d = new Dog2();
System.out.println(d.sum);//20,子类自己的属性
d.eat();//小狗爱吃肉包子,子类自己的方法
//8.创建多态对象
/*口诀1:父类引用指向子类对象*/
/*口诀2:编译(保存)看左边,运行(效果)看右边*/
Animal2 a = new Dog2();
/*多态中,成员变量使用的是父类的*/
System.out.println(a.sum);//10
/*多态中,方法的声明使用的是父类的,方法体使用的是子类的*/
a.eat();//小狗爱吃肉包子
/*多态中,调用的静态方法是父类的,因为多态对象把自己看作是父类类型
* 直接使用父类中的静态资源*/
a.play();//没有提示,玩啥都行~
Animal2.play();
}
}
//1.创建父类
class Animal2{
//3.创建父类的成员变量
int sum = 10;
//4.创建父类的普通方法
public void eat(){
System.out.println("吃啥都行~");
}
//9.1定义父类的静态方法play
public static void play(){
System.out.println("玩啥都行~");
}
}
//2.创建子类
class Dog2 extends Animal2{
//5.定义子类的成员变量
int sum = 20;
//6.重写父类的方法
@Override
public void eat(){
System.out.println("小狗爱吃肉包子");
}
//9.2创建子类的静态方法play
//@Override
/*这不是一个重写的方法,只是恰巧在两个类中出现了一模一样的两个静态方法
* 静态方法属于类资源,只有一份,不存在重写的现象
* 在哪个类里定义,就作为哪个类的资源使用*/
public static void play(){
System.out.println("小狗喜欢玩皮球~");
}
}
7 拓展 (选讲)
7.1 设计汽车综合案例
创建包: cn.tedu.oopexec
创建类: DesignCar.java
package cn.tedu.oop2;
/*本类用于完成汽车设计案例*/
public class DesignCar {
public static void main(String[] args) {
//9.创建一个纯纯的父类对象进行测试
Car c = new Car();
System.out.println(c.getColor());//null
c.start();
c.stop();
//c.swim();//报错,父类对象不可以调用子类的特有功能
//10.创建纯纯的子类对象做测试
BMW b = new BMW();
System.out.println(b.color);//五彩斑斓的黑
System.out.println(b.getColor());//null
b.start();//都让开,我的车要起飞啦~
b.stop();//唉呀妈呀熄火了~
//11.创建多态对象进行测试
Car c2 = new TSL();
//System.out.println(c2.color);
System.out.println(c2.getColor());
c2.stop();
c2.start();
//c2.swim();
}
}
//1.通过分析,抽象形成一个汽车类
class Car{
//2.定义并封装汽车类的属性--成员变量
private String brand;//品牌
private String color;//颜色
private int id;//编号
private double price;//价格
//3.定义功能
public void start(){
System.out.println("我的小车车启动啦~");
}
public void stop(){
System.out.println("唉呀妈呀熄火了~");
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
//4.创建子类
class BMW extends Car{
String color = "五彩斑斓的黑";
//5.重写父类的方法
@Override
public void start(){
System.out.println("都让开,我的车要起飞啦~");
}
}
//6.创建子类2
class TSL extends Car{
//7.重写父类的方法
@Override
public void stop(){
System.out.println("唉呀妈,怎么停不下来呢");
}
//8.添加子类的特有功能
public void swim(){
System.out.println("没想到吧,我还是个潜水艇");
}
}
7.2 多态为了统一调用标准 (选讲)
package cn.tedu.oop2;
public class TestFruit {
public static void main(String[] args) {
Fruit f = new Fruit();
Apple a = new Apple();
Orange o = new Orange();
get(f);
get(a);
get(o);
}
//只需要创建一个方法,就可以执行截然不同的效果
//忽略子类对象的差异统一看作父类类型
public static void get(Fruit f){
f.clean();
}
}
class Fruit{
public void clean(){
System.out.println("水果要洗洗再吃");
}
}
class Apple extends Fruit{
@Override
public void clean(){
System.out.println("苹果需要削皮");
}
}
class Orange extends Fruit{
@Override
public void clean(){
System.out.println("橙子需要剥皮");
}
}
多态的好处和弊端
好处:变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编
写更灵活、功能更强大,可维护性和扩展性更好了。
弊端:一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,
那么该变量就不能再访问子类中添加的属性和方法。
向上转型与向下转型
在JAVA中,继承是一个重要的特征,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以重写父类中的方法来加以扩展。
那么在这个过程中就存在着多态的应用。存在着两种转型方式,分别是:向上转型和向下转型。
向上转型:可以把不同的子类对象都当作父类来看,
比如:父类Parent,子类Child
父类的引用指向子类对象:Parent p=new Child();
说明:向上转型时,子类对象当成父类对象,
比如:花木兰替父从军,大家都把花木兰看做她爸,但是实际从军的是花木兰,而且,花木兰只能做她爸能做的事,在军营里是不可以化妆的。
向下转型(较少)
:子类的引用的指向子类对象,过程中必须要采取到强制转型。这个是之前向上造型过的子类对象仍然想执行子类的特有功能,所以需要重新恢复成子类对象
Parent p = new Child();//向上转型,
此时,p是Parent类型Child c = (Child)p;//此时,把Parent类型的p转成小类型Child
比如:花木兰打仗结束,就不需要再看做是她爸了,就可以”对镜贴花黄”了
7.6.2 如何向上或向下转型 (选讲)
向上转型:自动完成
向下转型:(子类类型)父类变量
package com.tedu;
import java.sql.SQLOutput;
public class Test1 {
public static void main(String[] args) {
Fruit fruit=new Apple();
fruit.eat();
//fruit.p();报错,不能使用子类的功能
System.out.println(fruit.age);
System.out.println(fruit.name);
//向上转型我们只能使用父类的功能,但是如果我们想要使用子类的功能怎么办
//向下转型
Apple apple=(Apple) fruit;
//我们通过向下转型就可以使用父类的功能
System.out.println(apple.sex);
apple.p();
//instancof的测试
//使用bana类测试向下转型
//Bana bana=(Bana) fruit;
/**
* 这里之所以运行会报错是因为,开辟空间的时候只开辟了
* apple类的内存空间,但是在这里却要指向bana类所以报错类
*
* 由于fruit对象是由Fruit类创建完成的,向下转型是只能转换成Fruit类型
* */
// bana.eat();//这个时候会报错
/** 为了解决这样的一个错误我们使用一个instanceof关键字进行与类型的判断
*
* 父类对象 instanceof 子类类型,如果成立则为true 否则为false
* 为了确保转型成功我们先进行变量的验证
* */
if (fruit instanceof Fruit){
Fruit fruit1=(Fruit)fruit;
fruit1.eat();
}
if (fruit instanceof Bana){
Bana bana1=(Bana)fruit;
}else {
System.out.println("转型失败");
}
}
}
class Fruit{
String name="水果";
int age=33;
public void eat(){
System.out.println("水果要洗洗再吃");
}
}
class Apple extends Fruit{
String sex="男";
public void p(){
System.out.println("这是一个玩耍的方法");
}
}
class Bana extends Fruit{
String color="黄色";
public void c(){
System.out.println("这是一个颜色方法");
}
}