Java接口的理论与实例们

接口

Java语言中所有的类都处于一个类层次结构中,除Object类以外,所有的类都只有一个直接父类,子类和父类之间是单继承的关系,不允许多重继承。

现实中类之间的继承关系往往是多继承的关系,为了实现多重继承,Java语言通过接口使得处于不同层次、甚至互不相关的类具有相同的行为。

6.1接口基本概念

接口是Java中最重要的概念之一,接口可以理解为一种特殊的类,里面全部由全局常量和公共的抽象方法组成。

接口的定义包括接口的声明和接口体两部分,格式:

interface 接口名称 {
    全局常量 ;
    抽象方法 ;
    静态方法 ;
    默认方法 ;
}

注意:在接口中的抽象方法必须定义成public访问权限,这是绝对不可改变的。

另:在接口中如果不写public,也是public访问权限。

例1:定义接口

//package cn.zzxy.demo;
interface A{
    public static final String AUTHOR = "ZZXY";
    public abstract void print();
    public abstract String getInfo();
}

如果接口使用public修饰,则该接口可以被所有的类使用,否则接口只能被同一个包中的类使用。

每个接口都被编译为独立的字节码文件。使用接口与使用抽象类相似。

接口可以作为引用变量的数据类型或类型转换的结果,与抽象类一样,不能用new运算符创建接口的实例。

public abstract String howToEat();
String howToEat();
//以上两行代码等价

在很多的Java程序中,经常看到编写接口方法的时候省略了public,有很多人认为它的访问权限是default,其实这是错误的,不管写不写接口中的方法永远是public。

抽象方法也可以省略修饰符,编译器自动加上public、abstract。

在UML中,接口的表示与类图相似。

例2:定义一个Eatable简单接口。

package cn.zzxy.demo;
public interface Eatable{
    public abstract String howToEat();
}

例3:接口UML图

在这里插入图片描述
接口上方使用<< interface >>表示接口,接口名和抽象方法名使用斜体表示。

接口通常表示某种能力,因此接口名后缀通常是able,如Comparable表示可比较的。

Flyable表示可飞的。

Runnable表示可执行的。

6.2 接口的实现

与抽象类一样,接口要使用也必须通过子类,子类通过implements关键字实现接口。

实现格式:

[public] class ClassName implements InterfaceList{
    //类体定义
}

一个类可以实现多个接口,需要在implements子句中指定要实现的接口并用逗号分隔。

如果把接口理解成特殊类,那么这个类利用接口实际上实现了多继承。

6.3 接口的继承

子接口继承父接口中的常量、抽象方法、默认方法。

例3:定义接口及接口继承

public interface AA{
    int STATUS = 100 ;			//常量声明
    public abstract void display();		//一个抽象方法
}
public interface BB{
    public abstract void show();		//一个抽象方法
    public default void print(){		//一个默认方法
        System.out.println("这是接口的默认方法");
    }
}
public interface CC extends AA,BB{
    int NUM = 3 ;							//定义一个常量
}
//以上三个接口单独保存为:AA.java,BB.java,CC.java

一个类要实现CC接口,必须实现CC接口的两个抽象方法。

例4:类实现具有多种继承关系的接口

public class DD implements CC{
    //实现AA接口中的display()方法
    public void display(){
        System.out.println("接口AA的display方法");
    }
    //实现BB接口中的show()方法
    public void show(){
        System.out.println("接口BB的show方法");
    }
    public static void main(String[] args){
        DD dd = new DD();
        System.out.println(DD.STATUS);
        dd.show();
        dd.print();
        AA aa = new DD();    		//向上自动转换
        aa.display();
    }
}

例5:类实现多个接口

interface A{
    public static final String AUTHOR = "ZZXY";
    public abstract void print();
    public abstract String getInfo();
}
interface B{
    public void say();
}
class X implements A,B{
    public void say(){
        System.out.println("Hello World!");
    }
    public String getInfo(){
        return "HELLO";
    }
    public void print(){
        System.out.println("作者: " + AUTHOR);
    }
}
public class InterfaceDemo{
    public static void main(String[] args){
        X newX = new X();
        newX.say();
        newX.print();
    }
}

一个类实现多个接口,就要实现每个接口的抽象方法。接口中的常量和默认方法都被实现类继承,但接口中的静态方法不被继承。

