在Java语言中,组成程序的基本接口有两种:类和接口,接口是一种与抽象类相类似的结构,接口在编译后也要生成一个子节码文件。
一.接口的定义
1.定义
接口为一种特殊的类,里面全部是由全局常量和公共的抽象方法(JDK8开始增加静态方法和默认方法,JDK 9开始增加私有方法和静态私有方法)所组成。
类是一种具体的实现,而接口定义的是一批类所要遵守的规范。接口不关心实现他的类的内部状态信息。如打印机的打印模板,任何打印机只要使用这个模板就要遵守这个模板的布局规范,而打印机如何打印和打印机的种类如何实现,无须关心。
接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。
public interface Printable {
int MAX_SIZE = 60; //定义常量并赋值
void print(); //定义抽象方法
default void show1(){
System.out.println("接口中的默认方法");
}
static void show2(){
System.out.println("接口中的静态方法");
}
private static void show3(){
System.out.println("接口中的私有静态方法");
}
}
2.定义接口的语法
可以通过interface
关键字来完成
[修饰符] interface Scratch {
0~N个常量的定义...
0~N个抽象方法的定义...
0~N个默认方法的定义、...
0~N个默认方法的定义、静态方法的或私有方法的定义...
}
3.定义接口需要注意以下几点
- 修饰符可以是public或者缺省状态的,接口名称与类名的命名规则基本相同,一个接口可以继承多个父接口
- 接口中定义的数据成员均是常量,名称全部采用大写字母(用下划线分隔单个标识里的多个单词)表示,定义的同时必须给这些常量赋值,否则会产生编译错误
- 在定义接口时,抽象方法和数据成员一般不写修饰符。因为在编译时系统会自动给方法和数据成员加上
public、static、final
修饰符 - 默认方法和静态方法在JDK8以上的版本才允许被定义。其中,静态方法必须使用
static
修饰,只能使用接口名调用;默认方法必须使用default
修饰,无论程序是否指定,静态方法和默认方法总是public的 - 私有方法是JDK9新增的,可以是静态方法也可以是实例方法,主要作为工具方法使用,为接口中的默认方法或静态方法提供支持
- 接口中不能包含静态代块和构造方法
- 与类相似,一个Java源文件中,可以有很多类和接口同时存在,可把接口当成一种特殊的类,所以一个Java源文件中最多只能有一个public修饰的接口或者一个public修饰的类存在,如果文件中定义了一个public接口则文件名必须与该接口名或者该类名称相同;同理如果Java源文件中定义了一个public类则文件名必须与该类名称相同;且一个源文件中类和接口名称不能相同
public class Printable {
public static void main(String[] args) {
System.out.println("Hello Word");
}
}
interface P {
// 正确,因为P不为public修饰
}
public interface I {
// 错误,已经有一个public类了 ,删除public关键字就不会报错
}
public class C {
// 错误,已经有一个public类了
}
二.接口的继承
1.定义
接口的继承指的是接口继承接口,一个子接口可以继承多个父接口,使用关键字extends
。子接口将继承父接口中定义的常量以及所有抽象方法,并可以继承和重写接口中的默认方法。
2.格式
[修饰符] interface 子接口名称 extends 父接口1,2,...,父接口n{
}
3.举例
interface X {
int VAR_X = 6;
void methodX();
}
interface Y{
int VAR_Y=7;
void methodY();
}
public interface Printable {
int MAX_SIZE = 60; //定义常量并赋值
void print(); //定义抽象方法
default void show1() {
System.out.println("接口中的默认方法");
}
static void show2() {
System.out.println("接口中的静态方法");
}
}
interface Z extends X,Y,Printable{
int VAR_Z= 8;
void methodZ();
@Override
default void show1() {
System.out.println("重写接口中的默认方法");
}
}
class TestInterface{
public static void main(String[] args){
System.out.println(Z.MAX_SIZE); // 常量可以直接调用,但是抽象方法
System.out.println(Z.VAR_X);
System.out.println(Z.VAR_Y);
System.out.println(Z.VAR_Z);
}
}
输出结果:
60
6
7
8
三.接口的实现
1.定义
继承一文中提到一个类只能继承一个父类,这是不够灵活的,可以通过实现多个接口来作补充。一个类可以实现一个或多个接口,关键字为implements
2.接口实现的语法
(1)格式一
class 类名 implements 接口名1,接口名2,...{
...
}
(2)格式二
class 类名 extends 父类名 implements 接口名1,接口名2,...{
...
}
3.接口实现举例
继承父类的extends
需要写在实现接口implements
之前。如果继承了抽象类,需要实现抽象类中的抽象方法,还需要实现接口中的所有抽象方法。
例如定义了一个接口
//定义一个打印接口
public interface Printable {
int MAX_SIZE = 60; //定义常量并赋值
void print(); //定义抽象方法
default void show1() {
System.out.println("接口中的默认方法");
}
static void show2() {
System.out.println("Printable接口中的静态方法");
}
}
因为接口中只定义了人们关心的功能,并不考虑这些功能是如何实现的。所以Printable这个接口的类具有打印的能力。打印机能打印,所以打印机类能实现这个口;相机能打印,所以打印机类能实现这个接口;其他能够打印的也可以实现这个接口。
Java系统类库中标准借接口的命名大都以able结尾(表示具有完成某功能的能力),比如Comparable、Cloneable、Runable等。
下面是一个例子:
//定义一个打印接口
public interface Printable {
int MAX_SIZE = 60; //定义常量并赋值
void print(); //定义抽象方法
default void show1() {
System.out.println("接口中的默认方法");
}
static void show2() {
System.out.println("Printable接口中的静态方法");
}
}
interface Z extends Printable{
int VAR_Z= 8;
void methodZ();
@Override
default void show1() {
System.out.println("重写接口中的默认方法");
}
}
class TestInterface implements Z{
@Override
public void print() {
System.out.println("重写Z中继承于Printable的抽象方法");
}
@Override
public void methodZ() {
System.out.println("重写Z中的抽象方法");
}
@Override
public void show1() {
System.out.println("重写Printable中的默认方法");
}
public static void main(String[] args){
System.out.println(Z.MAX_SIZE); // 常量可以直接调用,但是抽象方法
System.out.println(Z.VAR_Z);
TestInterface testInterface = new TestInterface();
System.out.println(testInterface.MAX_SIZE); // 由于TestInterface实现了上面的接口,所以可以通过TestInterface类的对象调用接口中的常量方法
System.out.println(testInterface.VAR_Z);
testInterface.methodZ();
testInterface.print();
Printable.show2(); // 接口中的静态方法可以直接调用
}
}
在TestInterface类中,对所实现的接口中定义的抽象方法都进行了重写,并且由于Z接口继承了Printable接口,TestInterface类实现了Z接口,所以TestInterface类要对Z接口和Printable接口中的所有抽象方法都进行了重写,并且,重写的方法要用public修饰,因为Java语言规定,在类中实现接口中定义的方法时不能比接口中定义的方法(接口中的抽象方法默认public修饰)有更低的访问权限。
4.接口实现需要注意的语法
- 实现接口的声明必须写在继承声明之后,即
implements
在extends
后面 - 一个类实现了接口,必须要实现接口中所有的抽象方法
- 实现了接口的类中可以直接以接口名.常量名的方式引用接口中定义的数据成员
四.接口与抽象类的异同
1.相似点
- 两者都可包含抽象方法。实现抽象类和接口的非抽象类必须实现这些抽象方法
- 两者都不能用来实例化对象。可以声明抽象类和接口的变量,对抽象类来说,要用抽象类的非抽象子类来实例化该变量;对接口来说,要用实现接口的非抽象子类来实例化该变量
- 两者的子类如果都没有实现抽象类(接口)中声明的所有抽象方法,则该子类就是抽象类
- 两者都可以实现程序的多态性
2.不同点
- 一个类只能继承一个直接父类,但是可以实现多个接口
- 接口不包含构造方法,抽象类可包含
- 接口的数据成员只能是静态常量,不能定义普通成员变量,抽象类都可
- 接口中只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法的实现,抽象类可以包含普通方法
- 接口体现的是一种规范(打印机和相机都有打印的功能),与实现接口的子类中不存在父与子的关系;抽象类与其子类存在父与子的关系(圆形和方形都是一种形状)