一、多态

1、多态的意义

​ 一个类型的引用在指向不同的对象时会有不同的实现,多态建立在继承的基础之上。相同的消息可以发送给多个不同类别的对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。
多态

/**
 * @document: 多态的概念
 * 建立在继承的基础之上
 * @Author:SmallG
 * @CreateTime:2023/7/28+9:09
 */

public class Demo01 {
    public static void main(String[] args) {
        Person person1 = new Student(); //new 的是Person的子类
        Person person2 = new Teacher(); //同一种数据类型,在不同时期具有不同的状态

        person1.speak(); //我是一名学生
        person2.speak(); //我是一名老师
    }
}

//父类
class Person {
    public void speak() {
        System.out.println("说话");
    }
}

//子类
class Student extends Person {
    public void speak() {
        System.out.println("我是一名学生");
    }
}

//子类
class Teacher extends Person {
    public void speak() {
        System.out.println("我是一名老师");
    }
}

个体多态

  • 父类型定义的变量引用的子类型个体是多种多样的,如Person变量可以引用Student、Teacher等
  • 个体多态是向上造型实现的

行为多态

  • 父类型变量引用子类型实例后,执行方法的时候可以得到很多不一样的结果
  • 行为多态利用的是方法的重写

为什么需要多态?

  • 应用多态可以使用同一种类型的变量代表多种类型的对象可以对多种类型的个体进行统一管理,可以对不同行为的功能进行统一处理。
  • 多态的前提是继承,在有继承关系的时候才能使用多态。
  • 具体在实际开发中可以先不考虑多态,先按照具体类型编程,当发现各种子类型都需要相同处理时候,再重构代码提升到父类型统一进行处理。
/**
 * @document: 多态
 * @Author:SmallG
 * @CreateTime:2023/7/28+9:37
 */

public class Demo02 {
    public static void main(String[] args) {
        Person01 p1 = new Student01("Tom", 21);
        Person01 p2 = new Teacher01("Jerry", 30);
        Person01 p3 = new Work01("Spike", 23);

        Person01[] ps = {p1, p2, p3};
        checkPerson(ps);
    }

    public static void checkPerson(Person01[] ps) {
        for (int i = 0; i < ps.length; i++) {
            Person01 p = ps[i];
            p.schedule();
        }
    }
}

class Student01 extends Person01 {

    public Student01(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void schedule() {
        System.out.println("我是" + name + ",我在学习");
    }

    public void study() {
        System.out.println("学生学习");
    }
}

class Teacher01 extends Person01 {

    public Teacher01(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void teach() {
        System.out.println("老师讲课");
    }

    public void schedule() {
        System.out.println("我是" + name + ",我在讲课");
    }
}

class Work01 extends Person01 {

    public Work01(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void schedule() {
        System.out.println("我是" + name + ",我在开车");
    }

    public void work() {
        System.out.println("工人工作");
    }
}

class Person01 {
    String name;
    int age;

    public void schedule() {
        System.out.println("计划");
    }
}

多态的缺点

多态只能执行共性,子类调用特性会报错,多态是父类的引用,不能访问子类声明的成员。如果想要避免这个问题只能向下造型

二、接口 interface

*接口和抽象类

  • 抽象类和接口都属于抽象的概念,可以从同类别和跨类别的角度来考虑,同一个类别的公共行文和属性可以抽取到抽象类中,不同种类的公共行为可以抽取到接口中。
  • 抽象类和接口的设计原则:将所有子类共有的方法抽象化到父类中,可以使用抽象类,将部分子类中的公共方法抽象化到接口中,适用于不同类别但具有相似行为的对象。

1、接口的语法

接口中不能有构造方法,因为接口中属性默认为静态常量。

/**
 * @document: 接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+10:39
 */

//定义一个飞行接口 使用interface声明
//接口中的方法默认的都是public abstract
public interface Flyable {
    //接口中可以有属性默认都是public static final静态常量
    int id = 1;

    //起飞
    void tackOff();

    //飞行
    void fly();

    //降落
    void land();
}

/**
 * @document: 实现类
 * @Author:SmallG
 * @CreateTime:2023/7/28+10:46
 */

//需要实现接口的所有方法
public class Eagle implements Flyable {

    @Override
    public void tackOff() {
        System.out.println("起飞喽");
    }

    @Override
    public void fly() {
        System.out.println("在盘旋");
    }