另外一种方式:

interface A{
    public static final String AUTHOR = "ZZXY";
    public abstract void print();
    public abstract String getInfo();
}
interface B{
    public void say();
}
abstract class X implements A,B{

    public String getInfo(){
        return "HELLO";
    }
    public void print(){
        System.out.println("作者: " + AUTHOR);
    }
}
public class InterfaceDemo extends X{
        public void say(){
        System.out.println("Hello World!");
    }
    public static void main(String[] args){
        X newX = new InterfaceDemo();
        newX.say();
        newX.print();
    }
}
6.5 接口类型的使用

接口也是一种引用类型,任何实现该接口的实例都可以存储在该接口类型的变量中。

当通过接口对象调用某个方法时,Java运行时系统确定该调用哪个类中的方法。

以下代码是合法的:

例6:向上自动类型转换

AA aa = new DD();     	//向上自动类型转换
BB bb = new DD();
CC cc = new CC();		//此处无法实现
aa.display();			//调用实现类的方法
bb.show();
cc.print();				//调用继承的默认方法
6.6 常量

定义在接口中的任何变量都自动加上public、final、static属性,因此它们都是常量,常量的定义可以省略修饰符。

例7:定义常量。

int STATUS = 100 ;
public int STATUS = 100 ;
public final static int STATUS = 100 ;
//按照Java标识符命名惯例,常量名都使用大写字母命名。

接口中的常量应该使用接口名引用。

不推荐在接口中定义常量,因为使用枚举类型描述一组常量集合比接口中定义常量更好。

由于接口可以多继承,可能出现常量冲突问题。

如果常量名不冲突,子接口可以继承父接口的常量。

如果多个父接口中有同名的常量,则子接口中不能继承。但子接口可以重新定义一个同名的常量。

7. 接口的静态方法和默认方法

在Java的早期版本中,接口的所用方法都必须是抽象的,从Java SE 8开始可以在接口中添加两种具体实现的方法:静态方法和默认方法。

静态方法

在一个类中可以定义静态方法,它被该类的所有实例共享。在Java SE 8中,可以在接口中定义静态方法,与接口有关的静态方法都可以在接口中定义,而不再需要辅助类。

定义静态方法使用static关键字,默认的访问修饰符是public。

例7:定义一个接口静态方法。

public interface SS{
    int STATUS = 100 ;
    public static void display(){		//静态方法
        System.out.println(STATUS);
    }
}

接口的静态方法使用“接口名.方法名()”的形式访问。接口的静态方法不能被子接口继承,也不被实现类继承。

默认方法

可以给接口中任何方法提供一个默认实现,这称为默认方法。默认方法需要使用default关键字定义。

例8:定义一个接口默认方法。

public interface BB{
    public void show();
    public default void print(){
              System.out.println("this is interface BB default method!");
    }
}

默认方法需要通过引用变量调用。

默认方法可以被子接口和实现类继承,但子接口中若定义了相同的默认方法,父类接口的默认方法被隐藏。

解决默认方法冲突

如果一个类实现了两个接口,其中一个接口有个默认方法,另一个接口也有一个名称和参数类型相同的方法。(默认方法或非默认方法)此时将产生冲突。如果出现这种情况,必须解决冲突。

例9:定义一个Person接口和Identified接口。

public interface Person{
    public String getName();
    public default int getID(){
        return 0 ;
    }
}
public interface Identified{
    public default int getID(){
        return Math.abs(hashCode());
    }
}

例10:定义一个Employee类,实现例9中的两个接口。

public class implements Person,Identified{
    String name;
    @Override
    public String getName(){
        return this.name;
    }
    //委托父接口Identified的getID()方法
    public int getID(){
        return Identified.super.getID();
    }
}

注意:如果父接口Identified不提供getID()方法的默认实现,而是定义为一个抽象方法,则Employee类将无法编译,从两个接口继承来的getID()方法发生冲突。

如果父接口都没有为共享方法提供默认实现,那么就不会发生冲突。实现类有两种选择:实现方法;或不实现方法并将该类声明为抽象类。

如果一个类继承一个父类并实现一个接口,而且从父类和接口继承了同样的方法,处理规则比较简单,采取“类比接口优先”原则,即只继承父类的方法,而忽略来自接口的默认方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值