【Java】接口interface学习
参考资料::黑马程序员·入门到飞起上
1 概述
在学习完抽象类后,我们了解到抽象类中既可以包含抽象方法,也能有普通方法、构造方法以及成员变量等。而接口则是一种更为彻底的抽象形式。在JDK7及之前的版本中,接口内全部为抽象方法,并且接口同样无法创建对象。
在Java中,接口(Interface)在JDK 7之前和之后(主要是JDK 8及更高版本)有显著的区别,主要体现在功能扩展上。以下是主要差异:
2 JDK7和它之后版本的区别
特性 | JDK 7及之前 | JDK 8及之后 |
---|---|---|
抽象方法 | 仅支持 | 支持 |
默认方法 | 不支持 | 支持(default ) |
静态方法 | 不支持 | 支持 |
私有方法 | 不支持 | JDK 9+支持 |
常量字段 | 支持 | 支持 |
函数式接口 | 无明确支持 | 支持(@FunctionalInterface ) |
3 接口成分的特点
在JDK7及之前,接口主要包含抽象方法和常量。
3.1抽象方法
需要注意的是,接口中的抽象方法默认会自动添加public abstract
修饰,程序员无需手动书写。按照规范,日后在接口中定义抽象方法时,建议省略public abstract
,因为系统会自动添加。
3.2 常量
在接口中定义的成员变量,默认会被public static final
修饰。这意味着接口中定义的成员变量实际上是常量。经public static final
修饰后,变量值不可修改,且由于其静态化特性,可直接通过接口名访问,因此称为常量。常量命名规范建议全部字母大写,多个单词间用下划线连接。
3.3 案例演示
以下通过具体代码示例展示接口的定义:
public interface InterF {// 抽象方法!// public abstract void run();void run();// public abstract String getName();String getName();// public abstract int add(int a, int b);int add(int a, int b);// 它的最终写法是:// public static final int AGE = 12 ;int AGE = 12; //常量String SCHOOL_NAME = "黑马程序员";
}
4 基本的实现
4.1 实现接口的概述
在Java中,类与接口之间存在实现关系,即类实现接口。实现接口的类被称为接口的实现类,也可称作接口的子类。实现的操作类似于继承,格式相似,只是使用的关键字不同,实现使用implements
关键字。
4.2 实现接口的格式
接口实现的格式如下:
/**接口的实现:在Java中接口是被实现的,实现接口的类称为实现类。实现类的格式:*/
class 类名 implements 接口1,接口2,接口3...{}
从上述格式可以看出,一个类可以实现多个接口,即接口支持多实现。这是因为接口代表一种规范,一个类可以遵循多种不同的规范,以满足不同方面的功能需求。
4.3 类实现接口的要求和意义
- 实现类必须重写其所实现的全部接口中的所有抽象方法。
- 如果一个类实现了接口,但未重写全部接口的所有抽象方法,那么这个类必须被定义为抽象类。
- 意义:接口体现的是一种规范,它对实现类具有强制性的约束。实现类要么完整实现接口所声明的功能,要么自身定义为抽象类。这种强制性规范确保了程序的一致性和规范性,不同的实现类按照统一的接口规范实现特定功能,便于代码的组织和维护。
情况 1:普通类不实现全部方法(❌ 编译错误)
interface Animal {void eat();void sleep();
}class Dog implements Animal { // ❌ 编译错误:必须实现所有接口方法@Overridepublic void eat() {System.out.println("Dog is eating.");}// 缺少 sleep() 的实现
}
错误信息:
Error: Dog is not abstract and does not override abstract method sleep() in Animal
情况 2:抽象类不实现全部方法(✔️ 合法)
如果类声明为 abstract
,则可以 不实现接口的部分或全部方法,但子类仍需实现剩余方法。
interface Animal {void eat();void sleep();
}abstract class AbstractDog implements Animal { // ✔️ 合法:抽象类可以不实现接口方法@Overridepublic void eat() {System.out.println("AbstractDog is eating.");}// 未实现 sleep(),由子类完成
}class RealDog extends AbstractDog { // 子类必须实现剩余的 sleep()@Overridepublic void sleep() {System.out.println("RealDog is sleeping.");}
}
情况 3:接口有默认方法(Java 8+ ✔️ 合法)
如果接口方法有 default
实现,实现类可以 选择不重写。
interface Animal {void eat();default void sleep() { // 提供默认实现System.out.println("Animal is sleeping.");}
}class Dog implements Animal { // ✔️ 合法:只需实现 eat()@Overridepublic void eat() {System.out.println("Dog is eating.");}// 不重写 sleep(),直接使用接口的默认实现
}
调用:
Dog dog = new Dog();
dog.eat(); // 输出: Dog is eating.
dog.sleep(); // 输出: Animal is sleeping. (使用默认方法)
场景 | 是否合法 | 说明 |
---|---|---|
普通类不实现全部方法 | ❌ | 编译错误,必须实现所有抽象方法 |
抽象类不实现全部方法 | ✔️ | 剩余方法由子类实现 |
接口有 default 方法 | ✔️ | 可选择性重写 |
4.4 类与接口基本实现案例
以定义一个运动员的接口(规范)为例,代码如下:
/**接口:接口体现的是规范。* */
public interface SportMan {void run(); // 抽象方法,跑步。void law(); // 抽象方法,遵守法律。String compittion(String project); // 抽象方法,比赛。
}
接下来定义一个乒乓球运动员类作为接口的实现类,代码如下:
package com.itheima;
/*** 接口的实现:* 在Java中接口是被实现的,实现接口的类称为实现类。* 实现类的格式:* class 类名 implements 接口1,接口2,接口3...{*** }* */
public class PingPongMan implements SportMan {@Overridepublic void run() {System.out.println("乒乓球运动员稍微跑一下!!");}@Overridepublic void law() {System.out.println("乒乓球运动员守法!");}@Overridepublic String compittion(String project) {return "参加" + project + "得金牌!";}
}
以下是测试代码:
public class TestMain {public static void main(String[] args) {// 创建实现类对象。PingPongMan zjk = new PingPongMan();zjk.run();zjk.law();System.out.println(zjk.compittion("全球乒乓球比赛"));}
}
4.5 类与接口的多实现案例
类与接口之间支持多实现关系,即一个类可以同时实现多个接口。
首先定义两个接口,代码如下:
/** 法律规范:接口*/
public interface Law {void rule();
}/** 这一个运动员的规范:接口*/
public interface SportMan {void run();
}
然后定义一个实现类:
/*** Java中接口是可以被多实现的:* 一个类可以实现多个接口: Law, SportMan** */
public class JumpMan implements Law, SportMan {@Overridepublic void rule() {System.out.println("尊长守法");}@Overridepublic void run() {System.out.println("训练跑步!");}
}
从上述代码可以看出,类与接口之间的多实现关系使得一个类能够遵循多种不同的规范,这在实际编程中具有重要意义,它允许类在不同的功能维度上进行扩展和实现。
5 接口与接口的多继承
在Java中,接口与接口之间支持多继承关系,即一个接口可以同时继承多个接口。需要明确的是,类与接口是实现关系,而接口与接口是继承关系。接口继承接口的本质是将其他接口的抽象方法与本接口进行合并。
以下是案例演示:
public interface Abc {void go();void test();
}/** 法律规范:接口*/
public interface Law {void rule();void test();
}** 总结:* 接口与类之间是多实现的。* 接口与接口之间是多继承的。* */
public interface SportMan extends Law, Abc {void run();
}
6 扩展:接口的细节
在接口的使用过程中,有一些语法细节需要注意。这些细节无需刻意背诵,当在开发工具(如IDEA)中出现报错时,能够知道如何修改即可。理解这些细节的关键在于把握抽象的本质。
-
当两个接口中存在相同抽象方法的时候:
只需要在实现类中重写一次该方法。此时这个重写的方法,既满足了第一个接口的抽象方法重写要求,也满足了第二个接口的要求。 -
实现类能否在继承A类的同时实现其他接口:
可以。继承的父类可类比为“亲爸爸”,实现的接口可类比为“干爹”。实现类能够在继承一个类的同时,实现多个接口,但需要将接口中的所有抽象方法全部实现。 -
实现类能否在继承一个抽象类的同时实现其他接口:
同样可以。实现类在继承一个抽象类的同时,实现其他多个接口时,需将抽象类和接口中的所有抽象方法全部重写。 -
实现类Zi实现了一个接口,还继承了一个Fu类,且接口与父类中有相同方法时:
- 处理办法一:若父类中的方法体能够满足当前业务需求,子类中可不重写该方法。
- 处理办法二:若父类中的方法体无法满足当前业务需求,则需要在子类中重写该方法。
-
如果一个接口中有10个抽象方法,但在实现类中只需要使用其中一个:
可以在接口与实现类之间新建一个中间类(也称为适配器类)。让这个适配器类实现接口,并对接口中的所有方法进行空重写。然后让子类继承这个适配器类,根据实际需求重写需要使用的方法。由于中间类通常不具备实际业务意义,一般将其定义为抽象类,以防止外界创建对象。这种方式在实际开发中能够有效减少实现类的代码冗余,提高代码的灵活性和可维护性。