抽象类中包含普通方法和抽象方法,如果把抽象类进行更彻底的抽象,即所有的方法都是抽象方法,那就是另外一个机制 —— 接口。
接口和类很相似,但接口不是类,它不像抽象类属于一种特殊的类。在常量、变量与运算符(一) 这篇文章里对引用数据类型分类过,接口属于引用数据类型的一种,它和类是同等级的概念而不是包含关系。
接口听起来很像是电脑主板上的USB接口或者PCI接口,或者插座上的插孔。很多教材也会这么比喻,其实这是错误的理解。如果要以主板上的接口比喻,例如USB接口,那么接口应该USB接口应该遵守的规范而不是接口本身。所以接口这个名字很容易让新人误解。
接口是干什么的
要说接口是干什么的,我认为有两个关键字,一是规范,二是能力。接口通常用来规定实现这个接口的类必须遵守的规范,或者类必需具备的能力。
语法
接口既然不是类,那么定义接口是自然不再是用class这个关键字了,而是用interface
关键字。
接口和抽象类一样不能实例化,也不能被类继承,它的使用方法是让类实现它。关键字是implements
。
接口可以继承其它接口,而且可以多继承。
定义接口的语法是:
[访问修饰符] 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。也就是说在接口里写
public static final String s
和String s
是等效的。 -
因为接口不能实例化,也不能有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来调
运行结果:
我是接口A的static 属性 我是接口A的static 方法 我是接口B的default 方法
以上是关于接口的static 属性,static 方法和default 方法。但是这三个成员其实不是很常用,最常用的是抽象方法。这个作为重点来讲:
-
抽象方法
关于接口的abstract方法有以下三点要说明的地方:
-
抽象类可以有普通方法和抽象方法,但是接口只能有抽象方法
-
接口的抽象方法和抽象类里的抽象方法一样,只是默认都是用
public abstract
修饰 -
类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);
运行结果:
以150MB/S的速度传输文件 连接失败,主板与TypeC不兼容
转载请注明:学时网 » Java接口深入分析