    @Override
    public void land() {
        System.out.println("降落喽");
    }
}

/**
 * @document: 测试接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+10:50
 */

public class Demo03 {
    public static void main(String[] args) {
        //接口不能实例化
        Flyable eagle = new Eagle(); //接口的引用类型指向了实现类的对象(也是多态)

        eagle.tackOff(); //起飞喽
        eagle.fly();     //在盘旋
        eagle.land();    //降落喽
    }
}

2、接口实例

/**
 * @document: 接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+10:39
 */

//定义一个飞行接口 使用interface声明
//接口中的方法默认的都是public abstract
public interface Flyable {
    //接口中可以有属性默认都是public static final静态常量
    int id = 1;

    //起飞
    void tackOff();

    //飞行
    void fly();

    //降落
    void land();
}

/**
 * @document: 接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+11:24
 */

public interface Runner {
    void run();
}


/**
 * @document:
 * @Author:SmallG
 * @CreateTime:2023/7/28+11:17
 */

public class Bird {
    public void eat(){
        System.out.println("鸟子在吃饭");
    }
    public void sleep(){
        System.out.println("鸟在呼呼睡觉");
    }
}


/**
 * @document: 实现类
 * @Author:SmallG
 * @CreateTime:2023/7/28+10:46
 */

//需要实现接口的所有方法
//继承优先于接口,先继承类后实现接口
//一个类可以实现多个接口,接口之间用逗号隔开
public class Eagle extends Bird implements Flyable, Runner {

    @Override
    public void tackOff() {
        System.out.println("起飞喽");
    }

    @Override
    public void fly() {
        System.out.println("在盘旋");
    }

    @Override
    public void land() {
        System.out.println("降落喽");
    }

    @Override
    public void run() {
        System.out.println("老楞在跑");
    }
}



/**
 * @document: 测试接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+10:50
 */

public class Demo03 {
    public static void main(String[] args) {
        //接口不能实例化
        Flyable eagle = new Eagle(); //接口的引用类型指向了实现类的对象(也是多态)

        eagle.tackOff(); //起飞喽
        eagle.fly();     //在盘旋
        eagle.land();    //降落喽

        Eagle e = new Eagle();
        e.eat();
        e.sleep();

    }
}

3、接口与多继承

java接口多继承弥补了java单继承的缺陷

/**
 * @document: 图形接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+14:03
 */
public interface Shape {
    void draw();
}


/**
 * @document: 圆
 * @Author:SmallG
 * @CreateTime:2023/7/28+14:05
 */
public interface Circle extends Shape {
    double getRadius(); //获取半径
}


/**
 * @document: 上色接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+14:06
 */
public interface Colorable extends Shape {
    void setColor(String color);
}


/**
 * @document: 彩色的圆,接口的多继承
 * @Author:SmallG
 * @CreateTime:2023/7/28+14:08
 */
public interface ColoredCircle extends Circle,Colorable{
    void rotate();
}


/**
 * @document: 测试类
 * @Author:SmallG
 * @CreateTime:2023/7/28+14:10
 */
public class TestColor implements ColoredCircle{

    @Override
    public double getRadius() {
        return 0;
    }

    @Override
    public void setColor(String color) {

    }

    @Override
    public void rotate() {

    }

    @Override
    public void draw() {

    }
}

4、**面试题:接口和抽象类的区别

  • 抽象类可以存在普通方法,而接口只能存在public abstract方法
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的(静态常量)
  • 抽象类只能继承一个,接口可以实现多个。

​ 接口的设计目的,是对类行为的约束,也就是提供一种机制,可以强制要求不同的类具有相同的行为,它只约束了行为的有无,但不对如何实现行为进行限制。

​ 抽象类设计的目的,是代码复用。当不同的类具有某写相同的行为,且其中一部分行为的实现方式一致时,可以让这些类都派生于一个抽象类。

​ 抽象类是对类本质的抽象,抽象类包含并实现子类的通用特性,将子类存在差异化的特性进行抽象,交由子类去实现。

​ 接口是对行为的抽象,接口的核心是定义行为,即实现类可以做什么,至于实现类的主体是谁,是如果实现的,接口并不关心。

​ 使用场景:当你关注一个事物的本质的时候,用抽象类。当你关注一个操作的时候,用接口。

​ 抽象类的功能要远超过接口,但是抽象类的代价高,因为java中一个类只能继承一个抽象类,而可以实现多个接口。

5、**面试题:Java如何实现多继承?

