跳到主要内容

第13章-接口

概述

  • 在实际的应用过程中,有一些特定的单一行为,是跨多个不同风格的系列类,如克隆、系列化、比较、迭代等
  • 针对这种行为,可定义为接口,并在需要的地方实现这个接口即可让实现的类具备该行为
  • Java自带类库中有大量的应用,像String类型、包装类等类型都实现了接口Serializable、Comparable
  • 生活场景
    • 作为一个人类的对象,具备一般从的特征和行为;但学习了Java技术后,就新多了写代码个能力(行为),这种叠加的能力,可使用接口描述
    • 某些游戏中,英雄都有自己的基本皮肤、技能;但也可以在实战过程中,通过购买不同道具,叠加一些英雄本身不具备的技能,这样的技能,就可以使用接口描述
    • ...

定义接口

  • 语法规则

    • 关键字是interface

    • 语法规则如下:

      /**
      * 【接口】
      */
      访问修饰符 interface 接口名称{
      //【静态常量】接口不具备属性,定义属性默认会加上static final关键字
      数据类型 常量名 = 常量值;

      //【抽象方法】,接口中的所有方法都是抽象方法,没有实现逻辑,只有方法声明(确定方法签名)
      返回值类型 方法名();
      }

      语法解释 :

      • interface:接口定义关键字
      • 方法签名定义后,直接使用分号(;)结尾,所有都没有方法体
    • 接口的具体规则

      • 接口的所有属性与方法,访问修饰符默认都是public
      • 接口的所有属性都是公共的静态属性,默认都是public static final修饰的,不可修改
      • 接口的所有方法都是公共的抽象方法,默认都是public abstract修饰的,且接口的抽象方法前无需添加abstract修饰符
      • 一个接口可以有0~n个抽象方法
      • 区别于普通类,接口也是纯抽象的,只能用于被实现不能使用new关键字进行实例化
      • 接口与接口可以相互继承,类则可以实现接口
      • 实现接口的的子类,一般需要覆盖接口的所有抽象方法,否则,子类只能是一个抽象类
      • 一个类,可以实现多个接口
      • 在很多编码规则中,习惯在大驼峰前加一个大写的I,标识是一个接口;但并不是强制要求
      • JDK1.8中还引入了static方法和default方法
    • 应用实例,基于上一章的动物示例;思考,是否可以作用一个接口?

      • 应用实例1,定义使用工具接口

        package com.bjpowernode.demo;

        /**
        * 工具
        */
        public interface ITool {
        /**
        * 使用工具
        * @param toolName 工具名称
        */
        void useTool(String toolName);
        }
      • 应用实例2,定义安抚接口

        package com.bjpowernode.demo;

        /**
        * 安抚
        */
        public interface IAppease {
        /**
        * 安抚
        * @param name 被安抚的人
        */
        void appease(String name);
        }

使用接口

  • 接口定义好后,可以在需要接口定义行为需要的类中进行实现

  • 使用关键字implements

  • 应用实例,基于上一章的动物示例

    • 实例1,鸟类具有使用工具的行为

      鸟类

      package com.bjpowernode.demo;

      /**
      * 鸟类,继承于抽象类动物类Animal,需要覆盖抽象类的所有抽象方法
      * 鸟能使用工具,实现了ITool工具类
      */
      public class Bird extends Animal implements ITool {
      @Override
      public void eatFood(String foodName) {
      System.out.println("鸟儿" + super.getName() + "一小口一小口吃完了" + foodName);
      }

      @Override
      public void sound() {
      System.out.println("鸟儿" + super.getName() + "在叫,叽叽叽...");
      }

      @Override
      public void useTool(String toolName) {
      System.out.println("鸟儿" + super.getName() + "使用" + toolName + "填满了瓶子,喝到了水...");
      }
      }

      测试代码

      package com.bjpowernode.demo;

      /**
      * 动物测试
      */
      public class AnnimalDemo {
      public static void main(String[] args) {
      //ITool工具类引用
      ITool tool;

      //鸟类
      Bird bird = new Bird();
      bird.setName("乌鸦");

      //接口ITool的引用,指向子类的对象,实现多态
      tool = bird;
      tool.userTool("石子");
      }
      }
    • 实例2,海豚具有使用工具、安抚的行为

      海豚类

      package com.bjpowernode.demo;

      /**
      * 海豚,继承于抽象类动物类Animal,需要覆盖抽象类的所有抽象方法
      * 海豚能使用工具,实现了ITool工具类
      * 海豚能安抚自闭小孩,实现了IAppease工具类
      */
      public class Dolphin extends Animal implements ITool, IAppease {
      @Override
      public void eatFood(String foodName) {
      System.out.println("海豚" + super.getName() + "一口吞了" + foodName);
      }

      @Override
      public void sound() {
      System.out.println("海豚" + super.getName() + "在叫,吱吱吱...");
      }

      @Override
      public void useTool(String toolName) {
      System.out.println("海豚" + super.getName() + "使用" + toolName + "进行了表演...");
      }

      @Override
      public void appease(String name) {
      System.out.println("海豚" + super.getName() + "安抚了一个emo的小朋友" + name + ",小朋友终于笑起来了...");
      }
      }

      测试代码

      package com.bjpowernode.demo;

      /**
      * 动物测试
      */
      public class AnnimalDemo {
      public static void main(String[] args) {
      //ITool工具类引用
      ITool tool;

      //海豚
      Dolphin dolphin = new Dolphin();
      dolphin.setName("海豚一号");

      //接口ITool的引用,指向子类的对象,实现多态
      tool = dolphin;
      tool.useTool("皮球");

      //接口IAppease的引用,指向子类的对象,实现多态
      IAppease appease = dolphin;
      appease.appease("张小花");
      }
      }

