十七、OOP之理解《多态》

本文详细介绍了Java中的多态性,包括对象多态、方法多态、向上转型、向下转型以及多态的好处——提高代码扩展性。还讨论了抽象方法、抽象类的概念和它们之间的关系。
摘要由CSDN通过智能技术生成

做了那么多铺垫,现在我们理解OOP的最后一个特性多态。
那么什么是多态?有什么好处?
多态最简单的一句话说就是方法的多态,属性没有多态
多态的好处 就是 提高了代码的扩展性下面会详细介绍

一、多态

同一个方法可以根据发送对象的不同而采区多种不同的行为方式、
一个对象的实际类型是确定的,但是可以指向对象的引用类型有很多
就比如遇到的报错:类型转换异常 ClassCastException!
static方法属于类,不属于实例。不能重写
final 常量;池里面的也不能重写
private私有的 也不能重新
1、对象的多态性:同一个对象,在不同的环境下,有不同的名称 (事物的多种形态)
如:你在教室 同学们叫你《小虫虫 》 回到了宿舍 你是他们的《室友》 回到了家 你是爸爸妈妈的《儿子》 狗蛋 😉

2、java中,同一个对象有不同类型的引用指向他,本质上,同一个事物具有不同的名称和描述
(1) MiddleStudent(中学生) 继承 Student(学生) 继承 Person(人)

(2) MiddleStudent ms = new MiddleStudent(); //初中生对象

(3) MiddleStudent ms2 = ms;

(4) Student ss = new MiddleStudent();

(5) Person p = ss;

(6) java中的多态指的是,父类的引用指向子类对象

3、多态的实现前提: (多态存在的条件)

(1) 必须要有父子类关系,没有继承,就没有多态!! (有继承关系)

(2) 父类的引用指向子类对象 father F1 = new son();

(3)子类重写父类的方法

根据代码理解一下

public class Demo01 {
    public static void main(String[] args) {
        //new Student是创建的对象
        //Student s就是学生类型的引用
        Student s = new Student();
        System.out.println(s.name);
        s.speak();
        s.hi();
        //人类对象
        Person p = new Person();
        System.out.println(p.name);
        p.speak();
        p.hi();
        //建立多态关系  父类的引用指向子类对象
        Person p2 = new Student();
        System.out.println(p2.ID);
        //真正的对象是学生
        //学生 通过不同的属性区分
        //在人类的情况下 去访问姓名
        p2.speak();
        p2.hi();
//        p2.study();
    }
}
class Person{
    String ID = "人";
    public void speak(){
        System.out.println("我是人");
    }
    public void hi(){
        System.out.println("大家早上好");
    }
}
class Student extends Person{
    String ID = "学生";
    @Override
    public void speak() {
        System.out.println("我是学生");
    }
    public void study(){
        System.out.println("学习");
    }
}

1.多态访问成员方法的特点

1、编译看左,运行看右

2、编译的时候看等号左边的类型中是否定义了该方法,如果有就编译通过。

3、运行的时候看等号右边的类型,对象所属的类中,如果对这个方法提供了新的实现,最终运行的就是子类对这个方法的实现,如果没有就使用父类中提供的实现
根据代码理解一下

public class Demo02 {
    public static void main(String[] args) {
        Zi z = new Zi();
        //子类中既可以访问父类的也可以访问子类的
        //建立多态关系
        Fu f = new Zi();//父类的引用指向子类对象
        //父类的引用 Fu  f      super表示本类当前对象父类的引用
        //此时当前对象时候 子类对象    引用是父类引用
        //子类的引用 Zi z       this表示本类当前对象的引用
        System.out.println(f.name);//如果访问属性,编译看左,运行看左
//        f.age父类中没有
        //行为   编译看左,运行看右
        //编译看左,只能看父类中有没有定义该方法,不能出现使用父类中没有的方法
        f.test();//父类有test。子类也有test子类的test和父类的关系是重写
        //重写的本质是使用父类的方法定义修改为子类的实现,本质上方法还是属于父类的

        f.test2();//编译看左,只要左边有就编译通过,运行看右,没有但是有继承关系
        //此时继承父类的test2方法,所以能够运行
//        f.test3();//编译看左,父类中此时没有该方法定义
    }
}
class Fu{
    String name = "张三";
    public void test(){
        System.out.println("我是父类的test方法");
    }
    public void test2(){
        System.out.println("我是父类的test2方法");
    }
}
class Zi extends Fu{
    int age = 33;
    public void test(){
        System.out.println("我是子类的test方法,我更加优秀");
    }
    public void test3(){
        System.out.println("我是子类的test3方法");
    }
}

2.多态的向上向下转型

1、向上转型:多态的体现,就是向上转型

(1) 原来是 子类的引用指向子类对象

(2) 向上转型就是 父类的引用指向子类对象

