Java的接口

介绍接口的基本使用,对接口多态,及继承类和实现接口时产生方法冲突和常量冲突分析

Author: Msuenb

Date: 2023-02-11


接口概述

多态的使用前提必须是"继承"。而类继承有如下问题:

  • 类继承有单继承限制

  • 类继承表示的是事物之间is-a的关系,但是is-a的关系要求太严格了。

为了解决这两个问题,引入了接口,接口支持:

  • 多实现

  • 用实现类和接口的has-a关系代替is-a关系

Bird is a Animal.  鸟是一种动物。
Plane is not a Animal.  飞机不是一种动物。
Plane is a Vehicle.    飞机是一种交通工具。
    
Bird has a fly();    鸟具有飞的能力。或 鸟会飞。
Plane has a fly();    飞机具有飞的功能。或 飞机会飞。
    
is-a 解决的是:是不是的问题
has-a 解决的是:能不能、会不会、可不可以的问题

语法格式

使用 interface 关键字声明接口。它也会被编译成.class文件,但它并不是类,而是另外一种引用数据类型。

【修饰符】 interface 接口名{	// 修饰符只能是 public / 缺省
    //接口的成员列表:
    // 公共的静态常量
    // 公共的抽象方法
    // 公共的默认方法(JDK1.8以上)
    // 公共的静态方法(JDK1.8以上)
    // 私有方法(JDK1.9以上)
}

示例:

interface Flyable {
    int MAX_SPEED = 299792458;  // 省略 public static final 

    void fly();   			// 省略 public

    default void end(){		// 省略public
        System.out.println("end");
    }

    static void start(){	// 省略public
        System.out.println("start");
    }

    /*private void f() {  // 不能省略 private JDK1.9及以上
        System.out.println("私有方法");
    }*/
}

注意:接口中不能定义构造方法! 但抽象类中可以定义构造方法(供子类使用)!

接口使用

  • 使用接口静态成员

    接口不能直接创建对象,但是可以通过接口名直接调用接口的静态方法和静态常量。

    Flyable.MAX_SPEED;	// 调用接口的静态常量
    Flyable.start();	// 调用接口的静态方法
    
  • 类实现接口

    类与接口的关系为实现关系,即类实现接口。实现的动作类似继承,格式相仿,实现使用 implements关键字。

    【修饰符】 class 实现类  implements 接口{
        // 重写接口中抽象方法【必须】,若实现类是抽象类,可以不重写
        // 重写接口中默认方法【可选】
    }
    
    【修饰符】 class 实现类 extends 父类 implements 接口{
        // 重写接口中抽象方法【必须】,若实现类是抽象类,可以不重写
        // 重写接口中默认方法【可选】
    }
    

    注意:

    • 如果接口的实现类是非抽象类,必须重写接口中的所有抽象方法
    • 默认方法可以选择保留,也可以重写。default单词就不要再写
    • 接口中的静态方法不能被继承也不能被重写
    class Animal {
        public void eat(){
            System.out.println("吃东西");
        }
    }
    
    class Bird extends Animal implements Flyable{
        // 重写父接口的抽象方法,【必选】
        @Override
        public void fly() {
            System.out.println("我要飞的更高~~~");
        }
    
        // 重写父接口的默认方法,【可选】
        @Override
        public void end() {
            System.out.println("轻轻落在树枝上~~~");
        }
    }
    
  • 使用接口的非静态方法

    • 对于接口的静态方法,直接(也只能)使用接口名.进行调用即可。
    • 对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用
    Bird bird = new Bird();
    
    Flyable.start();	// 调用接口的静态方法,只能通过 接口名.
    
    // 必须依赖于实现类的对象
    bird.fly();	// 调用接口的抽象方法
    bird.end();	// 调用接口的默认方法
    
    bird.eat();	// 调用父类继承的方法
    
  • 接口多实现

    在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的。并且,一个类能继承一个父类,同时实现多个接口。

    【修饰符】 class 实现类 implements 接口1, 接口2, 接口3...{
      	// 重写接口中抽象方法【必须】,若实现类是抽象类,可以不重写
        // 重写接口中默认方法【可选】
    }
    
    【修饰符】 class 实现类 extends 父类 implements 接口1, 接口2, 接口3...{
        // 重写接口中抽象方法【必须】,若实现类是抽象类,可以不重写
        // 重写接口中默认方法【可选】
    }
    

    接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次

    定义多个接口:

    interface Jumpable {
        void jump();
    }
    
    interface Runnable {
        void jump();
        void run();
    }
    

    定义实现类:

    class Bird implements Flyable, Jumpable, Runnable{
    
        // 重写父接口的抽象方法,【必选】
        @Override
        public void fly() {
            System.out.println("我要飞的更高~~~");
        }
    
        // 重写父接口的默认方法,【可选】
        @Override
        public void end() {
            System.out.println("轻轻落在树枝上~~~");
        }
    
        @Override
        public void jump() {
            System.out.println("我会跳跳~~~");
        }
    
        @Override
        public void run() {
            System.out.println("我会跑~~");
        }
    }
    

    测试类:

    public class TestBird {
        public static void main(String[] args) {
            Bird bird = new Bird();
            bird.fly();		//调用Flyable接口的抽象方法
            bird.jump();	//调用Jumpable接口的抽象方法
            bird.run();		//调用Runnable接口的抽象方法
        }
    }
    
  • 接口多继承

    一个接口能继承另一个或者多个接口,接口的继承也使用 extends 关键字,子接口继承父接口的方法。

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

