【Java23】接口

抽象类是多个子类抽象出来的模版。如果把这种抽象进行得再彻底一些,就可以提炼出更加特殊的”抽象类“,即接口(interface)

所谓接口,是比抽象类更抽象的东西:它不关心类方法的实现细节,甚至连类内部状态数据也不关心。它只规定类内必须提供某些方法(行为)。可以认为接口是多个相似类的行为的抽象,是某种行为规范接口不提供任何实现,体现了面向对象设计和实现分离的特点。

在这里插入图片描述

由上图可见,接口是比类更抽象的存在。通过使用接口,既可以让相似类之间聚起来(遵循同一个接口),又避免关系过于紧密(继承),从而实现松耦合

接口

接口使用关键字interface

[修饰符] interface 接口名 extends 父接口1, 父接口2...
{
  0到多个常量定义;
  0到多个抽象方法定义;
  0到多个内部类、接口、枚举定义;
  0到多个private, defaultstatic方法定义;
}
  • 接口的修饰符可以是public或省略。省略请开你改下,默认是包权限,即只有在相同的包接口下才可以访问该接口。
  • 接口的定义和类非常相似,除了没有构造器、初始化块,可以拥有其他成员。
  • 但请注意,接口里的成员变量只能是静态常量,方法只能是抽象实例方法、类方法、默认方法或私有方法
  • 由于接口是多个类的公有规范,因此接口里的常量、方法、内部类和内部枚举都是public的。定义接口时可以忽略访问控制符,默认是public。
  • Java 9以后允许接口拥有私有方法,主要目的是让接口自己实现一些内部功能。私有方法可以是类方法,也可以是实例方法。
  • 接口里的静态常量默认永远是public static final

对接口里的方法,除了默认方法、类方法和私有方法,其余所有方法都是抽象方法,系统自动添加public abstract修饰符。因此,接口里的”普通方法“没有方法体。但是其他方法是有方法体的。

下面是一个接口的代码示例:

public interface Output
{
    // 接口里的成员变量肯定是public static final
    int MAX_CACHE_LINE = 50;
    // 接口里的普通方法肯定是public abstract
    void out();
    void getData(String msg);
    // 接口中的默认方法需要显式用default修饰
    default void print(String... msgs)
    {
        for (var msg : msgs)
        {
            System.out.println(msg);
        }
    }
    default void test()
    {
        System.out.println("默认的test方法");
    }
    // 接口中定义类方法需要显式用static修饰
    static String staticTest()
    {
        return "staticTest";
    }
    // 接口中的私有方法需要显式用private修饰
    private void foo()
    {
        System.out.println("foo私有方法");
    }
    private static void bar()
    {
        System.out.println("bar私有静态方法");
    }
}
  1. 接口里的default方法

接口里default 的存在感突然变强了。这是为什么呢?

因为Java8之前,不允许接口里定义普通实例方法。接口里的”普通方法“统统都是抽象方法。但是Java8开始允许接口拥有自己的实例方法。为了不违反之前普通方法都是抽象方法这个原则,只好要求使用default来显式修饰真正的普通实例方法。

一句话,default修饰的方法,或者叫默认方法,就是带有方法体的实例方法。

  1. 接口里的类方法

Java8以后允许接口里定义类方法,必须使用static修饰,且总是使用public修饰。如果开发者没有明确指定public,系统会自动添加public修饰符。类方法通过接口名来调用。

  1. 接口里的private方法

由于Java8以后允许接口中定义带方法体的default方法和类方法,势必带来代码复用的问题。例如,两个default方法中包含一段相同的逻辑,这时可以把这段逻辑抽取出来变成工具方法,且隐藏起来。所以接口也允许private方法存在。

接口里的成员变量默认都是public static final。因此,即使另一个类位于不同的包下,也可以通过接口名来访问接口的成员变量。

实际上,可以把接口看成是一个特殊的类。因此,一个Java源文件中只能有1个public接口。且文件名必须与该接口名相同。

接口的继承

