Java中接口(interface)总结

前言

在日常生活中,我们可以接触到很多接口。Java中的接口和现实中的接口很相似,都是把某些东西衔接起来。
在这里插入图片描述
Java中的接口是什么?

接口与类一样,也是一种“引用数据类型”,其中只能有常量(只能是常量,总是static和final修饰的)和方法的声明(只能有方法名称、参数及其类型,不能有方法体);接口没有构造方法,不能被实例化。

接口是完全抽象的,可以看作是特殊的抽象类

接口和抽象类的比较:

抽象类接口
是半抽象的是完全抽象的
有构造方法没有构造方法,不能实例化
类与类之间只能单继承接口与接口之间支持多继承
一个类可以实现多个接口,一个抽象类只能继承一个类(单继承)-
-接口中只允许出现常量和抽象方法

一、接口的基础语法

1、定义接口

语法是什么?

[修饰符列表] interface 接口名{ }

接口中只包含两部分内容,一部分是:常量。一部分是:抽象方法。接口中只有以上两部分,没有其它内容了。
接口中所有的元素都是public修饰的。(都是公开的。)

我们定义一个数学加减接口:

interface MyMath {
	//常量
    double PI = 3.14;

    double add(double a,double b);  //加法
    double sub(double a,double b);  //减法
}

在接口中,我们随便定义一个变量都是常量(即static和final修饰的,值不能发生改变的变量),在定义时可以把static和final省略掉。同理,接口中的方法都是抽象方法,它的public abstract也可以省略,但是注意其中不能有方法体,不然会报错。

可以选择在接口中将方法声明为public,但即使你不这么做,它们也是public的。因此,在实现一个接口时,在接口中定义的方法必须被定义成是public,否则,它们只能得到默认的包访问权限,这样在方法被继承的过程中,其访问权限就降低了,这是编译器不允许的。

public static void main(String[] args) {
		//访问接口中常量
        System.out.println(MyMath.PI);  //输出3.14
    }

2、接口的实现

类和类之间叫做继承,类和接口之间叫做实现。继承使用extends关键字完成,实现使用implements关键字完成。
当一个非抽象的类实现接口的话,必须将接口中所有的抽象方法全部实现(覆盖、重写)。

实现MyMath接口:

class MyMathImp implements MyMath {
    @Override
    public double add(double a, double b) {
        return a + b;
    }

    @Override
    public double sub(double a, double b) {
        return a - b;
    }
}

interface MyMath {
    double PI = 3.14;

    double add(double a,double b);  //加法
    double sub(double a,double b);  //减法
}

3、接口支持多继承,一个接口可以继承多个接口

interface A{

}

// 接口支持继承
interface B extends A{

}

// 一个接口可以继承多个接口(支持多继承)
interface C extends A, B{
}

4、接口和接口之间支持多继承,那么一个类也可以同时实现多个接口

对于一个事物而言,比如一台计算机,它的上面有很多接口,有主机电源接口、键盘接口、鼠标接口,还有接显示器的…

这种机制弥补了java中的哪个缺陷? Java不像C++一样支持多继承,java中类和类只支持单继承。实际上单继承是为了简单而出现的,现实世界中存在多继承,java中的接口弥补了单继承带来的缺陷。

之前有一个结论:
无论向上转型还是向下转型,两种类型之间必须要有继承关系,没有继承关系编译器会报错。(这句话不适用在接口方面。)

在下面代码中,接口A和接口B虽然没有继承关系,但是写代码的时候,可以互转。编译器没意见。但是运行时可能出现:ClassCastException。
最好加:instanceof运算符进行判断。向下转型养成好习惯。转型之前先if+instanceof进行判断。

接口多继承示例:

public class interfaceTest03 {
    public static void main(String[] args) {
        A mma = new ABC();
        B mmb = new ABC();

        mma.a();
        //调用其他接口中的方法,需要转型(接口转型)
        B mmb2 = (B)mma;
        mmb2.b();;
        
        //向下转型
        C mmc = (C)mmb;
        mmc.b();

    }
}

class ABC implements A,B,C {
    @Override
    public void a() {
        System.out.println("This is A.");
    }
    @Override
    public void b() {
        System.out.println("This is B.");
    }
    @Override
    public void c() {
        System.out.println("This is C.");
    }
}

interface A {
    void a();
}

interface B {
    void b();
}

interface C extends A,B {
    void c();
}

输出:

This is A.
This is B.
This is B.

经过测试:接口和接口之间在进行强制类型转换的时候,没有继承关系,也可以强转。 但是要注意,运行时可能会出现ClassCastException异常。

5、当一个子类继承父类,还需要实现接口,该怎么写

extends和implements可以共存,extends在前,implements在后。

比如:    class Cat extends Animal implements Flyable {}

不同的类实现接口,都需要按照需求重写方法。

下面的代码示例中,有动物父类,子类小白龙和猫咪。小龙和小猫都继承动物类,小龙实现了飞接口和游接口,小猫也实现了飞接口,但是它们飞的时候肯定是不一样的呀。即使它们 调用同一个fly()方法,最后的执行效果不同。
示例:

public class interfaceTest {
    public static void main(String[] args) {
        //多态
        Flyable f1 = new Dragon();
        f1.fly();
        Flyable f2 = new Cat();
        f2.fly();
        // 调用同一个fly()方法,最后的执行效果不同。

        Swimable s = new Dragon();
        s.swim();
        
    }
}

//父类:动物
class Animal {
}

//接口:可飞翔的
interface Flyable {
    void fly();
}

//接口:可游泳的
interface Swimable {
    void swim();
}

//子类:继承Animal父类和实现飞和游,一个上天入海的小白龙
class Dragon extends Animal implements Flyable,Swimable {
    @Override
    public void fly() {
        System.out.println("飞龙在天~~");
    }
    @Override
    public void swim() {
        System.out.println("蛟龙入海~~");
    }
}