(3) 本质:缩小了对象本身的访问范围,减少了访问权限,只能访问父类中的内容

2、向下转型

(1) 前置条件:一定是一个经过向上转型的对象

(2) 子类类型 新的引用名称 = (子类类型)父类类型的引用;

(3) 本质:恢复子类本身的访问范围

3、注意:不可以把父类对象进行向下转型,必须是父类的引用指向子类对象这种类型,才可以向下转型
根据代码理解一下

public class Demo03 {
    public static void main(String[] args) {
        //金龙是超人,隐藏身份和我们相处
        //向上转型,  父类的引用指向子类对象
        Man m = new SuperMan();//创建了一个多态关系,缩小了对象本身的访问范围,访问的都是父类的内容
        System.out.println(m.name);
        m.talkMoney();//男人都会谈生意,只是小虫虫牛了一点,都是几个亿的能接受
//        m.fly();男人不会飞,要隐藏身份,否则真的解剖了

        //向下转型 恢复子类本身的访问范围,既能访问子类的,也能访问父类的
        //某一天,金龙遭遇了暴打,不装了,摊牌了,生气了,告诉你我是个超人
        SuperMan s = (SuperMan)m;
        System.out.println(s.name);
        s.fly();
        s.talkMoney();
    }
}
//金龙 是一个隐藏了身份和大家相处的超人,需要挨打才能解锁变身
class Man{
    String name = "小虫虫";
    public void talkMoney(){
        System.out.println("谈一谈不同的小生意几十块的");
    }
}
class SuperMan extends Man{
    String name = "金虫超人";

    @Override
    public void talkMoney() {
        System.out.println("谈的都是几个亿的大生意");
    }
    public void fly(){
        System.out.println("到处飞着倒垃圾");
    }
}

3.多态的好处

上面说了多态的好处是:提高了代码的扩展性
根据上面的代码扩展理解

public class Demo03 {
    public static void main(String[] args) {
        //金龙是超人,隐藏身份和我们相处
        //向上转型,  父类的引用指向子类对象
        Man m = new SuperMan();//创建了一个多态关系,缩小了对象本身的访问范围,访问的都是父类的内容
        System.out.println(m.name);
        m.talkMoney();//男人都会谈生意,只是小虫虫牛了一点,都是几个亿的能接受
//        m.fly();男人不会飞,要隐藏身份,否则真的解剖了
        //要转换到SuperMan类型
        //先判断这个对象能否转换到这个类型
        //多态对象  m
        //要转换的类型 SuperMan
//        m instanceof SuperMan 对象m是SuperMan类型返回true不是返回false
 /*       if(m instanceof SuperMan){
            //向下转型 恢复子类本身的访问范围,既能访问子类的,也能访问父类的
            //某一天,金龙遭遇了暴打,不装了,摊牌了,生气了,告诉你我是个超人
            SuperMan s = (SuperMan)m;
            System.out.println(s.name);
            s.fly();
            s.talkMoney();
        }*/
        Man m2 = new OldMan();
        if(m2 instanceof SuperMan) {//验证m2本质上是否是SuperMan类型
            SuperMan s = (SuperMan) m2;
            s.fly();
        }
        OldMan o = new OldMan();
        test(o);
        SuperMan s = new SuperMan();
        test(s);
    }
    public static void test(Man m){//Man m = new SuperMan();
        //原来只能传递Man 类型的引用 类型的对象 
        //现在可以传递不同的子类对象
        m.say();
    }
}
//金龙 是一个隐藏了身份和大家相处的超人,需要挨打才能解锁变身
class Man{
    String name = "小虫虫";
    public void talkMoney(){
        System.out.println("谈一谈不同的小生意几十块的");
    }
    public void say(){
        System.out.println("说男人");
    }
}
class SuperMan extends Man{
    String name = "金虫超人";
    @Override
    public void talkMoney() {
        System.out.println("谈的都是几个亿的大生意");
    }
    public void fly(){
        System.out.println("到处飞着倒垃圾");
    }
    public void say(){
        System.out.println("说超人");
    }
}
class OldMan extends Man{
    @Override
    public void say() {
        System.out.println("说老男人");
    }
}
-------------------------------------------------------------------------
package com.demo;
public class Demo04 {
    public static void main(String[] args) {
        Test t = new Test();
        Fu f = new Zi1();
        Fu f2 = new Zi2();
        Fu f3 = new Zi3();
        t.test(f);
        t.test(f2);
        t.test(f3);
    }
}
class Test{
    //能不能传递子类对象
    public void test(Fu f){ //
        f.say();
    }
}
class Fu{
    public void say(){
        System.out.println("说");
    }
}
class Zi1 extends Fu{
    public void say(){
        System.out.println("说天");
    }
}
class Zi2 extends Fu{
    public void say(){
        System.out.println("说地");
    }
}
class Zi3 extends Fu{
    public void say(){
        System.out.println("说虫虫");
    }
}