  • 在Java中,类是单继承的,即一个类只能继承自一个父类
  • 通过接口的使用,Java可以实现多继承的效果
  • 类可以实现多个接口中的方法,从而获得多个接口定义的行为和功能
  • 这种机制提供了灵活性和可扩展性,使得Java在面对多继承需求时能够更好地满足设计和开发的需求

三、造型

1、向上造型

将一个子类对象赋值给父类引用变量的过程,视为其父类类型,实现多态性的体现。

Student01 student = new Student01("Jerry",21);
Person01 p = student; //向上造型

向上造型的特点

  • 子类对象可以赋值给父类引用变量,但是父类对象不能赋值给子类引用变量
  • 向上造型是自动进行的,不需要额外的转换操作
  • 通过向上造型,可以调用父类中声明的方法,但无法调用子类中特有的方法

向上造型的优势

  • 实现多态性:通过向上造型,可以将不同子类的对象视为父类类型,统一对待,实现多态性的效果
  • 灵活性和扩展性:通过向上造型,可以在不改变父类引用的情况下,使用不同的子类对象,使程序具备更大的灵活性和可扩展性。

2、向下造型(强制转换)

  • 调用子类中特有的方法,就需要向下造型
  • 向下造型是指将一个已经向上造型的对象重新转回其原始的子类类型,允许我们在需要的时候访问和调用子类特有的方法和属性
  • 在Java中向下造型用强制类型转换操作符来实现
//访问学生对象的特有方法
Person01 person01 = new Student01("Tom",21);
Student01 student01 = (Student01) person01;
student01.study();

向下造型的风险

如果对一个不兼容的对象进行向下转型,将会抛出ClassCastException类型转换异常

//ClassCastException类型转换异常
Person01 person02 = new Teacher01("Jerry",21);
Student01 student02 = (Student01) person02;
student02.study();

解决办法:

Person01 person02 = new Teacher01("Jerry",21);
if (person02 instanceof Student01){
     Student01 student02 = (Student01) person02;
     student02.study();
}else{
     System.out.println("数据类型不匹配");
}


//java 17 新特性 如果类型匹配 直接完成类型转换 转换后的对象名是s
if (person01 instanceof Student01 s){
            s.study();
        }

四、内部类

1、内部类

  • 局部内部类:声明在外部类的局部位置上,有类名,在方法里声明的内部类
  • 成员内部类:声明在外部类的成员位置上,有类名,无static修饰
  • 静态内部类:声明在外部类的成员位置上,有类名,有static修饰
  • 匿名内部类:在一行代码上继承父类并且创建出子类实例的语法,无类名
/**
 * @document: 局部内部类
 * @Author:SmallG
 * @CreateTime:2023/7/28+15:15
 */
/*
* 局部内部类和局部变量一样,不能用static修饰
* 局部内部类不能用public,private,protected修饰
* */

public class Person {
    public Person getMan() {
        //局部内部类  用的不多 简单了解即可
        class Man extends Person {
            String name = "Tom";
            int age = 20;
        }
        Man man = new Man();
        System.out.println(man.name + " " + man.age);
        return man;
    }

    public static void main(String[] args) {
        Person person = new Person();
        Person man = person.getMan();

    }
}

成员内部类可以访问外部类的所有成员,包括私有成员和静态成员。

/**
 * @document: 成员内部类
 * @Author:SmallG
 * @CreateTime:2023/7/28+15:26
 */

public class Circle {
    private double radius = 0;
    public static int count = 1;

    public Circle(double radius) {
        this.radius = radius;
    }

    private Draw getDrawInstance() {
        return new Draw();
    }

    //成员内部类 可以使用访问控制符修饰
    class Draw {

        public void drawShape() {
            System.out.println("画图形");
            //成员内部类可以访问外部类的所有成员
            System.out.println(radius); //外部类的私有成员
            System.out.println(count);  //外部类的静态成员
        }
    }

    public static void main(String[] args) {
        //创建内部类的对象
        //第一种方式:先创建外部类对象
        Circle circle = new Circle(5);
        Circle.Draw draw = circle.new Draw();
        draw.drawShape();
        System.out.println("--------------");

        //第二种方式:
        Circle.Draw draw1 = circle.getDrawInstance();
        draw1.drawShape();
    }
}

静态内部类

/**
 * @document: 静态内部类
 * @Author:SmallG
 * @CreateTime:2023/7/28+16:08
 */

public class OuterClass {