//子类:继承Animal和实现飞
class Cat extends Animal implements Flyable {
    public void fly() {
        System.out.println("这是一只飞猫!!!");
    }
}

输出:

飞龙在天~~
这是一只飞猫!!!
蛟龙入海~~

使用接口,写代码的时候,可以使用多态(父类型引用指向子类型对象)。

二、为什么需要接口

接口在开发中的作用,类似于多态在开发中的作用。
(多态的作用:面向抽象编程,不要面向具体编程。降低程序的耦合度。提高程序的扩展力。)

在日常的生活中,到处都有接口。用电脑举例,大家的鼠标、键盘和内存条等都是通过接口与主机连接,键盘坏了,可以重新买一个再插上去,很方便。而如果没有接口,键盘是和主机焊接死的,键盘坏了难道我们要把它整套换了吗?这就体现了接口的高扩展性(低耦合度)。
在这里插入图片描述

面向抽象编程这句话以后可以修改为:面向接口编程
有了接口就有了可插拔。可插拔表示扩展力很强。不是焊接死的。

总结一句话:三个字“解耦合”
面向接口编程,可以降低程序的耦合度,提高程序的扩展力。符合OCP开发原则(对扩展开放,对修改关闭)。
接口的使用离不开多态机制。(接口+多态才可以达到降低耦合度。)

任何一个接口都有调用者和实现者。
接口可以将调用者和实现者解耦合。
调用者面向接口调用。
实现者面向接口编写实现。

以后进行大项目的开发,一般都是将项目分离成一个模块一个模块的,模块和模块之间采用接口衔接。降低耦合度。

三、“解耦合”示例

有一间饭馆,他们不仅有中国师傅,还有美国师傅。
在这里插入图片描述

这是他们的菜单,里面可以点的菜有番茄炒蛋和鱼香肉丝(接口):

//菜单
interface FoodMenu {
    void fqcd();  //番茄炒蛋
    void yxrs();  //鱼香肉丝
}

中国师傅可以按照菜单做菜(实现菜单接口):

//中餐师傅
class China_Cook implements FoodMenu{
    @Override
    public void fqcd() {
        System.out.println("中餐-->番茄炒蛋!");  //中国师傅做的番茄炒蛋
    }
    @Override
    public void yxrs() {
        System.out.println("中餐-->鱼香肉丝!");  //中国师傅做的鱼香肉丝
    }

就你中国人会做番茄炒蛋和鱼香肉丝?美国师傅也能做:

//美国师傅
class Americ_Cook implements FoodMenu{
    @Override
    public void fqcd() {
        System.out.println("西餐-->番茄炒蛋!");  //美国师傅做的番茄炒蛋
    }
    @Override
    public void yxrs() {
        System.out.println("西餐-->鱼香肉丝!");  //美国师傅做的鱼香肉丝
    }
}

只有菜单和厨师,没有顾客怎么行,想办法让顾客下单呗!
这是一个顾客类,每个顾客手里都有一份菜单,他们可以通过order()方法下单:
注意:传入的是一份菜单,不是厨师!!
*** private FoodMenu foodMenu; //每个顾客手里都有一份菜单 ***

//顾客
class Customer {
    private FoodMenu foodMenu;  //每个顾客手里都有一份菜单

    public Customer() {

    }
    public Customer(FoodMenu foodMenu) {
        this.foodMenu = foodMenu;
    }
    public void setFoodMenu(FoodMenu foodMenu) { //想改 用set方法
        this.foodMenu = foodMenu;
    }
    public FoodMenu getFoodMenu() {  //想读 用get方法
        return foodMenu;
    }
    //点菜
    public void order() {
        FoodMenu fm = this.getFoodMenu();  //调用get方法获取菜单
        fm.fqcd();
        fm.yxrs();
    }
}

有顾客类,也有厨师类了,接下来就是实例化他们:
测试:

public class Test01 {
    public static void main(String[] args) {
    	 //创建厨师对象(多态)
        FoodMenu cook_China = new China_Cook();
        FoodMenu cook_Americ = new Americ_Cook();

        //创建顾客对象
        Customer aa = new Customer(cook_China);
        Customer bb = new Customer(cook_Americ);

        //顾客下单
        aa.order();
        bb.order();
    }
}

输出:

中餐-->番茄炒蛋!
中餐-->鱼香肉丝!
西餐-->番茄炒蛋!
西餐-->鱼香肉丝!

上面的bb顾客如果想点中国师傅做的,就设置Customer bb = new Customer(cook_China); 把cook_China传入里面。

解耦合”体现在哪?
如果顾客需要换厨师,Customer类需要改程序吗?答案是不需要,因为传入Customer类的是一份菜单接口(抽象的),不是具体的美国厨师和中国厨师(具体的类),换厨师只需要把传入的参数改变一下。
如果Customer中的构造方法是把中国师傅传入,那西餐厨师怎么办?传进去就会类型不兼容。构造方法中的传参是接口的妙处就在于它既可以传中国师傅,也可以传美国师傅。

每个接口都有实现方和调用方。在这个程序中:
接口的实现者:中国厨师、美国师傅
接口的调用者:顾客
“解耦合” 解的是谁和谁的耦合?解的是顾客和厨师的耦合。顾客关心的是菜单,不关心你后厨;厨师关心的是我要做什么菜,才不管是谁点的。所以说实际上顾客和厨师实际上是“分离”的,靠的接口把他们衔接。

进行大项目的开发的时候,一般都是将项目分离成一个模块一个模块的(比如此例中的顾客模块、中国厨师模块、美国厨师模块),模块和模块之间采用接口衔接。降低耦合度。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值