什么是接口##
抽象类中包含普通方法和抽象方法,如果把抽象类进行更彻底的抽象,即所有的方法都是抽象方法,那就是另外一个机制 —— 接口。
接口和类很相似,但接口不是类,它不像抽象类属于一种特殊的类。在常量、变量与运算符(一) 这篇文章里对引用数据类型分类过,接口属于引用数据类型的一种,它和类是同等级的概念而不是包含关系。
接口听起来很像是电脑主板上的USB接口或者PCI接口,或者插座上的插孔。很多教材也会这么比喻,其实这是错误的理解。如果要以主板上的接口比喻,例如USB接口,那么接口应该USB接口应该遵守的规范而不是接口本身。所以接口这个名字很容易让新人误解。
接口是干什么的##
要说接口是干什么的,我认为有两个关键字,一是规范,二是能力。接口通常用来规定实现这个接口的类必须遵守的规范,或者类必需具备的能力。
语法##
接口既然不是类,那么定义接口是自然不再是用class这个关键字了,而是用<code>interface</code>关键字。
接口和抽象类一样不能实例化,也不能被类继承,它的使用方法是让类实现它。关键字是<code>implements</code>。
接口可以继承其它接口,而且可以多继承。
定义接口的语法是:
[访问修饰符] interface 接口名 extends 父接口1, 父接口2, 父接口3... {
[public static final]属性;
[public]static方法;
[public]default方法;
[public abstract]方法;
[public static]内部类;
[public static]内部接口;
[public static]内部枚举;
}
使用接口的语法是:
class ClassName implements 接口1, 接口2, 接口3... {
}
从定义接口的语法可以看到接口内部成员可以是static 属性、static 方法、default 方法、抽象方法、内部类、内部接口、内部枚举。后面三个内部类、内部接口和枚举现在我没写到先不谈。说说前面四个。
-
static 属性
关于接口的static 属性有以下四点要说明的地方:- 接口所有的属性都是用public static final修饰。所以你写不写修饰符默认都是public static final。也就是说在接口里写<code>public static final String s</code>和<code>String s</code>是等效的。
- 因为接口不能实例化,也不能有static代码块。而属性都是final的,所以必须指定初始值。
- 可以用接口调用static属性,接口不能实例化就是没有接口的对象,但是可以用实现了这个接口的类的对象调用static属性,但是建议用接口来调不要用对象来调,这样比较规范,这样一看就知道是属于接口的方法。
- 如果接口A、接口B都有属性s。类C实现了A和B接口,那么就不能用类C的对象来调用s属性,因为不知道到底调用A.s还是B.s。
-
static 方法
关于接口的static 方法有以下两点要说明的地方:- 所有static 方法都是用public修饰,写不写public都一样。
- static 属性可以用实现了这个接口的类的对象来调用,但是static 方法不能,只能用接口来调用。
-
default 方法
关于接口的default 方法有以下三点要说明的地方:- 所有default方法都是用public修饰,写不写public都一样。
- 跟普通方法一样,使用实现了这个接口的类的对象调用default 方法。
- 如果接口A、接口B都有default 方法m()。类C实现了A和B接口,那么类C就必须重写m(),否则m()方法冲突不知道实现的是A.m()还是B.m()。如果希望实现的是A.m()可以在重写m()的时候使用A.super().m()来复用代码。
我举个例子来使用以下接口的 static 属性、static 方法和default 方法。
示例代码:
创建接口A,在前面加大写字母I是规范,方便和类区分。
接口A有static 属性,static 方法和default 方法。
public interface IA {
String s = "我是接口A的static 属性";
static void show() {
System.out.println("我是接口A的static 方法");
}
default void defaultMethod() {
System.out.println("我是接口A的default 方法");
}
}
创建接口B,接口B有一个和接口A里同名的default 方法。
public interface IB {
default void defaultMethod() {
System.out.println("我是接口B的default 方法");
}
}
创建类C实现接口A和接口B,用逗号隔开。因为接口A和接口B中都有名为defaultMethod()的default 方法,所以类C里必须重写。
public class C implements IA, IB {
@Override
public void defaultMethod() {
//因为IA, IB都有defaultMethod(),所以冲突了,必须重写
IB.super.defaultMethod(); // 通过接口名.super.xx() 来调用接口的default 方法
}
}
测试代码:
C c = new C();
System.out.println(IA.s); //static 属性可以通过对象c来调但是不建议这么做,避免混淆
IA.show(); //static 方法只能通过接口调
c.defaultMethod(); // default 方法通过对象c来调
运行结果:
<pre>
我是接口A的static 属性
我是接口A的static 方法
我是接口B的default 方法
</pre>
以上是关于接口的static 属性,static 方法和default 方法。但是这三个成员其实不是很常用,最常用的是抽象方法。这个作为重点来讲:
- 抽象方法
关于接口的abstract方法有以下三点要说明的地方:- 抽象类可以有普通方法和抽象方法,但是接口只能有抽象方法
- 接口的抽象方法和抽象类里的抽象方法一样,只是默认都是用<code>public abstract</code>修饰
- 类C实现接口A,那么类C必须实现接口A的所有抽象方法,除非类C是抽象类
来看示例代码,来个复杂点的例子。继续抽象类与抽象方法 里的示例代码。
创建一个ITransport接口,有一个传输文件抽象方法。前面说接口通常用来定义规范和能力,这个传输文件就可以视为一种能力。实现这个接口的类必须有传输文件的能力。
public interface ITransport {
//在两个硬盘间传输文件
void transport(Dish dish1, Dish dish2);
}
创建一个ISATA和IUSB接口都继承ITransport,各自添加一个connection方法。以ISATA为例,它继承了ITransport那么也就继承了transport这个方法。即是说实现ISATA必须实现transport和connection两个方法。
public interface ISATA extends ITransport {
// 连接硬盘到主板
void connection(HDD hdd);
}
public interface IUSB extends ITransport {
// 连接U盘到主板
void connection(UDish uDish);
}
创建一个主板类实现ISATA和IUSB两个接口,按照接口规定的能力它必须实现void transport(Dish dish1, Dish dish2);、void connection(HDD hdd);、void connection(UDish uDish);三个方法。
public class MotherBoard implements ISATA, IUSB {
@Override
public void transport(Dish dish1, Dish dish2) {
connection(dish1);
connection(dish2);
if (dish1.getIsConnection() && dish2.getIsConnection()) {
int speed1 = dish1.getSpeed();
int speed2 = dish2.getSpeed();
int speed = (speed1 > speed2 ? speed2 : speed1); // 传输速度取决于速度慢的那个
System.out.println("以" + speed + "MB/S的速度传输文件");
}
}
//连接硬盘,返回是否连接成功
boolean connection(Dish dish) {
if (dish instanceof HDD) {
connection((HDD)dish);
return true;
} else if (dish instanceof UDish) {
connection((UDish)dish);
return true;
} else {
System.out.println("连接失败,主板与" + dish.getName() + "不兼容");
return false;
}
}
@Override
public void connection(HDD hdd) {
hdd.setIsConnection(true);
}
@Override
public void connection(UDish uDish) {
uDish.setIsConnection(true);
}
}
定义一个硬盘根类,抽象类无法实例化。有name、iSConnection两个属性和对应的getter()和setter(),有一个获取传输速度的getSpeed();方法交给子类去实现。
public abstract class Dish {
protected String name;
protected boolean iSConnection; // 是否连接到主板
public String getName() {
return this.name;
}
public boolean getIsConnection() {
return this.iSConnection;
}
public void setIsConnection(boolean isConnection) {
this.iSConnection = isConnection;
}
public abstract int getSpeed(); // 传输速度
}
创建HDD、USB和TypeC三个类都继承自Dish,只是名字和传输速度不同。
public class HDD extends Dish {
public HDD() {
this.name = "HDD";
}
@Override
public int getSpeed() {
return 500;
}
}
public class UDish extends Dish {
public UDish() {
this.name = "UDish";
}
@Override
public int getSpeed() {
return 150;
}
}
public class TypeC extends Dish {
public TypeC() {
this.name = "TypeC";
}
@Override
public int getSpeed() {
return 1000;
}
}
测试代码:
HDD hdd = new HDD();
UDish uDish = new UDish();
TypeC tc = new TypeC();
MotherBoard mb = new MotherBoard();
mb.transport(hdd, uDish);
mb.transport(hdd, tc);
运行结果:
<pre>
以150MB/S的速度传输文件
连接失败,主板与TypeC不兼容
</pre>
本文代码下载:百度网盘
网友评论