06-接口与多态

接口

  1. 接口就是一种公共的规范标准。接口是一种引用数据结构,最重要内容是其中的抽象方法

  2. 定义格式:

    1
    2
    3
    public interface 接口名称 {
    // 接口内容
    }
  3. 注意:
    1.接口不能有静态代码块和构造方法
    2.一个类可以同时实现多个接口 implements FuAI, FuBI,…
    3.如果实现类所实现的抽象方法在几个接口中重名,那么只需要覆盖重写一次这个方法
    4.如果覆盖重写没有覆盖全部的方法,这个类必须是抽象类
    5.对多个接口中冲突的默认方法(名字一样方法体不同)一定要覆盖重写
    6.一个类直接父类当中的方法和接口中默认方法产生了冲突,优先使用父类中的方法。java中,继承优先于接口实现
    7.接口与接口之间实现多继承。一个接口可以extends多个接口
    8.多个父类接口默认方法重复,子接口必须进行默认方法的覆盖重写【带default关键字】
    9.多个父类接口抽象方法重复,没关系

  4. 换成关键字interface之后,编译生成的字节码文件仍然是:.java –>.class
    如果是Java 7,那么接口中可以包含内容有:
    1.常量
    2.抽象方法
    Java 8,额外包含:
    3.默认方法
    4.静态方法
    Java 9,额外包含:
    5.私有方法

抽象方法

  1. 定义格式:
    public abstract 返回值类型 方法名称(参数列表);
    注意事项:
    public abstract 这两个关键词可以选择性省略(部分或者全部)
    方法三要素可以随意进行选择

  2. 接口使用步骤:
    1.不能直接使用,必须要有一个“实现类”来“实现”该接口
    格式:

    1
    2
    3
    public class 实现类名称 implements 接口名称 {
    //…
    }

    2.接口实现类必须覆盖重写(实现)接口中所有的抽象方法
    (alt+回车)
    实现:去掉abstract关键字,加上方法体大括号
    跟继承相似,如果覆盖重写没有覆盖全部的方法,这个类必须是抽象类。

  3. 创建实现类的对象,进行使用

默认方法

  1. 定义格式:
    1
    2
    3
    public default 返回值类型 方法名称(参数列表) {
    方法体
    }
    public可以省略不写,但是不能改别的
  2. 接口中的默认方法,解决接口升级的问题
  3. 调用默认方法,如果实现类中没有,就会向上找接口
  4. 接口实现类可以直接调用默认方法,也可以覆盖重写
    理解:如果接口升级,新增了方法,那么实现这个接口的“实现类”就会报错。为了不报错而且接口实现类不用改,就能使用新添加的这个方法,我们让这新增的方法带有默认方法体。这样的方法就叫做默认方法。

静态方法

  1. 定义格式:
    1
    2
    3
    public static 返回值类型 方法名称(参数列表) {
    方法体
    }
  2. 使用:
    通过接口名称,直接调用其中的静态方法
  3. 强调:
    不能通过接口实现类的对象来调用接口当中的静态方法
  4. 问题描述:我们需要抽取一个公共方法,来解决两个默认方法(静态方法)之间重复代码问题,但这个共有方法不应该让实现类使用,应该是私有的。
    解决:
    Java9开始,接口中允许定义私有方法。
    1.普通私有方法,解决多个默认方法之间重复代码问题
    1
    2
    3
    private 返回值类型 方法名称(参数列表) {
    方法体
    }
    2.静态私有方法,解决多个静态方法之间重复代码问题
    1
    2
    3
    private static 返回值类型 方法名称(参数列表) {
    方法体
    }

常量

  1. 效果上看,可以当成是接口中的 ‘成员变量’,但必须使用public static final进行修饰。不可改变
  2. 格式:
    public static final 数据类型 常量名称 = 数据值;
  3. 一旦使用final进行修饰,不可改变。
  4. 注意事项:
    1.可以省略全部或部分修饰词,但不写也是照样
    2.接口中的常量必须进行赋值
    3.接口中的常量名称,使用全部大写,下划线分隔

多态性

  1. 代码体现:父类引用指向子类对象

  2. 格式:
    父类名称 对象名 = new 子类名称();
    接口名称 对象名 = new 实现名称();

  3. 多态中成员变量的使用特点:
    1.直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,否则向上找
    思考:左边是父类,所以不能访问子类成员变量
    2.间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,否则向上找
    思考:如果有,子类如果覆盖重写,就用子类,否则用父类。
    编译看左边,运行看左边

  4. 多态中成员方法的使用特点:
    new的是谁,就优先用谁,没有则向上找

    编译看左边,运行看右边
    编译可以把.java源代码翻译成.class源代码。如Obj.methodZi();methodZi红色,看左边,发现是Fu类,里面并没有这个方法,所以报错

  5. 多态的好处:
    无论右边new的时候换成哪个子类对象,等号左边调用方法都不会改变。不关心等号左边new的是谁。

    1
    2
    3
    4
    Employee one = new Teacher();
    one.work(); // 讲课
    Employee two = new Assistant();
    two.work(); // 辅导
  6. 对象的向上转型,其实就是多态写法
    格式:父类名称 对象名 = new 子类名称();
    含义:右侧创建一个子类对象,当作父类看待使用

    向上转型一定是安全的。从小范围转换成为更大范围。(类似于int->double,自动类型转换)
    一旦转型为父类,不能调用子类原本特有的内容

  7. 对象的向下转型,其实是一个【还原】动作
    格式:子类名称 对象名 = (子类名称)父类对象;
    含义:将父类对象【还原】成为本来的子类对象

    1
    2
    Animal animal = new Cat(); // 本来是猫,向上转型为动物
    Cat cat = (Cat)animal; // 向下转型只能还原成为本来的猫

    如果向下转型成不是本来的子类,会报错classcastException

    如何知道一个父类引用的对象,本来是什么子类?
    格式: 对象 instanceof 类名称
    这会得到一个Boolean值,判断前面的对象能不能当作后面类型的实例。例如:
    if(animal instanceof Cat)