练习

1、现在有男孩类(Boy),行为会喂养宠物feedPet

2、有宠物类Pet,有狗类Dog、猫类Cat、猴类Monkey,行为都会吃,只不过吃的内容不太一样

3、运用恰当的方式,使下方代码可以运行

(1) Boy b = new Boy();

(2) Dog d = new Dog();

(3) b.feedPet(d)

(4) Cat c = new Cat();

(5) b.feedPet©;

(6) Monkey m = new Monkey();

(7) b.feedPet(m);

public class Demo05 {
    public static void main(String[] args) {
        Boy b = new Boy();
        Dog d = new Dog();
        b.feedPet(d);
        Cat c = new Cat();
        b.feedPet(c);
        Monkey m = new Monkey();
        b.feedPet(m);
    }
}
//1、现在有男孩类(Boy),行为会喂养宠物feedPet
//        2、有宠物类Pet,有狗类Dog、猫类Cat、猴类Monkey,行为都会吃,只不过吃的内容不太一样
//        3、运用恰当的方式,使下方代码可以运行
//        (1)Boy b = new Boy();
//        (2)Dog d = new Dog();
//        (3)b.feedPet(d)
//        (4)Cat c = new Cat();
//        (5)b.feedPet(c);
//        (6)Monkey m = new Monkey();
//        (7)b.feedPet(m);
class Boy{
    public void feedPet(Pet p){//形式参数是父类类型的,实际参数可以传递不同的子类对象
        p.eat();
	
    }
//    public void feedDog(Dog d){
//        d.eat();
//    }
}
class Pet{
    public void eat(){
        System.out.println("吃");
    }
}
class Dog extends Pet{
    @Override
    public void eat() {
        System.out.println("吃狗粮");
    }
}
class Cat extends Pet{
    @Override
    public void eat() {
        System.out.println("吃猫粮");
    }
}
class Monkey extends Pet{
    @Override
    public void eat() {
        System.out.println("吃香蕉");
    }
}

二、关键字instanceof

关键字instanceof来判断向下转型的类型是不是这个对象的类型

格式: 多态对象 instanceof 指定的数据类型 返回值是布尔类型的数据

System.out.println(X instanceof Y); 看XY有没有父子关系

1、向下转型,可能出现转为其他的子类类型,编译不会报错,但是运行时会发生类型转换异常

2、解决方案:在转换之前判断一下要转换的类型是不是多态对象之前的类型

import oop.Duotai.A;
import oop.Duotai.B;

public class Application {
    public static void main(String[] args) {
       // System.out.println(X instanceof Y);  看XY有没有父子关系
        //Object>String
        //Object>B>A
        Object object = new A();
        System.out.println(object instanceof A);//true
        System.out.println(object instanceof B);//true
        System.out.println(object instanceof Object);//true
        System.out.println(object instanceof String);//false
        System.out.println("=====================================");
        B b = new A();
        System.out.println(b instanceof A);//true
        System.out.println(b instanceof B);//true
        System.out.println(b instanceof Object);//true
       // System.out.println(b instanceof String);//编译报错!
        System.out.println("=====================================");
        A a = new A();
        System.out.println(a instanceof A);//true
        System.out.println(a instanceof B);//true
        System.out.println(a instanceof Object);//true
        //System.out.println(b instanceof String);//编译报错
    }
}

好啦最后对多态做一个总结:

1、多态关系的构建语法格式是? 父类的引用指向子类对象
2、多态的前提是? 先构建继承关系,没有继承就没有多态
3、多态访问成员方法的特点   编译看左运行看右
4、多态访问成员变量的特点   编译看左运行看左
5、多态的好处是什么	提高了代码的扩展性
6、什么是向上转型,什么是向下转型
多态的体现就是向上转型,父类的引用指向子类对象
恢复子类本身的访问范围,类型强制转换  子类类型  新对象名 = (子类类型)对象名;
7、抽象方法如何定义
	public abstract 返回值类型 方法名称(参数列表);
8、抽象类和抽象方法有什么关系
	抽象类中不一定有抽象方法,
         抽象方法必须在抽象类中
9、抽象类和普通类有什么区别
	多了抽象方法
	不能创建对象
	子类必须重写抽象类中的抽象方法
10、抽象类可以创建对象吗  
       不能
11、抽象类的子类一定能够创建对象吗
	不一定
	子类如果重写了所有的抽象方法  可以创建
	如果没有全部重写父类的抽象方法,还是一个抽象类