【练习】

  1. 练习应用实例的内容,完成代码编写

接口的作用

  • 主要定义简单的通用规范或能力,用于在需要这些能力的类中实现
  • 也能弥补类单继承的不足
  • 面向接口编程,使得不同项目、不同层次的代码间代码耦合底降低,实现可插拔,实现高可扩展(后续的课程会学习面向接口编程)

接口继承

  • 针对一个已有的接口,除了可以被类实现,也可以被另一个接口继承,但不多见

  • 接口间的继承与类间继承一样,使用extends关键字,接口可以多继承

  • 应用实例,接口间的继承

    • 实例1,人类工具接口继承于工具接口,并使用

      工具接口

      package com.bjpowernode.demo;

      /**
      * 工具接口,父接口
      */
      public interface ITool {
      //工具别称
      String ALIAS = "干饭神器";

      /**
      * 使用工具
      *
      * @param toolName 工具名称
      */
      void useTool(String toolName);
      }

      人类工具接口

      package com.bjpowernode.demo;

      /**
      * 人类工具接口,子接口
      */
      public interface IHumanTool extends ITool {
      //工具别称
      String ALIAS = "生存必备";

      /**
      * 制作工具
      *
      * @param toolName 工具名称
      */
      void makeTool(String toolName);

      /**
      * 销毁工具
      *
      * @param toolName 工具名称
      */
      void destroyTool(String toolName);
      }

      人类

      package com.bjpowernode.demo;

      /**
      * 人类,实现IHumanTool接口,如果是普通类,则同时需要实现IHumanTool和ITool中的抽象方法
      */
      public class Human implements IHumanTool {
      @Override
      public void makeTool(String toolName) {
      System.out.println("制作了工具[" + toolName + "]");
      }

      @Override
      public void destroyTool(String toolName) {
      System.out.println("销毁了工具[" + toolName + "]");
      }

      @Override
      public void useTool(String toolName) {
      System.out.println("使用了工具[" + toolName + "]");
      }
      }

      人类测试

      package com.bjpowernode.demo;

      /**
      * 人类测试
      */
      public class HumanDemo {
      public static void main(String[] args) {
      //定义对象
      Human human = new Human();

      //使用方法
      human.makeTool("火箭");
      human.useTool("火箭");
      human.destroyTool("核武器");

      //接口静态常量
      //可使用【对象.静态常量】访问,使用的是子接口常量,但不推荐,推荐使用【接口.静态常量】
      System.out.println("【不推荐】人类使用的工具别称:" + human.ALIAS);
      System.out.println("【推荐】人类工具别称:" + IHumanTool.ALIAS);
      System.out.println("【推荐】工具别称:" + ITool.ALIAS);
      }
      }

【练习】

  1. 练习应用实例的内容,完成代码编写

static方法【扩展】

概述

  • 在JDK1.8中,接口引入了static关键字修饰的方法
  • static修饰的方法需要有方法体,即有相应的业务逻辑
  • 好处
    • 提供与接口相关的工具方法,而无需放到单独的工具类中,使得代码更内聚、更合理
  • 注意,接口的静态方法只能使用接口.方法调用,一般不能使用子类.方法调用

应用实例

  • 应用实例1、猴子使用工具,并使用static方法

    • 接口,工具接口

      package com.bjpowernode.demo.behavior;

      /**
      * 工具接口
      */
      public interface ITool {
      /**
      * 使用工具
      *
      * @param toolName 工具名称
      */
      void useTool(String toolName);

      /**
      * 增强工具
      *
      * @param toolName
      * @return
      */
      static String enhanceTool(String toolName) {
      String enhanceToolName = null;

      if (toolName != null) {
      enhanceToolName = "超级工具-" + toolName;
      }

      return enhanceToolName;
      }
      }
    • 猴子,使用工具接口

      package com.bjpowernode.demo;

      import com.bjpowernode.demo.behavior.ITool;

      /**
      * 猴子类
      */
      public class Monkey extends Animal implements ITool {
      @Override
      public void eatFood(String foodName) {
      System.out.println("猴子" + this.getName() + "大口吃完了" + foodName);
      }

      @Override
      public void sound() {
      System.out.println("猴子" + this.getName() + "在叫,呀呀呀...");
      }

      @Override
      public void useTool(String toolName) {
      System.out.println("猴子" + this.getName() + "使用[" + toolName + "]砸开了核桃,吃到了果肉...");
      }
      }
    • 猴子测试,使用static方法

      package com.bjpowernode.demo;

      import com.bjpowernode.demo.behavior.ITool;

      public class MonkeyTest {
      public static void main(String[] args) {
      System.out.println("-------------static方法使用-------------");
      Monkey monkey = new Monkey();
      monkey.setName("悟空");

      ITool tool = monkey;
      String toolName = ITool.enhanceTool("石块"); //static方法使用
      tool.useTool(toolName);
      }
      }

