Java基础教程

第一章: 开启Java学习之旅

第二章: 掌握计算机基础知识

第三章: 掌握命令行基础知识

第四章: 我的第一个Java程序

第五章: Java编程必备基础

第六章: Java编程的核心:控制结构

第七章: Java面向对象基础

第八章: Java面向对象进阶

第九章: Java字符串类型

第十章: Java数组与数据结构

第十一章: Java高级数据结构

第十二章: Java并发编程基础

首页 > Java基础教程 > 第八章: Java面向对象进阶 > 8.4节: 面向接口编程

8.4节: 面向接口编程

薯条老师 2021-12-09 11:36:38 182716 0

编辑 收藏

广州番禺Python, Java小班周末班培训

第四期线下Java, Python小班周末班已经开课了,培训的课程有Python爬虫,Python后端开发,Python办公自动化,Python大数据分析, Java中高级工程师培训。授课详情请点击:http://chipscoco.com/?cate=6    

8.4.1 接口的定义

在9.3.4节中提及了接口,接口与类的作用大致相同:用来提供基本的抽象。但二者在语义上还是有区别的,类是对共有属性、行为的抽象,而接口侧重于对行为进行抽象。严格来说,子类继承父类需在语义上符合is-a关系,即子类和父类必需是同一类事物,比如父类是猫,子类也必需是猫。 思考题,如果子类是机器猫,是否违背继承的语义?

机器猫具有猫的外形,亦具有猫的行为(模拟猫的叫声),在AI的帮助下,甚至会抓老鼠。

但机器猫并不是猫,此时应该这么来设计,定义一个机器类,定义一个机器猫类,以及定义一个与猫抓老鼠行为相关的捕手接口。机器猫继承于机器类,同时实现捕手接口。现在我们开始来学习Java中的接口。以下是定义Java接口的基本语法:

[public]interface Interface [extends ParentInterface]{
 
        [public] [static] [final] property=value;
 
        [public] [abstract] data_type methodName(parameters_list);
 
 }

小写的interface是Java中的关键字,用来定义一个接口。Interface表示接口名,在命名接口时,通常采用驼峰式命名法。extends关键字用来表示继承关系,在Java中,接口可以继承接口,ParentInterface表示父接口。

Java中的类只能继承于类,有且仅有一个父类。同样地,Java中的接口只能继承接口,有且仅有一个父接口。

在接口中也可以定义属性,但属性通常是以常量的形式来定义使用,默认用public static final来进行修饰,即公有,静态,不可修改。而接口中的方法默认用public  abstract来修饰,即在默认情况下,接口中的方法都是公有的抽象方法。现在根据接口的语法来定义一个捕手接口,该接口提供了一个抓老鼠的抽象方法:

// 定义一个Catcher接口,用来提供与捕捉相关的行为抽象
interface Catcher {
    // 接口中的方法默认是抽象方法
    void catchMouse();

8.4.2 接口的实现

定义好了接口以后,需要再实现接口。在Java中通过implements关键字来实现接口。使用implements实现接口的基本语法:

[modifier] class ClassName [extends BaseClass] [implements interface1,interfac2,...]{
 // {}里面的为类体
    ;
 }

语法前面的一部分系定义类的基本语法,不再赘述。主要是后面的implements那一部分,在Java中只能通过类来实现接口,且一个类可以同时实现多个接口。类实现多个接口时,接口之间以英文逗号进行分隔。现在我们将9.4.1节中的机器猫例子写出来,定义一个机器类,机器猫类,然后让机器猫实现捕手接口:

// 定义一个Catcher接口,用来提供与捕捉相关的行为抽象
interface Catcher {
    // 接口中的方法默认是抽象方法
    void catchMouse();
}
 
 
// 定义抽象基类Machine,提供run的抽象方法
abstract class Machine {
    abstract void run();
}
 
 
// 机器猫本质是机器,所以继承于Machine类
class MachineCat extends Machine implements Catcher{
    void run(){
        System.out.println("机器猫已启动");
    }
 
    public void catchMouse(){
        System.out.println("我也能抓老鼠,难道我只能是机器吗?");
    }
}
 
 
public class HelloJava {
    public static void main(String[] args) {
        MachineCat doraemon = new MachineCat();
        // 调用run方法来启动机器猫
        doraemon.run();
        // 调用catchMouse接口来抓老鼠
        doraemon.catchMouse();
    }
}

Java中的类有且仅有一个父类,但却可以同时实现多个接口,现在修改上面的代码,新增一个Cryable接口,使得机器猫不仅能抓老鼠,还会发出像猫一样的怪叫声。

// 定义一个Catcher接口,用来提供与捕捉相关的行为抽象
interface Catcher {
    // 接口中的方法默认是抽象方法
    void catchMouse();
}
 
 
// 定义一个Cryable接口,用来提供与动物叫声相关的行为抽象
interface Cryable {
    // 供类来实现不同的cry行为
    void cry();
}
 
 
// 定义抽象基类Machine,提供run的抽象方法
abstract class Machine {
    abstract void run();
}
 
 
// 机器猫同时实现Catcher接口和Cryable接口
class MachineCat extends Machine implements Catcher, Cryable{
    void run(){
        System.out.println("机器猫已启动");
    }
 