接口的继承保留了多继承的特点,一个接口可以有多个直接父接口。接口继承某个父接口,将获得父接口里定义的所有抽象方法和常量。

interface InterfaceA
{
  int PROP_A = 5;
  void testA();
}

interface InterfaceB
{
  int PROP_B = 6;
  void testB();
}

interface InterfaceC extends InterfaceA, InterfaceB
{
  int PROP_C = 7;
  void testC();
}

public class InterfaceExtendsTest()
{
  public static void main(String[] args)
  {
    System.out.println(InterfaceC.PROP_A);
    System.out.println(InterfaceC.PROP_B);
    System.out.println(InterfaceC.PROP_C);
  }
}
使用接口

如果把接口看做是一个抽象类的话,就好理解了。接口不能用来创建实例,但可以用于声明引用变量。当使用接口来声明引用类型变量时,这个引用类型变量必须引用到其实现类的对象。接口的主要作用就是被实现类实现。

接口的主要用途包括:

  • 定义变量,也可以用于强制类型转换;
  • 调用接口中定义的常量;
  • 被其他类实现。

接口被其他类实现,类似抽象类被继承。区别在于,一个类可以“实现”(继承)多个接口。这也是Java为类的单继承约束做的一个补充修正。另外,“实现”接口的关键字不是extends而是implements

注意,一个类实现某个接口时,必须完全实现这个接口中所有抽象方法。否则,该类将保留这个接口里继承到的抽象方法,导致该类变成抽象类。

接口的实现可以认为是继承了一个彻底抽象的类。

// 定义一个接口
interface Product
{
  int getProduceTime();
}
// 让Printer类实现Output和Product接口,Output接口在上面
public class Printer implements Output, Product
{
  private String[] printData
    = new String[MAX_CACHE_LINE]; // MAX_CACHE_LINE来自Output接口
  private int dataNum = 0; // 作业数
  public void out()
  {
    // 只要还有作业,就继续打印
    while (dataNum > 0)
    {
      System.out.println("打印机打印:" + printData[0]);
      // 作业整体前移一位,并将剩余作业数减一
      System.arraycopy(printData, 1, printData, 0, --dataNum);
    }
  }
  public void getData(String msg)
  {
    if (dataNum >= MAX_CACHE_LINE)
    {
      System.out.println("输出队列已满,添加失败");
    }
    else
    {
      // 把打印数据添加到队列里,已保存数据的数量+1
      printData[dataNum++] = msg;
    }
  }
  public int getProductTime()
  {
    return 45;
  }
  public static void main(String[] args)
  {
    // 创建一个Printer对象,被Output引用
    Output o = new Printer();
    o.getData("轻量级Java EE企业应用实战");
    o.getData("疯狂Java讲义");
    o.out();
    // 调用Output接口中的default方法
    o.print("孙悟空","猪八戒","白骨精");
    o.test();
    // 创建一个Printer对象,被Product引用
    Product p = new Printer();
    System.out.println(p.getProductTime());
  }
}

可以看到,Printer的实例既可以赋给Output引用变量,也可以赋给Product引用变量。这就模拟了C++中的多继承。

接口和抽象类

相似之处:

  • 都不能被实例化,只能被继承(实现);
  • 都可以包含抽象方法,子类必须实现这些抽象方法,否则也会变成抽象类

不同之处:

  • 接口的目的是向外提供服务(以方法的形式提供)。接口定制了系统各模块之间应该遵循的标准,因此接口不应该经常被改变。
  • 抽象类的目的是模版。可以把抽象类看成是系统实现过程中的中间产品,这个产品已经实现了系统的部分功能,但依然不能被当成最终产品,必须进一步完善。
  • 接口只能包含抽象方法、静态方法、默认方法和私有方法,不能包含普通方法;
  • 抽象类可以包含普通方法。
  • 接口只能定义常量,不能定义普通成员变量;抽象类可以。
  • 接口没有构造器;抽象类有。
  • 接口没有初始化块;抽象类可以有。
  • 抽象类是单继承,接口可以多继承。
  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值