12. 定义一个Man类, 属性有姓名, 方法: 开车
   有一个公交车类Bus, 属性有:速度 方法有: 跑
   有一个卡车类Truck, 属性有:速度 方法有: 跑
   有一个跑车类SportsCar:  属性有:速度 方法有: 跑
   要求:编写一个测试类: 创建一个Man对象,可以驾驭上面任意一款车型
        Man m = new Man();
public class Demo01 {
    public static void main(String[] args){
        Man m = new Man();
        m.name = "陈毅";
        m.driverCar(new Bus(100));
        m.driverCar(new SportsCar(30));
        m.driverCar(new Truck(120));
    }
}
//12. 定义一个Man类, 属性有姓名, 方法: 开车
//        有一个公交车类Bus, 属性有:速度 方法有: 跑
//        有一个卡车类Truck, 属性有:速度 方法有: 跑
//        有一个跑车类SportsCar:  属性有:速度 方法有: 跑
//        要求:编写一个测试类: 创建一个Man对象,可以驾驭上面任意一款车型
class Man{
    String name;
    public void driverCar(Car c){
        c.run();
    }
}
abstract class Car{
   private int speed;
    public int getSpeed() {
        return speed;
    }
    public void setSpeed(int speed) {
        this.speed = speed;
    }
    public Car(int speed) {
        this.speed = speed;
    }
    public abstract void run();
}
class Bus extends Car{
    public Bus(int speed) {
        super(speed);
    }
    @Override
    public void run() {
        System.out.println("摇摇晃晃" +getSpeed());
    }
}
class Truck extends Car{
    public Truck(int speed) {
        super(speed);
    }
    @Override
    public void run() {
        System.out.println("横推一切" + getSpeed());
    }
}
class SportsCar extends Car{
    public SportsCar(int speed) {
        super(speed);
    }
    @Override
    public void run() {
        System.out.println("风驰电掣" + getSpeed());
    }
}
13.编一个Perosn, 包含属性姓名, 年龄, 行为:说话, 吃饭, 睡觉
  Person有一个子类Student, 比Person多一个属性分数, 重写说话, 吃饭,睡觉的方法
  Person有一个子类Teacher, 比Person多一个属性薪资, 重写说话, 吃饭,睡觉的方法
	人类下每个子类对行为的实现均不相同
 编写测试类Student和Teacher对象,并调用每个对象的方法.
public class Demo02 {
}
//13.编一个Perosn, 包含属性姓名, 年龄, 行为:说话, 吃饭, 睡觉
//        Person有一个子类Student, 比Person多一个属性分数, 重写说话, 吃饭,睡觉的方法
//        Person有一个子类Teacher, 比Person多一个属性薪资, 重写说话, 吃饭,睡觉的方法
//        人类下每个子类对行为的实现均不相同
//        编写测试类Student和Teacher对象,并调用每个对象的方法.
//
abstract class Person{
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public abstract void say();
    public abstract void eat();
    public abstract void sleep();
}
class Student extends Person{
    private int score;
    public Student() {
    }
    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }
    public Student(String name, int age, int score) {
        super(name, age);
        this.score = score;
    }
    @Override
    public void say() {
        System.out.println("谈天说地就是不聊学习");
    }
    @Override
    public void eat() {
        System.out.println("干饭人,干饭魂。吃最重要");
    }
    @Override
    public void sleep() {
        System.out.println("教室里睡的最香");
    }
}
class Teacher extends Person{
    private int salary;
    public Teacher() {
    }
    public Teacher(String name, int age, int salary) {
        super(name, age);
        this.salary = salary;
    }
    public int getSalary() {
        return salary;
    }
    public void setSalary(int salary) {
        this.salary = salary;
    }
    @Override
    public void say() {
        System.out.println("那叫一个说啊,一直说,一说六个小时");
    }
    @Override
    public void eat() {
        System.out.println("吃,也是最重要的,同是干饭人");
    }
    @Override
    public void sleep() {
        System.out.println("上班天天打瞌睡 不能睡");
    }
}
14. 有如下代码,请问哪些是不正确的?(    )
       class ClassA{}
        class ClassB extends ClassA{}
        class ClassC extends ClassA{}
        //BC是A的子类
        ClassA p0 = new ClassA();//A类引用指向A类对象
        ClassB p1 = new ClassB();//B类引用指向B类对象
        ClassC p2 = new ClassC();//C类引用指向C类对象
        ClassA p3 = new ClassB();//A类的引用指向B类的对象
        ClassA p4 = new ClassC();//A类的引用指向C类的对象
//        A类的引用 = B类的对象
        p0 = p1;  //合理
        //sp1 = p2;//b类的引用 = C类的对象  BC都是子类 没有直接关系  所以不可以
        //必须是父类的引用指向子类对象才可以
        p1 = (ClassB)p3;//B类的引用  向下转型 将P3转为B类的引用B类的对象
        p2 = (ClassC)p4;//同上
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值