接口
抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更加彻底,则可以提炼出一种更特殊的“抽象类–接口”。接口里不能包含普通的方法,接口里的所有方法都是抽象方法
接口定义的是多个类共同的公共行为规范,这些行为是与外界交流的通道,这就意味着接口里通常是定义一组公用方法。
和类不同,定义接口使用的是关键字interface.
定义接口的基本语法如下:
[修饰符] interface 接口名 extends 父接口1, 父接口2...
{
零到多个常量定义...
零到多个抽象方法定义...
零到多个内部类,接口,枚举定义...
零到多个默认方法或类方法定义...
}
修饰符可以是public或者省略,如果省略则默认采用包权限访问控制符
接口只能继承接口,不能继承类
由于接口定义的是一种规范,因此接口里不能包含构造器和初始化块定义。接口里可以包含成员变量(只能是静态常量),方法(只能是抽象实例方法,类方法或默认方法),内部类。
在接口内定义成员变量的时候,系统自动为其添加public static final修饰
在接口内定义普通方法的时候,系统自动为其增加public abstract修饰
接口里的普通方法不能有方法体;但是类方法和默认方法都必须有方法体
下面我们来看一个例子
package chen;
public interface Output {
//接口里面定义的成员变量只能是常量
int MAX_CACHE_LINE = 50;
//接口里定义的普通方法只能是public的抽象方法
void out();
void getData(String msg);
//在接口里面定义默认方法,需要使用default修饰
default void print(String... msgs)
{
for (String msg: msgs){
System.out.println(msg);
}
}
default void test()
{
System.out.println("默认的test()方法");
}
//在接口中定义类方法,需要static修饰
static String staticTest()
{
return "接口的类方法";
}
}
上面定义了一个Output接口,这个接口包含一个成员变量MAX_CACHE_LINE。除此之外,这个接口还定义了两个普通方法:表示取得数据的getData()方法和表示输出的out()方法。这就定义了Output接口的规范:只要某个类能取得数据,并可以将数据输出,那么它就是一个输出设备。至于实现的细节这里暂时不关心。
接口中的默认方法和类方法都默认是public的,默认方法需要用接口的实现类的实例来调用,而类方法可以直接使用接口来调用。
package min;
public class OutputFieldTest {
public static void main(String[] args)
{
//访问另一个包中的Output接口的成员变量
System.out.println(chen.Output.MAX_CACHE_LINE);
//下面语句将引发“为final变量赋值”的编译异常
//chen.Output.MAX_CACHE_LINE = 20;
//使用接口来调用类方法
System.out.println(chen.Output.staticTest());
}
}
从上面的程序我们可以看到,虽然这个类和接口处于不同的包下,但是可以访问接口的MAX_CACHE_LINE常量,说明其为public访问限定的,而且可以通过接口来访问,表明这个成员变量是一个类变量。而且很明显,这个变量是final的
接口的继承
接口的继承和类继承不一样,接口完全支持多继承,即一个接口可以有多个直接父接口。
和类继承相似,子接口扩展某个父接口时,会获得父接口里定义的所有抽象方法和常量。
package the6;
interface interfaceA{
int PROP_A = 5;
void testA();
}
interface interfaceB{
int PROP_B = 6;
void testB();
}
interface interfaceC extends interfaceA, interfaceB
{
int PROP_C = 7;
void testC();
}
public class InterfaceExtendsTest {
public static void main(String[] args)
{
System.out.println(interfaceC.PROP_A);
System.out.println(interfaceC.PROP_B);
System.out.println(interfaceC.PROP_C);
}
}
上面的程序中的interfaceC接口继承了interfaceA和interfaceB,所以interfaceC获得了它们的常量。
使用接口
接口的主要用途:
1. 定义变量,也可以用于强制类型转换。
2. 调用接口中定义的常量。
3. 被其他类实现。
一个类可以实现一个或者多个接口。
继承使用extends关键字,实现使用implements关键字,语法如下:
[修饰符] class 类名 extends 父类 implements 接口1, 接口2...
{
类体部分
}
一个类实现了一个或多个接口后,必须重写全部的抽象方法。
package the6;
import chen.Output;
//定义一个product接口
interface Product
{
int getProduceTime();
}
//让Printer类实现Output和Product接口
public class Printer implements Output, Product {
private String[] printData = new String[MAX_CACHE_LINE];
private int dataNum = 0; //用以记录当前需打印的作业数
@Override
public int getProduceTime() {
return 45;
}
@Override
public void out() {
//只要还有作业,就继续打印
while(dataNum > 0)
{
System.out.println("打印机打印:"+printData[0]);
//把作业队列整体前移一位,并将剩余的作业数减1
System.arraycopy(printData, 1, printData, 0, --dataNum);
}
}
@Override
public void getData(String msg) {
if (dataNum >= MAX_CACHE_LINE){
System.out.println("输出队列已买,添加失效");
}
else{
printData[dataNum++] = msg;
}
}
public static void main(String[] args){
//创建一个Printer对象,当成Output使用
Output o = new Printer();
o.getData("打印一");
o.getData("打印二");
o.out();
o.getData("打印三");
o.getData("打印四");
o.out();
//调用Output接口中定义的默认方法
o.print("调用Output接口中定义的默认方法","123","456","789");
o.test();
//创建一个Printer对象,当成Product使用
Product p = new Printer();
System.out.println(p.getProduceTime());
//所有接口类型的引用变量都可直接赋给Object类型的变量
Object obj = p;
}
}
我们可以看到,Printer类实现了Output接口和Product接口。
因此Printer对象既可以直接赋给Output变量,也可以直接赋给Product变量,这就是java提供的模拟多继承。
接口和抽象类
相同点:
- 接口和抽象类都不能被实例化。他们都用于被其他类实现和继承。
- 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。
不同点:
- 接口只能包含抽象方法和默认方法,不包含普通方法;抽象类可包含普通方法
- 接口不能定义静态方法;抽象类可以定义静态方法。
- 接口里只能定义静态常量;抽象类不受此限制。
- 接口不含构造器;抽象类可含构造器但是不用于创建对象。
- 接口不含初始化块;抽象类可以含有初始化块。
- 一个类可以同时实现多个接口,弥补了java单继承的不足。
面向接口编程
接口体现的是一种规范和实现分离的设计哲学,充分利用接口可以极好地降低程序各个模块之间的耦合,从而提高系统的可扩展性和可维护性。