接口的深入理解

一、基本概念

接口(Interface),在Java编程语言中是一个抽象类型,是抽象方法的集合。接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

如果一个类只由抽象方法和全局常量组成,那么这种情况下不会将其定义为一个抽象类。只会定义为一个接口,所以接口严格的来讲属于一个特殊的类,而这个类里面只有抽象方法和全局常量,就连构造方法也没有。

范例:定义一个接口

interface A{//定义一个接口

    public static final String MSG = "hello";//全局常量
    public abstract void print();//抽象方法
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

二、接口的使用

1、由于接口里面存在抽象方法,所以接口对象不能直接使用关键字new进行实例化。接口的使用原则如下: 
(1)接口必须要有子类,但此时一个子类可以使用implements关键字实现多个接口; 
(2)接口的子类(如果不是抽象类),那么必须要覆写接口中的全部抽象方法; 
(3)接口的对象可以利用子类对象的向上转型进行实例化。

范例:

package com.wz.interfacedemo;

interface A{//定义一个接口A

    public static final String MSG = "hello";//全局常量

    public abstract void print();//抽象方法
}

interface B{//定义一个接口B

    public abstract void get();
}

class X implements A,B{//X类实现了A和B两个接口

    @Override
    public void print() {
        System.out.println("接口A的抽象方法print()");
    }

    @Override
    public void get() {
        System.out.println("接口B的抽象方法get()");
    }

}

public class TestDemo {

