Java学习笔记之Java接口 详解

申明:非原创,转载自 Java接口 详解(一)
Java接口 详解(二)

一、基本概念

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

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

范例:定义一个接口

interface A{//定义一个接口

    public static final String MSG = "hello";//全局常量
    public abstract void print();//抽象方法
}

二、接口的使用

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();
    }

}

运行结果:

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

以上的代码实例化了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();

    }

}

运行结果:

接口B的抽象方法get()

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

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);

    }

运行结果:

接口B的抽象方法get()
true
true

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

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

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

范例:

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()");

    }

}

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

interface A{
    public static final String MSG = "hello";
    public abstract void print();
}

等价于

interface A{
    String MSG = "hello";
    void print();
}

但是,这样会不会带来什么问题呢?如果子类子类中的覆写方法也不是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();
    }
}

运行结果:

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

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

interface A{

    String MSG = "hello";

    public void print();
}

class X implements A{

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

}

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)一个子类只能继承一个抽象类,却可以实现多个接口(在Java中,接口的主要功能是解决单继承局限问题)

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

interface A{
    public void funA();

    abstract class B{//定义一个抽象内部类
        public abstract void funB();
    }
}

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

interface A{
    public void funA();

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

    @Override
    public void funB() {

    }

}

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

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


interface

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

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

interface USB {   // 操作标准       
    public void install() ;
    public void work() ;
}

在电脑上应用此接口:

class Computer {
   public void plugin(USB usb) {
          usb.install() ;
          usb.work() ;
   }
}

定义USB设备—手机:

class Phone implements USB {
     public void install() {
           System.out.println("安装手机驱动程序。") ;
     }
     public void work() {
           System.out.println("手机与电脑进行工作。") ;
     }
}

定义USB设备—打印机:

class Print implements USB {
      public void install() {
           System.out.println("安装打印机驱动程序。") ;
      }
      public void work() {
           System.out.println("进行文件打印。") ;
      }
}

定义USB设备—MP3:

class MP3 implements USB {
      public void install() {
           System.out.println("安装MP3驱动程序。") ;
      }
      public void work() {
           System.out.println("进行MP3拷贝。") ;
      }
}

测试主类:

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());
    }
}

运行结果:

安装手机驱动程序。
手机与电脑进行工作。
安装打印机驱动程序。
进行文件打印。
安装MP3驱动程序。
进行MP3拷贝。

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

上一篇Java接口 详解(一)讲到了接口的基本概念、接口的使用和接口的实际应用(标准定义)。我们接着来讲。

一、接口的应用—工厂设计模式(Factory)

我们先看一个范例:

package com.wz.factoryDemo;

interface Fruit{
    public void eat();
}

class Apple implements Fruit{

    @Override
    public void eat() {
        System.out.println("吃苹果。。。");

    }
}

public class Client {

    public static void main(String[] args) {
        Fruit f = new Apple();
        f.eat();

    }

}

运行结果:

吃苹果。。。

以上程序非常简单,就是通过接口的子类为接口对象实例化,但这样操作会存在什么样的问题呢?在软件开发中,我们强调以下两点:
(1)主方法或主类是一个客户端,客户端的操作应该越简单越好;
(2)客户端之外的代码修改,不影响用户的使用。也就是说,用户可以不用去关心代码是否由变更。

确实,以上范例没有任何语法错误,但关键的问题是客户端中出现的new关键字上。因为,一个接口会有多个子类,对于上面的Furit接口,也可能出现多个子类对象。

来看范例,我们多加上一个接口子类:

class Orange implements Fruit{

    @Override
    public void eat() {
        System.out.println("吃橘子。。。");

    }

}

客户端是若要得到这个新的子类对象,需要修改代码为:

public class Client {

    public static void main(String[] args) {
        //Fruit f = new Apple();
        Fruit f = new Orange();
        f.eat();

    }

}

从上面我们发现,如果直接在客户端上产生一个实例化对象,那么我们每次要更换对象时,都需要修改客户端代码,这样的做法明显是不好的。而在整个代码中,我们最关心的是如何取得一个Fruit接口对象,然后进行方法的调用,至于这个接口对象时被谁实例化的,不是客户端关心的。这个问题就是代码耦合度太高!耦合度太高的产生的直接问题是代码不方便维护。

在本程序之中,最大的问题在于耦合上,发现在主方法中,一个接口和一个子类紧密耦合在一起,这种方式比较直接,可以简单的理解为由A —>B,但是这种紧密的方式不方便于维护,所以我们可以这样改:A—> C—>B,中间经历了一个过渡,这样一来,B改变,然后C去改变,但是A不需要改变。这可以参考Java中JVM的设计思想:程序—> JVM—>适应不同的操作系统。

于是,本程序我们这么修改,加上一个工厂类:

class Factory{
    public static Fruit getInstance(String className){
        if("apple".equals(className)){
            return new Apple();
        }else if("orange".equals(className)){
            return new Orange();
        }else{
            return null;
        }

    }
}

然后修改客户端:

public class Client {

    public static void main(String[] args) {
        Fruit f = Factory.getInstance("apple");
        f.eat();

    }

}

运行结果:

吃苹果。。。

这样的话,客户端不会看见具体的子类,客户端不再和一个具体的子类耦合在一起了,就算以后增加了新的子类,那么只需要修改Factory类即可实现,客户端的调用不会改变。

工厂模式的关系图如下:

factory

从工厂模式关系图看出,客户端不和具体的子类耦合在一起,若要增加新的子类,只需要修改Factory类即可实现。

二、接口的应用—代理设计模式(Proxy)

Java代理设计模式单独讲解,请移步到 Java设计模式之代理模式

所谓代理,就是一个人或者机构代表另一个人或者机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

关系图如下:

proxy

代理设计模式的核心精髓就在于:有一个主题操作接口(接口中可能有多个方法),而核心业务主题只完成核心功能,而代理主题负责完成所有与核心主题有关的辅助性操作。

三、Java抽象类和接口的区别

区别

通过上面的分析可以得出结论:在开发之中,抽象类和接口实际上都是可以使用的,并且使用那一个都没有明确的限制,可是抽象类有一个最大的缺点 : 一个子类只能够继承一个抽象类,存在单继承的局限。所以当遇到抽象类和接口都可以使用的情况下,优先考虑接口,避免单继承局限。

一些参考原则(根据自身情况参考):
(1)在进行某些公共操作的时候一定要定义出接口;
(2)有了接口就需要利用子类完善方法;
(3)如果是我们自己写的接口,尽量不要使用关键字new去直接实例化接口子类,要使用工厂类完成。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • 面向对象主要针对面向过程。 面向过程的基本单元是函数。 什么是对象:EVERYTHING IS OBJECT(万物...
    sinpi阅读 1,048评论 0 4
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,912评论 25 707
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,929评论 1 15
  • 小白心里苦啊,看图片画,果然只能这样,看来还是要多加练习加油(^ω^)
    花芊猪儿阅读 254评论 0 0
  • 001 日本茶道的传统,有一间曲径相通、专供宾客坐待主人邀请入茶室的布置得非常幽雅、简洁的“待合”。 002 茶室...
    奕品阅读 262评论 0 0