    private static int outerStaticFiled = 10;
    private int outerNoStaticFiled = 20;

    //静态内部类和静态方法一样不能访问外部类的非静态成员
    public static class InnerClass {
        private static int innerStaticFiled = 5;
        private int innerNoStaticFiled = 15;

        public void print() {
            //静态内部类访问外部类的静态成员
            System.out.println(outerStaticFiled);
            //报错:不能访问非静态成员
            //System.out.println(outerNoStaticFiled);
            //静态内部类可以访问自己内部类的成员变量
            System.out.println(innerNoStaticFiled);  //15
            System.out.println(innerStaticFiled);    //5
        }
    }

    public static void main(String[] args) {
        //创建内部类的对象,可以通过类名直接访问,可以通过外部类直接创建
        OuterClass.InnerClass innerClass = new  OuterClass.InnerClass();
        innerClass.print();
    }
}

2、*匿名内部类

什么是匿名内部类?

如果在一段程序中需要创建一个类的对象(通常这个类需要实现某个接口或者继承某个类),而且对象创建后,这个类的价值也就不存在了,这个类可以不必命名,称之为匿名内部类

/**
 * @document: 匿名内部类
 * @Author:SmallG
 * @CreateTime:2023/7/28+16:25
 */

public class Demo01 {
    public static void main(String[] args) {
        //匿名内部类 接口不能实例化,创建了一个类实现了接口的所有方法
        Animal animal = new Animal() {
            @Override
            public void eat() {
                System.out.println("动物吃");
            }

            @Override
            public void call() {
                System.out.println("动物叫");
            }
        };
        animal.call();
        animal.eat();
    }
}

使用匿名内部类要注意!

  • 如果只是简洁地继承父类,并且只需要创建一个子类对象,就采用匿名内部类
  • 如果子类需要反复使用创建一组子类对象就采用普通的子类
  • 匿名内部类一定是子类,一定需要有父类型时候才能使用
  • 匿名内部类的最大优点就是语法简洁,在一行上继承父类并且创建子类对象

水果店项目

创建一个水果店---FruitShop
水果店定义一个数组保存5种水果
/**
 * @document: 水果店
 * @Author:SmallG
 * @CreateTime:2023/7/28+17:29
 */

public class FruitShop {
    Fruit[] fruits = new Fruit[5];

    public void print() {
        for (int i = 0; i < fruits.length; i++) {
            Fruit fruit = fruits[i];
            fruit.getName();
            fruit.eat();
        }
    }
}
定义一个接口Fruit,提供两个方法getName()打印水果名字,eat()打印吃XX水果
/**
 * @document: Fruit接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+17:21
 */

public interface Fruit {
    void getName();
    void eat();
}
提供5个实现类

Watermelon

/**
 * @document: 西瓜类,实现接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+17:23
 */

public class Watermelon implements Fruit {
    private String name;

    public Watermelon(String name) {
        this.name = name;
    }

    @Override
    public void getName() {
        System.out.println("西瓜名:" + name);
    }

    @Override
    public void eat() {
        System.out.println("吃:" + name);
    }
}

Blueberry

/**
 * @document: 蓝莓
 * @Author:SmallG
 * @CreateTime:2023/7/28+17:25
 */

public class Blueberry implements Fruit{
    private String name;

    public Blueberry(String name) {
        this.name = name;
    }

    @Override
    public void getName() {
        System.out.println("蓝莓名:" + name);
    }

    @Override
    public void eat() {
        System.out.println("吃:" + name);
    }
}

Grape

/**
 * @document: 葡萄类
 * @Author:SmallG
 * @CreateTime:2023/7/28+17:26
 */

public class Grape implements Fruit{
    private String name;

    public Grape(String name) {
        this.name = name;
    }

    @Override
    public void getName() {
        System.out.println("葡萄名:" + name);
    }

    @Override
    public void eat() {
        System.out.println("吃:" + name);
    }
}

KiwFruit

/**
 * @document:  猕猴桃
 * @Author:SmallG
 * @CreateTime:2023/7/28+17:27
 */

public class KiwFruit implements Fruit{
    private String name;