【练习】

  1. 练习应用实例的内容,完成代码编写

default方法【扩展】

概述

  • 在JDK1.8中,接口引入了default关键字修饰的方法
  • default修饰的方法需要有方法体,即有相应的业务逻辑
  • default修饰的方法无需在子类中覆盖
  • 另外,由于一个类可以实现多个接口,当实现的多个接口有相同签名的default修饰方法时,需要主动覆盖此方法
  • 好处
    • 为已有的被广泛使用的接口添加新方法,但不破坏原有接口使用代码的兼容性
    • 允许在接口中提供一定的具备默认实现方法,减少实现类强制覆盖抽象方法的负担,类似抽象类中的普通方法模板效果

应用实例

  • 应用实例1,人类使用工具,并使用default方法

    • 接口,工具接口

      package com.bjpowernode.demo.behavior;

      /**
      * 工具接口
      */
      public interface ITool {
      /**
      * 使用工具
      *
      * @param toolName 工具名称
      */
      void useTool(String toolName);

      /**
      * 重新使用工具,添加一些安全措施
      *
      * @param toolName
      */
      default void reuseTool(String toolName) {
      System.out.println("【检查安全帽、安全鞋、手套和防毒面具等防护措施】");
      this.useTool(toolName);
      }
      }
    • 人类,使用工具接口

      package com.bjpowernode.demo;

      import com.bjpowernode.demo.behavior.ITool;

      /**
      * 人类,实现ITool接口,只需要覆盖其中的抽象方法(不包括default方法)
      */
      public class Human implements ITool {
      private String name;

      @Override
      public void useTool(String toolName) {
      System.out.println(this.name + "使用[" + toolName + "]完成了工作。");
      }

      public Human() {
      }

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

      public String getName() {
      return name;
      }

      public void setName(String name) {
      this.name = name;
      }
      }
    • 人类测试,使用default方法

      package com.bjpowernode.demo;

      import com.bjpowernode.demo.behavior.ITool;

      public class HumanTest {
      public static void main(String[] args) {
      System.out.println("-------------default方法使用-------------");
      Human human = new Human("张三丰");

      ITool tool = human;
      tool.reuseTool("焊机"); //default方法
      }
      }

【练习】

  1. 练习应用实例的内容,完成代码编写

接口与抽象类对比

接口-与抽象类的对比

【注意】:抽象类与接口的应用场景,初学不那么容易理解,在持续学习过程中,会有更深刻的认识,先按照示例应用起来。

实战和作业

  1. 提问

    1. 下面的代码正确吗?为什么?

      package com.bjpowernode.demo;

      /**
      * 步骤
      */
      public interface IStep {
      /**
      * 第1步
      */
      void firstStep();

      /**
      * 第2步
      */
      void secondStep();

      /**
      * 第3步
      */
      void thirdStep();
      }
      package com.bjpowernode.demo;

      /**
      * 最后一步
      */
      public interface ILastStep extends IStep {
      /**
      * 第4步
      */
      void lastStep();
      }
    2. 下面的代码正确吗?为什么?

      package com.bjpowernode.demo;

      /**
      * 步骤
      */
      public interface IStep {
      /**
      * 第1步
      */
      void firstStep();

      /**
      * 第2步
      */
      void secondStep();

      /**
      * 第3步
      */
      void thirdStep();
      }
      package com.bjpowernode.demo;

      /**
      * 我的步骤
      */
      public abstract class MyStep implements IStep {
      public void firstStep() {
      System.out.println("我迈出了学习Java的第1步...");
      }
      }
  2. 完成示例应用,理解接口的使用特点

  3. 【扩展】重构程序,在"第12章-抽象类"的animal项目中添加两个Animal的子类,分别为狗类、猫类,要求如下

    1. 添加的猫类、狗类,继承于Animal类,针对eatFood、sound方法,实现子类自己的逻辑
    2. 添加陪伴行为(accompany)
      1. 添加IAccompany接口,具有accompany方法,形式参数为被陪伴的人、陪伴时间,返回为精神饱满度(值为1~100,逻辑可根据陪伴时间自定义)
      2. 狗类、猫类具备陪伴行为
    3. 狗类添加本章“定义接口”小节中的使用工具(ITool接口)、安抚(IAppease接口)的行为
    4. 使用接口ITool的引用指向不同的会使用工具的对象,测试使用工具功能
    5. 使用接口IAccompany的引用指向不同的会陪伴的对象,测试陪伴功能