前言

理解好了抽象的概念,进一步学习接口。

接口的设计弥补了Java不能多继承的缺陷。


关于接口

可以将接口理解为:多个类的公共规范。

接口是一种引用数据类型,最重要的内容就是其中的抽象方法

接口的定义格式:

1
2
3
public interface 接口名称 {
// 接口内容
}

在Java7中,接口可以包含:

  • 常量(定义后不可更改)
  • 抽象方法(abstract)

在Java8中,又可以定义:

  • 默认方法(default)
  • 静态方法(static)

在Java9中,还可以包含:

  • 私有方法(private)

注意:接口是没有静态代码块或者构造方法的。

使用

基本使用

接口使用规范:

  1. 接口不能直接使用,必须有一个实现类来实现该接口

    1
    2
    3
    public class 实现类名称 implements 接口名称 {
    // ...
    }
  2. 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法

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

在定义接口的抽象方法时需要注意格式:

public abstract 返回值类型 方法名称(参数列表);

  • 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
  • 这两个关键字修饰符,可以选择性地省略(省略后仍为抽象方法)

定义接口InterfaceDemo

1
2
3
4
public interface InterfaceDemo {
public abstract void method1();
void method2();
}

定义接口实现类InterfaceDemoImpl

1
2
3
4
5
6
7
8
9
10
11
public class InterfaceDemoImpl implements InterfaceDemo {
@Override
public void method1() {
System.out.println("第一个方法");
}

@Override
public void method2() {
System.out.println("第二个方法");
}
}

定义使用类Demo

1
2
3
4
5
6
7
public class Demo {
public static void main(String[] args) {
InterfaceDemoImpl Demo = new InterfaceDemoImpl();
Demo.method1();
Demo.method2();
}
}

结果如下:

1
2
第一个方法
第二个方法

默认方法

接口中的默认方法,可以解决接口升级的问题。

1
2
3
public default 返回值类型 方法名称(参数列表) {
方法体
}

假设一个项目已经投入使用,这时在接口升级中准备添加一个新的方法,但是该接口的所有实现类都需要重写新的方法。如此一来升级维护成本太大,所以在Java8之后,允许接口定义默认方法,免去接口实现类重写的麻烦。

  • 实现类可以重写接口中定义的默认方法
  • 实现类可以直接使用接口中定义的默认方法

定义接口InterfaceDemo

1
2
3
4
5
6
7
8
9
10
11
public interface InterfaceDemo {
public abstract void method();

public default void method1() {
System.out.println("接口默认方法一");
}

default void method2() {
System.out.println("接口默认方法二");
}
}

定义接口实现类InterfaceDemoImpl

1
2
3
4
5
6
7
8
9
10
11
public class InterfaceDemoImpl implements InterfaceDemo {
@Override
public void method() {
System.out.println("重写接口的抽象方法");
}

@Override
public void method1() {
System.out.println("重写接口的默认方法一");
}
}

定义使用类Demo

1
2
3
4
5
6
7
8
public class Demo {
public static void main(String[] args) {
InterfaceDemoImpl Demo = new InterfaceDemoImpl();
Demo.method();
Demo.method1();
Demo.method2();
}
}

结果如下:

1
2
3
重写接口的抽象方法
重写接口的默认方法一
接口默认方法二

静态方法

Java8后还允许接口定义静态方法,格式如下:。

1
2
3
public static 返回值类型 方法名称(参数列表) {
方法体
}

注意:

  • 必须有public关键字修饰,不然其他类无法访问
  • 正确用法:不可以通过接口的实现类来调用静态方法,直接按照调用格式使用:接口名称.静态方法名(参数);

定义接口InterfaceDemo

1
2
3
4
5
public interface InterfaceDemo {
public static void method() {
System.out.println("接口的静态方法");
}
}

定义使用类Demo

1
2
3
4
5
public class Demo {
public static void main(String[] args) {
InterfaceDemo.method();
}
}

结果如下:

1
接口的静态方法

常量

接口当中也可以定义成员变量,但是必须使用public static final三个关键字进行修饰,从效果上看,这其实就是接口的常量

1
public static final 数据类型 常量名称 = 数据值;