接口多态

实现类似继承,因此,接口类型的变量与实现类的对象之间,也可以构成多态引用。

class Plane implements Flyable{
    @Override
    public void fly() {
        System.out.println("我直入云霄");
    }
}

class Kite implements Flyable {
    @Override
    public void fly() {
        System.out.println("我怎么飞也挣脱不了线");
    }
}
public class TestFlyableImpl {
    public static void main(String[] args) {
        Flyable f1 = new Bird();	// 接口引用实现类对象
        f1.fly();	// 调用实现类实现的方法
        
        Flyable f2 = new Plane();	
        f2.fly();
        
        Flyable f3 = new Kite();
        f3.fly();
    }
}

接口特点总结

  • 接口本身不能创建对象,只能创建接口的实现类对象,接口类型的变量可以与实现类对象构成多态引用。
  • 声明接口用interface,接口的成员声明有限制:公共的静态常量、公共的抽象方法、公共的默认方法、公共的静态方法、私有方法(JDK1.9
  • 类可以实现接口,且支持多实现。接口可以继承接口,而且支持多继承。
  • 如果接口实现类不是抽象类,就必须实现接口中所有的抽象方法。如果实现类既要继承父类又要实现父接口,那么extends在前,implements在后。
  • 接口的默认方法可以选择重写或不重写。子类重写父接口的默认方法,要去掉default,子接口重写父接口的默认方法,不要去掉default。
  • 接口的静态方法不能被继承,也不能被重写。接口的静态方法只能通过接口名.静态方法名进行调用。

关于接口的其他问题

默认方法冲突问题:

  • 亲爹优先原则

    当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的抽象方法重名,子类就近选择执行父类的成员方法。

  • 左右为难

    当一个类同时实现了多个父接口,而多个父接口中包含方法签名相同的默认方法时。

interface FatherInterface {
    default void fun() {
        System.out.println("父接口中的方法");
    }
}

interface FatherInterface2 {
    default void fun() {
        System.out.println("第二个父接口中的方法");
    }
}

class FatherClass {
    public void fun() {
        System.out.println("父类中的方法");
    }
}

class Son1 extends FatherClass implements FatherInterface {
    // 不重写 默认保留父类的
}

class Son2 extends FatherClass implements FatherInterface {
    @Override
    public void fun() {     // 重写
        // super.fun();    // 保留父类的
        FatherInterface.super.fun();    // 保留父接口的
    }
}

class Son3 implements FatherInterface, FatherInterface2 {
    @Override
    public void fun() {     // 重写
        // FatherInterface.super.fun();    // 保留第一个父接口的
        FatherInterface2.super.fun();    // 保留第二个父接口的
    }
}
public class Test {
    public static void main(String[] args) {
        new Son1().fun();
        new Son2().fun();
        new Son3().fun();
    }
}

常量冲突问题:

  • 当子类继承父类又实现父接口,而父类中存在与父接口常量同名的成员变量,并且该成员变量名在子类中仍然可见。
  • 当子类同时继承多个父接口,而多个父接口存在相同同名常量。
class SuperClass {
    int x = 1;
}
interface SuperInterface {
    int x = 2;
    int y = 2;
}
interface MotherInterface {
    int x = 3;
}
public class SubClass extends SuperClass implements SuperInterface, MotherInterface {
    public void method(){
//        System.out.println("x = " + x);//模糊不清
        System.out.println("super.x = " + super.x);
        System.out.println("SuperInterface.x = " + SuperInterface.x);
        System.out.println("MotherInterface.x = " + MotherInterface.x);
        System.out.println("y = " + y);//没有重名问题,可以直接访问
    }
}
posted @ 2023-02-12 19:28  msuenb  阅读(11)  评论(0编辑  收藏  举报