    public KiwFruit(String name) {
        this.name = name;
    }

    @Override
    public void getName() {
        System.out.println("猕猴桃名:" + name);
    }

    @Override
    public void eat() {
        System.out.println("吃:" + name);
    }
}

Apple

/**
 * @document: 苹果
 * @Author:SmallG
 * @CreateTime:2023/7/28+17:27
 */

public class Apple implements Fruit{
    private String name;

    public Apple(String name) {
        this.name = name;
    }

    @Override
    public void getName() {
        System.out.println("苹果名:"+name);
    }

    @Override
    public void eat() {
        System.out.println("吃:"+name);
    }
}
定义Demo类提供main方法遍历所有水果
/**
 * @document: 测试类
 * @Author:SmallG
 * @CreateTime:2023/7/28+17:32
 */

public class Demo {
    public static void main(String[] args) {
        Fruit fruit1 = new Watermelon("麒麟西瓜");
        Fruit fruit2 = new Blueberry("康维尔");
        Fruit fruit3 = new Grape("阳光玫瑰");
        Fruit fruit4 = new Apple("青岛富士");
        Fruit fruit5 = new KiwFruit("红心猕猴桃");

        FruitShop fruitShop = new FruitShop();
        fruitShop.fruits[0] = fruit1;
        fruitShop.fruits[1] = fruit2;
        fruitShop.fruits[2] = fruit3;
        fruitShop.fruits[3] = fruit4;
        fruitShop.fruits[4] = fruit5;

        fruitShop.print();
    }
}

五、练习

教练和运动员案例

现有教练和运动员案例需求如下:

  • 教练:乒乓球教练、篮球教练
  • 运动员:乒乓球运动员、篮球运动员
  • 教练和运动员共同的属性:姓名(name)、年龄(age)
  • 教练和运动员共同的行为:睡觉(sleep)、吃饭(eat)
  • 为了出国交流,跟乒乓球相关的人员都需要会说英语
  • 关于吃:乒乓球相关人员 “吃大白菜,喝小米粥”,篮球相关人员“吃牛肉,喝牛奶”
  • 教练特有的行为是教学(teach),不同类型教练教学的内容不同:乒乓球教练的教学内容为“教如何发球和接球”;篮球教练的教学内容为“教如何运球和投篮”
  • 运动员特有的行为是学习(study),不同类型的运动员学习的内容不同:乒乓球运动员的学习内容为“学习如何发球和接球”;篮球运动员的学习内容为“学习如何运球和投篮”

请根据需求完成抽象类、接口和类的设计,并编写测试类:

  • 在测试类的main方法中创建两个数组,分别管理运动员和教练员
  • 创建一个乒乓球运动员对象和一个篮球运动员对象,存入运动员数组中
  • 创建一个乒乓球教练对象和一个篮球教练对象,存入教练数组中
  • 遍历运动员数组,在控制台输出运动员的信息,并调用运动员的吃饭、睡觉、学习方法,如果是乒乓球运动员,还需要调用说英语的方法
  • 遍历教练数组,在控制台输出教练的信息,并调用教练的吃饭、睡觉、教学方法,如果是乒乓球教练,还需要调用说英语的方法

运行效果如下所示:

/**
 * @document: 抽象类Person用于存储运动员和教练共有的行为和属性
 * @Author:SmallG
 * @CreateTime:2023/7/28+8:21
 */

public abstract class Person {
    String name; //姓名
    int age; //年龄

    //定义父类的抽象方法,方便子类继承
    //睡觉
    public abstract void sleep();

    //吃饭eat
    public abstract void eat();
}

/**
 * @document: 教练的抽象类继承Person类 写教练的特有属性和方法
 * @Author:SmallG
 * @CreateTime:2023/7/28+8:25
 */

public abstract class Coach extends Person {

    //教练特有的教学行为
    public abstract void teach();

}

/**
 * @document: 说英语的接口 供运动员和教练调用
 * @Author:SmallG
 * @CreateTime:2023/7/28+8:30
 */

public interface SpeakE {
    //说英语的行为
    void speakEnglish();
}

/**
 * @document: 运动员抽象类 继承Person抽象类 用于写运动员的特有行为
 * @Author:SmallG
 * @CreateTime:2023/7/28+8:28
 */

public abstract class Sportsman extends Person {
    //运动员特有的学习行为
    public abstract void study();

}


/**
 * @document: 乒乓球教练类 继承教练抽象类  实现说英语接口
 * @Author:SmallG
 * @CreateTime:2023/7/28+8:34
 */

public class PingPongCoach extends Coach implements SpeakE {