注意:

  • 接口当中的常量,可以省略public static final。(省略不写效果一样)
  • 接口当中的常量,必须进行赋值。
  • 推荐接口常量的命名规则:使用完全大写的字母,用下划线进行分隔。

定义接口InterfaceDemo

1
2
3
4
public interface InterfaceDemo {
public static final String STR1 = "接口常量一";
String STR2 = "接口常量二";
}

定义使用类Demo

1
2
3
4
5
6
public class Demo {
public static void main(String[] args) {
System.out.println(InterfaceDemo.STR1);
System.out.println(InterfaceDemo.STR2);
}
}

结果如下:

1
2
接口常量一
接口常量二

接口的多实现

一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。

1
2
3
public class InterfaceImpl implements InterfaceA, InterfaceB {
// 覆盖重写所有抽象方法
}

使用时需要注意:

  • 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可;
  • 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类;
  • 如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写;
  • 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法(可以不用重写)

定义接口InterfaceDemoA

1
2
3
4
5
6
7
public interface InterfaceDemoA {
public abstract void methodA();

public default void method() {
System.out.println("接口A的重名默认方法");
}
}

定义接口InterfaceDemoB

1
2
3
4
5
6
7
public interface InterfaceDemoB {
public abstract void methodB();

public default void method() {
System.out.println("接口B的重名默认方法");
}
}

定义父类Fu

1
2
3
4
5
6
7
8
9
public class Fu {
public void method_Fu() {
System.out.println("父类方法");
}

public void method() {
System.out.println("父类重名方法");
}
}

定义子类/接口实现类Zi

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Zi extends Fu implements InterfaceDemoA,InterfaceDemoB {
@Override
public void methodA() {
System.out.println("重写接口A的方法");
}

@Override
public void methodB() {
System.out.println("重写接口B的方法");
}

@Override
public void method() {
System.out.println("重写接口和父类的重名方法");
}
}

定义使用类Demo

1
2
3
4
5
6
7
8
9
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodA();
zi.methodB();
zi.method_Fu();
zi.method();
}
}

结果如下:

1
2
3
4
重写接口A的方法
重写接口B的方法
父类方法
重写接口和父类的重名方法

接口的多继承

接口与接口之间是多继承的。

1
2
3
public interface InterfaceDemo extends InterfaceA, InterfaceB {
// 覆盖重写所有冲突的默认方法
}

使用时注意:

  • 可以不用重写父接口的抽象方法(冲突也不要紧,因为实现类总要重写)
  • 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写

定义父接口InterfaceA

1
2
3
4
5
6
7
public interface InterfaceA {
public abstract void methodA();
public abstract void methodCommon();
public default void methodDefault() {
System.out.println("父接口A的重名默认方法");
}
}

定义父接口InterfaceB

1
2
3
4
5
6
7
public interface InterfaceB {
public abstract void methodB();
public abstract void methodCommon();
public default void methodDefault() {
System.out.println("父接口B的重名默认方法");
}
}

定义子接口InterfaceDemo

1
2
3
4
5
6
7
public interface InterfaceDemo extends InterfaceA, InterfaceB {
public abstract void methodC();
@Override
public default void methodDefault() {
System.out.println("重写父接口默认重名方法");
}
}

定义子接口实现类InterfaceDemoImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class InterfaceDemoImpl implements InterfaceDemo {
@Override
public void methodA() {
System.out.println("重写父接口A的方法");
}

@Override
public void methodB() {
System.out.println("重写父接口B的方法");
}

@Override
public void methodC() {
System.out.println("重写子接口的方法");
}

@Override
public void methodCommon() {
System.out.println("重写父接口的重名方法");
}
}

定义实用类Demo

1
2
3
4
5
6
7
8
9
10
public class Demo {
public static void main(String[] args) {
InterfaceDemoImpl Demo = new InterfaceDemoImpl();
Demo.methodA();
Demo.methodB();
Demo.methodC();
Demo.methodCommon();
Demo.methodDefault();
}
}

结果如下:

1
2
3
4
5
重写父接口A的方法
重写父接口B的方法
重写子接口的方法
重写父接口的重名方法
重写父接口默认重名方法

后记