    public static void main(String[] args){

        X x = new X();//实例化子类对象
        A a = x;//向上转型
        B b = x;//向上转型

        a.print();
        b.get();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

运行结果:

接口A的抽象方法print()
接口B的抽象方法get()
  • 1
  • 2
  • 1
  • 2

以上的代码实例化了X类的对象,由于X类是A和B的子类,那么X类的对象可以变为A接口或者B接口对象。我们把测试主类代码改一下:

public class TestDemo {

    public static void main(String[] args){

        A a = new X();

        B b = (B) a;
        b.get();

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

运行结果:

接口B的抽象方法get()
  • 1
  • 1

好,没任何问题,我们再来做个验证:

public class TestDemo {

    public static void main(String[] args){

        A a = new X();

        B b = (B) a;
        b.get();

        System.out.println(a instanceof A);
        System.out.println(a instanceof B);

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

运行结果:

接口B的抽象方法get()
true
true
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

我们发现,从定义结构来讲,A和B两个接口没有任何直接联系,但这两个接口却拥有同一个子类。我们不要被类型和名称所迷惑,因为实例化的是X子类,而这个类对象属于B类的对象,所以以上代码可行,只不过从代码的编写规范来讲,并不是很好。

2、对于子类而言,除了实现接口外,还可以继承抽象类。若既要继承抽象类,同时还要实现接口的话,使用一下语法格式:

class 子类 [extends 父类] [implemetns 接口1,接口2,...] {}
  • 1
  • 1

范例:

interface A{//定义一个接口A

    public static final String MSG = "hello";//全局常量

    public abstract void print();//抽象方法
}

interface B{//定义一个接口B

    public abstract void get();
}

abstract class C{//定义一个抽象类C
    public abstract void change();
}

class X extends C implements A,B{//X类继承C类,并实现了A和B两个接口

    @Override
    public void print() {
        System.out.println("接口A的抽象方法print()");
    }

    @Override
    public void get() {
        System.out.println("接口B的抽象方法get()");
    }

    @Override
    public void change() {
        System.out.println("抽象类C的抽象方法change()");

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

对于接口,里面的组成只有抽象方法和全局常量,所以很多时候为了书写简单,可以不用写public abstract 或者public static final。并且,接口中的访问权限只有一种:public,即:定义接口方法和全局常量的时候就算没有写上public,那么最终的访问权限也是public,注意不是default。以下两种写法是完全等价的:

interface A{
    public static final String MSG = "hello";
    public abstract void print();
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

等价于

interface A{
    String MSG = "hello";
    void print();
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

但是,这样会不会带来什么问题呢?如果子类子类中的覆写方法也不是public,我们来看:

package com.wz.interfacedemo;

interface A{

    String MSG = "hello";

    void print();
}

class X implements A{

    void print() {
        System.out.println("接口A的抽象方法print()");
    }

}

public class TestDemo {
    public static void main(String[] args){

        A a = new X();
        a.print();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

运行结果:

Exception in thread "main" java.lang.IllegalAccessError: com.wz.interfacedemo.X.print()V
    at com.wz.interfacedemo.TestDemo.main(TestDemo.java:22)
  • 1
  • 2
  • 1
  • 2

这是因为接口中默认是public修饰,若子类中没用public修饰,则访问权限变严格了,给子类分配的是更低的访问权限。所以,在定义接口的时候强烈建议在抽象方法前加上public ,子类也加上:

interface A{

    String MSG = "hello";

    public void print();
}

class X implements A{

    public void print() {
        System.out.println("接口A的抽象方法print()");
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

3、在Java中,一个抽象类只能继承一个抽象类,但一个接口却可以使用extends关键字同时继承多个接口(但接口不能继承抽象类)。

范例:

interface A{
    public void funA();
}

interface B{
    public void funB();
}

//C接口同时继承了A和B两个接口
interface C extends A,B{//使用的是extends
    public void funC();
}

class X implements C{

    @Override
    public void funA() {


    }

    @Override
    public void funB() {


    }

    @Override
    public void funC() {


    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

由此可见,从继承关系来说接口的限制比抽象类少: 
(1)一个抽象类只能继承一个抽象父类,而接口可以继承多个接口; 
(2)一个子类只能继承一个抽象类,却可以实现多个接口(在Java中,接口的主要功能是解决单继承局限问题)

4、从接口的概念上来讲,接口只能由抽象方法和全局常量组成,但是内部结构是不受概念限制的,正如抽象类中可以定义抽象内部类一样,在接口中也可以定义普通内部类、抽象内部类和内部接口(但从实际的开发来讲,用户自己去定义内部抽象类或内部接口的时候是比较少见的),范例如下,在接口中定义一个抽象内部类:

interface A{
    public void funA();

    abstract class B{//定义一个抽象内部类
        public abstract void funB();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在接口中如果使用了static去定义一个内接口,它表示一个外部接口:

interface A{
    public void funA();

    static interface B{//使用了static,是一个外部接口
        public void funB();
    }
}
class X implements A.B{

    @Override
    public void funB() {

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

三、接口的实际应用(标准定义)

在日常的生活之中,接口这一名词经常听到的,例如:USB接口、打印接口、充电接口等等。 
interface

如果要进行开发,要先开发出USB接口标准,然后设备厂商才可以设计出USB设备。

现在假设每一个USB设备只有两个功能:安装驱动程序、工作。 
定义一个USB的标准:

interface USB {   // 操作标准       
    public void install() ;
    public void work() ;
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

在电脑上应用此接口:

class Computer {
   public void plugin(USB usb) {
          usb.install() ;
          usb.work() ;
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

定义USB设备—手机:

class Phone implements USB {
     public void install() {
           System.out.println("安装手机驱动程序。") ;
     }
     public void work() {
           System.out.println("手机与电脑进行工作。") ;
     }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

定义USB设备—打印机:

class Print implements USB {
      public void install() {
           System.out.println("安装打印机驱动程序。") ;
      }
      public void work() {
           System.out.println("进行文件打印。") ;
      }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

定义USB设备—MP3:

class MP3 implements USB {
      public void install() {
           System.out.println("安装MP3驱动程序。") ;
      }
      public void work() {
           System.out.println("进行MP3拷贝。") ;
      }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

测试主类:

public class TestDemo {
    public static void main(String args[]) {
        Computer c = new Computer() ;
        c.plugin(new Phone()) ;
        c.plugin(new Print()) ;
        c.plugin(new MP3());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

运行结果:

安装手机驱动程序。
手机与电脑进行工作。
安装打印机驱动程序。
进行文件打印。
安装MP3驱动程序。
进行MP3拷贝。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可以看出,不管有多少个USB接口的子类,都可以在电脑上使用。 
在现实生活中,标准的概念随处可见,而在程序里标准使用接口定义的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值