    public PingPongCoach() {
    }

    public PingPongCoach(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void teach() {
        System.out.println(name+"是乒乓球教练,教如何发球和接球");
    }

    @Override
    public void sleep() {
        System.out.println(name + "是乒乓球教练,呼呼睡觉");
    }

    @Override
    public void eat() {
        System.out.println(name + "是乒乓球教练,吃大白菜,喝小米粥");
    }

    @Override
    public void speakEnglish() {
        System.out.println(name + "是乒乓球教练,会说英语");
    }
}


/**
 * @document: 乒乓运动员类
 * @Author:SmallG
 * @CreateTime:2023/7/28+13:37
 */

public class PingPongPlayer extends Sportsman implements SpeakE {

    public PingPongPlayer() {
    }

    public PingPongPlayer(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public void sleep() {
        System.out.println(name+"是乒乓球运动员,在睡觉");
    }

    @Override
    public void eat() {
        System.out.println(name+"是乒乓球运动员在吃大白菜,喝小米粥");
    }

    @Override
    public void speakEnglish() {
        System.out.println(name+"是乒乓球运动员会说英语");
    }

    @Override
    public void study() {
        System.out.println(name + "是乒乓球运动员,学习如何发球和接球”");
    }
}

/**
 * @document: 篮球教练
 * @Author:SmallG
 * @CreateTime:2023/7/28+13:41
 */

public class BasketballCoach extends Coach {

    public BasketballCoach() {
    }

    public BasketballCoach(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void teach() {
        System.out.println(name + "是篮球教练,教如何运球和投篮");
    }

    @Override
    public void sleep() {
        System.out.println(name + "是篮球教练,在睡觉");
    }

    @Override
    public void eat() {
        System.out.println(name+"是篮球教练,在吃牛肉,喝牛奶");
    }
}


/**
 * @document:
 * @Author:SmallG
 * @CreateTime:2023/7/28+13:44
 */

public class BasketballPlayer extends Sportsman {
    public BasketballPlayer() {
    }

    public BasketballPlayer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void sleep() {
        System.out.println(name + "在睡觉");
    }

    @Override
    public void eat() {
        System.out.println(name + "在吃牛肉,喝牛奶");
    }

    @Override
    public void study() {
        System.out.println(name + "学习如何运球和投篮");

    }
}


/**
 * @document: 测试类 在测试类的main方法中创建两个数组,分别管理运动员和教练员
 * @Author:SmallG
 * @CreateTime:2023/7/28+8:18
 */

public class TestMain {
    public static void main(String[] args) {
        //创建篮球运动员对象
        BasketballPlayer basketballPlayer = new BasketballPlayer("Tom", 21);
        //创建乒乓球运动员对象
        PingPongPlayer pingPongPlayer = new PingPongPlayer("Jerry", 20);
        //创建乒乓球教练
        PingPongCoach pingPongCoach = new PingPongCoach("Spike", 30);
        //创建篮球教练
        BasketballCoach basketballCoach = new BasketballCoach("SmallG", 30);
        //创建运动员数组
        Sportsman[] sportsmen = {basketballPlayer, pingPongPlayer};
        //创建教练数组
        Coach[] coaches = {basketballCoach, pingPongCoach};

        //遍历运动员
        for (int i = 0; i < sportsmen.length; i++) {
            Sportsman sportsman1 = sportsmen[i];
            sportsman1.eat();
            sportsman1.sleep();
            sportsman1.study();
            if (sportsman1 instanceof PingPongPlayer) {
                PingPongPlayer p = (PingPongPlayer) sportsman1;
                p.speakEnglish();
            }
        }
        System.out.println("----------------------------------");
        //遍历教练
        for (int i = 0; i < coaches.length; i++) {
            Coach coach1 = coaches[i];
            coach1.eat();
            coach1.sleep();
            coach1.teach();
            if (coach1 instanceof PingPongCoach) {
                PingPongCoach p1 = (PingPongCoach) coach1;
                p1.speakEnglish();
            }
        }
    }
}
最后修改:2023 年 07 月 28 日
如果觉得我的文章对你有用,请随意赞赏