    public void catchMouse(){
        System.out.println("我也能抓老鼠,难道我只能是机器吗?");
    }
    public void cry(){
        System.out.println("机器猫发出了像猫一样的怪叫声:miaow...miaow");
    }
}
 
 
public class HelloJava {
    public static void main(String[] args) {
        MachineCat doraemon = new MachineCat();
        // 调用run方法来启动机器猫
        doraemon.run();
        // 调用catchMouse接口来抓老鼠
        doraemon.catchMouse();
        // 调用cry接口发出猫叫声
        doraemon.cry();
    }
}

类在实现多个接口时,会同时具有多个接口的类型,利用这样的特性以及Java的多态机制,可以写出更为灵活的代码。

8.4.3 抽象类与接口

在9.4.2节中,笔者有意将Machine类设计为抽象基类。读者如果细心对比Machine类与Catcher接口,会发现两者并无太大区别:它们提供的均为行为的抽象,且提供的方法都由子类去实现。一些读者可能有疑惑,既然区别不大,那为什么Java还要引入接口的机制?其实抽象类与接口存在着较大的区别。

首先第一点,Java中的接口都是抽象方法,必需由类来实现,而抽象类既可以定义抽象方法,也可以定义非抽象方法。Java中的接口主要提供的是一系列行为的抽象,在语义上更为明确和纯粹。抽象基类新增一个非抽象方法时,子类会自动继承,不会影响代码的编译和执行。但如果接口新增一个方法,那么实现该接口的类必需实现该方法(因为接口中的方法只能是抽象方法),否则会编译报错。

其次第二点,抽象类本质还是类,子类在继承抽象类时需在语义上符合is-a关系。而类在实现接口时,通常是用来混合一个于继承体系之外的行为。以本节的机器类和机器猫类为例。机器猫本质是机器,具有机器的共性,所以应该从机器类中继承。但是抓老鼠和发出猫叫的行为,却不能通过继承获得(因为机器猫并不是猫)。此时就应该通过接口来混合一个新的类型,使得机器猫既具有机器的共性,又可以获得像猫一样的行为。

8.4.4 接口的多态

Java的接口天然支持多态,因为接口中的方法都为抽象方法,必须由其它类来实现,而且类在实现接口方法时,不需要考虑这种类继承的is-a关系,所以接口的多态对于类的多态来说更为泛型。接口的多态实例-猫鼠齐飞:

// 定义一个Flyable接口,用来提供与飞行相关的行为抽象
interface Flyable{
    void fly();
}


// 定义抽象基类Machine,提供run的抽象方法
abstract class Machine {
    abstract void run();
}


// 机器猫实现Flyable接口
class MachineCat extends Machine implements Flyable{
    void run(){
        System.out.println("机器猫已启动");
    }
    public void fly(){
        System.out.println("猫飞起来了");
    }
}


// 老鼠实现Flyable接口
class Mouse implements Flyable {
    public void fly(){
        System.out.println("老鼠飞起来了");
    }
}


public class HelloJava {
    public static void main(String[] args) {
        /*
        接口的多态不局限于同一类型,机器猫与老鼠都实现了Flyable接口,
        读者需深入理解类的多态与接口的多态的区别
         */
        Flyable everyoneCanFly = new MachineCat();
        everyoneCanFly.fly();
        everyoneCanFly = new Mouse();
        everyoneCanFly.fly();
    }
}

8.4.5 面向接口编程

在笔者看来,狭义的面向接口编程就是本节所讲的接口设计。由于Java中的接口提供的都为抽象方法,所以必需由其它类来实现,这样设计有一个很明显的优点,将接口的定义和实现隔离。

隔离是一种松耦合思想,客户端在使用接口时,只需关注接口提供了哪些方法。

定义接口就相当于制定一套行为规范,类在实现接口时,可以有不同的实现细节,但必需遵循统一的接口规范,这样可以保证接口的易用性和一致性。面向接口编程还体现在接口的多态特性,类在实现多个接口时就同时具有了多个接口的类型,利用多态,就可以编写出低冗余,高扩展的代码。广义的面向接口编程是更为高层的抽象,不会局限于某一类编程语言,感兴趣的同学,可以深入学习设计模式和系统架构。

8.4.6 高薪就业班


欢迎 发表评论: