【JAVA】十三、基础知识“接口”精细讲解!(二)(新手友好版~)

        哈喽大家好呀qvq,这里是乎里陈,接口这一知识点博主分为三篇博客为大家进行讲解,今天为大家讲解第二篇java中实现多个接口,接口间的继承,抽象类和接口的区别知识点,更适合新手宝宝们阅读~更多内容持续更新中~

目录

1. 实现多个接口

完整代码讲解

 2. 接口间的继承

完整代码讲解

3. 抽象类和接口的区别

定义方式上不同

继承/实现方式不同

区别表格



1. 实现多个接口

        在Java中,接口(Interface)定义了一组抽象方法,用于规范类的行为。类和类之间是单继承的,一个类只能由一个父类,即java不支持类的多重继承,但是一个类可以实现多个接口,从而扩展类的功能;这种机制使得Java可以实现类似多重继承的效果。

        比如说:

  • 一个 Bird 类可以同时实现 Flyable 和 Run 这两个接口。

  • 一个 Smartphone 类可以同时实现 Callable(可打电话),Playable(可播放音乐)和 Photographable(可拍照)三个接口。

        像这样实现多个接口的话,我们可以更灵活地设计程序,避免单继承的限制,可以让类具备更多的功能~

首先,我们初步定义了一个狗狗类🐶继承动物类:

//建立一个动物抽象类:
//1.用abstract修饰,表示这是一个抽象类,不能直接创建实例
//2.包含了所有动物共有的属性和方法
public abstract class Animal {
    //公共属性名字和年龄(所有动物和植物都有的)
    public String name;
    public int age;
    //抽象方法:吃东西
    //1.用abstract修饰,没有具体的实现
    //2.所有的子类必须实现这个方法
    public abstract void eat();
    //动物类的构造方法
    public Animal(String name,int age) {
        this.name = name;
        this.age = age;
    }
}
//定义了一个狗狗类
//1.继承自动物类(extends Animal)
//2.必须实现父类的抽象方法eat()
public class Dog extends Animal{
    //狗狗的构造方法
    public Dog(String name, int age) {
        //通过super调用父类的构造方法初始化name和age
        super(name, age);
    }
    //实现父类的eat()方法
    @Override//表示重写
    public void eat() {
        System.out.println(name+"吃狗粮啦~");
    }
}

        写完上面代码后,我们知道狗狗不仅会游泳,也会跑步,则可以让这个狗狗类实现游泳接口和跑步接口(前提是定义了游泳和跑步的接口):

 定义的三个接口,狗狗只能实现里面的两个:

public interface Flyable {
    void fly();
}
public interface Running {
    void run();
}
public interface Swimming {
    void swim();
}
  • extends Animal 表示继承Animal类

  • implements Running, Swimming 表示实现两个接口,后面可通过逗号再添加几个狗狗能实现的接口

// Dog通过extends继承Animal并且通过implemens实现了Running和Swimming接口
public class Dog extends Animal implements Running,Swimming{
    ...
}

在实现接口后,我们必须还要实现Running和Swimming接口里面的方法:

// Dog通过extends继承Animal并且通过implemens实现了Running和Swimming接口
public class Dog extends Animal implements Running,Swimming{
    //狗狗的构造方法
    public Dog(String name, int age) {
        //通过super调用父类的构造方法初始化name和age
        super(name, age);
    }
    //实现父类的eat()方法
    @Override//表示重写
    public void eat() {
        System.out.println(name+"吃狗粮啦~");
    }
    //实现Running接口的run()方法
    @Override
    public void run() {
        System.out.println(name+"正在飞跑~");
    }
    //实现Swimming接口的swim()方法
    @Override
    public void swim() {
        System.out.println(name+"正在狗刨~");
    }
}

        所以通过上面的代码我们实现了Dog类继承Animal类,然后通过implements实现了Running和Swimming两个接口我们要注意一定是先继承再实现接口如果把extends放后面是会产生错误的啦:

同样的,我们知道java中一个类只能继承一个类,若是再继承一个也会产生错误啦:

        那么我们说过解决多继承的问题,到底是怎么体现的呢?我们往下看,不妨假设一下,没有Running和Swimming这两个接口,而是Running和Swimming两个类,然后Dog再继承它们:

        很显然是不行的啦~怎么可以继承多个类呢,所以我们就需要接口,可以将我们这些规则的行为放到我们接口当中,这样就能解决多继承。很明显不是所有的动物都会飞,如果我们将Flyable放到我们Animal类里面,很明显是不合适的,狗狗是不会飞的。

        我们再定义一个鸟类继承父类Animal并通过implements实现Running和Flyable接口来观察

// Bird通过extends继承Animal并且通过implemens实现了Running和Flyable接口
public class Bird extends Animal implements Running,Flyable{
    //小鸟儿的构造方法
    public Bird(String name, int age) {
        //通过super调用父类的构造方法初始化name和age
        super(name, age);
    }
    //实现父类的eat()方法
    @Override
    public void eat() {
        System.out.println(name+"吃鸟粮");
    }
    //实现Flyable接口的fly()方法
    @Override
    public void fly() {
        System.out.println(name+"正在酷酷飞翔");
    }
    //实现Running接口的run()方法
    @Override
    public void run() {
        System.out.println(name+"奋力疾走中");
    }
}

        很明显小鸟只能飞和跑,并不能游泳,所以鸟类只能实现Running和Flyable接口,不能实现Swimming接口~

🟢那么我们写下测试类

public class Test2 {
    //测试方法1 - 接收Animal类型参数;animal可以是Animal类或其任何子类的对象
    public static void test1(Animal animal) {
        // 调用动物的eat方法,具体执行哪个看实际传入对象类型
        // 例如:test1(new Dog()) 会调用Dog类的eat()
        // test1(new Cat()) 会调用Cat类的eat()
        animal.eat();
    }
    //测试方法2 - 接收Running接口类型参数
    //是任何实现了Running接口的对象
    public static void test2(Running running) {
        //调用传入对象的run()方法
        //只要对象实现了Running接口就能传入,不管它是什么具体类
        running.run();
    }
    //测试方法3 - 接收Swimming接口类型参数
    //是任何实现了Swimming接口的对象
    public static void test3(Swimming swimming) {
        //调用传入对象的swim()方法
        //可以接收任何"会游泳"的对象
        swimming.swim();
    }
    //测试方法4 - 接收Flyable接口类型参数
    //是任何实现了Flyable接口的对象
    public static void test4(Flyable flyable) {
        //调用传入对象的fly()方法
        //可以接收任何"会飞"的对象
        flyable.fly();
    }
    //第一个测试主方法 - 演示接口多态
    public static void main1(String[] args) {
        // 创建一只鸟对象,名字叫"愤怒的小鸟儿",年龄2岁
        Bird bird = new Bird("愤怒的小鸟儿",2);
        // 创建一只狗对象,名字叫"开心的小狗狗",年龄3岁
        Dog dog = new Dog("开心的小狗狗",3);
        // 测试test2方法 - 传入会跑步的对象
        test2(bird);// 让鸟儿跑步(Bird实现了Running接口)
        test2(dog);// 让狗狗跑步(Dog实现了Running接口)
        System.out.println("----------------");// 分隔线
        // 测试test3方法 - 传入会游泳的对象
        test3(dog);// 让狗狗游泳(Dog实现了Swimming接口)
        //⚠️不可以test3(bird);Bird没有实现Swimming接口
        System.out.println("----------------");// 分隔线
        // 测试test4方法 - 传入会飞的对象
        test4(bird);// 让鸟儿飞翔(Bird实现了Flyable接口)
        //⚠️不可以test4(dog);Dog没有实现Flyable接口
    }
    //第二个测试主方法 - 演示继承多态
    public static void main2(String[] args) {
        // 创建一只鸟对象,名字叫"愤怒的小鸟儿",年龄2岁
        Bird bird = new Bird("愤怒的小鸟儿",2);
        // 创建一只狗对象,名字叫"开心的小狗狗",年龄3岁
        Dog dog = new Dog("开心的小狗狗",3);
        // 测试test1方法 - 传入动物对象
        test1(bird);// 让鸟儿吃东西(调用Bird的eat方法)
        test1(dog);// 让狗狗吃东西(调用Dog的eat方法)
        // 这里因为Bird和Dog都是Animal的子类,所以都可以传入啦
    }
}

🌟 我们可以从上述代码得出以下的一些个人理解:

1. 多态是什么?

  • 就像同一个"动作"在不同对象上有不同表现
  • 例如:同样是"吃",鸟吃鸟粮,狗吃狗粮

2. 接口多态(main1演示)

  • test2( )中可以接受任何会跑步的对象实现了Running接口就可以)

  • 不管传入的是鸟还是狗,只要会跑步就行

3. 继承多态(main2演示)

  • test1( )可以接受任何动物对象(是Animal的子类)

  • 不管传入的是鸟还是狗,因为它们都是动物

4. 那么为什么这样设计呢?

  • 能提高代码灵活性:不需要为每种动物写单独的方法

  • 便于扩展:新增一些动物类型的时候不需要修改测试方法

⚠️还是再提一下:每个测试方法(test1-test4)的行为取决于:实际传入的对象类型,该对象对应方法的实现方式~


完整代码讲解

下面是完整版本的代码:附带超详细注释,更适合新手宝宝们阅读~

// 1. 首先定义接口
public interface Flyable {
    void fly();
}
public interface Running {
    void run();
}
public interface Swimming {
    void swim();
}

// 2. 定义抽象父类
//建立一个动物抽象类:
//1.用abstract修饰,表示这是一个抽象类,不能直接创建实例
//2.包含了所有动物共有的属性和方法
public abstract class Animal {
    //公共属性名字和年龄(所有动物和植物都有的)
    public String name;
    public int age;
    //抽象方法:吃东西
    //1.用abstract修饰,没有具体的实现
    //2.所有的子类必须实现这个方法
    public abstract void eat();
    //动物类的构造方法
    public Animal(String name,int age) {
        this.name = name;
        this.age = age;
    }
}

// 3. 定义具体实现类
// Bird通过extends继承Animal并且通过implemens实现了Running和Flyable接口
public class Bird extends Animal implements Running,Flyable{
    //小鸟儿的构造方法
    public Bird(String name, int age) {
        //通过super调用父类的构造方法初始化name和age
        super(name, age);
    }
    //实现父类的eat()方法
    @Override
    public void eat() {
        System.out.println(name+"吃鸟粮");
    }
    //实现Flyable接口的fly()方法
    @Override
    public void fly() {
        System.out.println(name+"正在酷酷飞翔");
    }
    //实现Running接口的run()方法
    @Override
    public void run() {
        System.out.println(name+"奋力疾走中");
    }
}

// Dog通过extends继承Animal并且通过implemens实现了Running和Swimming接口
public class Dog extends Animal implements Running,Swimming{
    //狗狗的构造方法
    public Dog(String name, int age) {
        //通过super调用父类的构造方法初始化name和age
        super(name, age);
    }
    //实现父类的eat()方法
    @Override//表示重写
    public void eat() {
        System.out.println(name+"吃狗粮啦~");
    }
    //实现Running接口的run()方法
    @Override
    public void run() {
        System.out.println(name+"正在飞跑~");
    }
    //实现Swimming接口的swim()方法
    @Override
    public void swim() {
        System.out.println(name+"正在狗刨~");
    }
}

// 4. 定义测试类
public class Test2 {
    //测试方法1 - 接收Animal类型参数;animal可以是Animal类或其任何子类的对象
    public static void test1(Animal animal) {
        // 调用动物的eat方法,具体执行哪个看实际传入对象类型
        // 例如:test1(new Dog()) 会调用Dog类的eat()
        // test1(new Cat()) 会调用Cat类的eat()
        animal.eat();
    }
    //测试方法2 - 接收Running接口类型参数
    //是任何实现了Running接口的对象
    public static void test2(Running running) {
        //调用传入对象的run()方法
        //只要对象实现了Running接口就能传入,不管它是什么具体类
        running.run();
    }
    //测试方法3 - 接收Swimming接口类型参数
    //是任何实现了Swimming接口的对象
    public static void test3(Swimming swimming) {
        //调用传入对象的swim()方法
        //可以接收任何"会游泳"的对象
        swimming.swim();
    }
    //测试方法4 - 接收Flyable接口类型参数
    //是任何实现了Flyable接口的对象
    public static void test4(Flyable flyable) {
        //调用传入对象的fly()方法
        //可以接收任何"会飞"的对象
        flyable.fly();
    }
    //第一个测试主方法 - 演示接口多态
    public static void main1(String[] args) {
        // 创建一只鸟对象,名字叫"愤怒的小鸟儿",年龄2岁
        Bird bird = new Bird("愤怒的小鸟儿",2);
        // 创建一只狗对象,名字叫"开心的小狗狗",年龄3岁
        Dog dog = new Dog("开心的小狗狗",3);
        // 测试test2方法 - 传入会跑步的对象
        test2(bird);// 让鸟儿跑步(Bird实现了Running接口)
        test2(dog);// 让狗狗跑步(Dog实现了Running接口)
        System.out.println("----------------");// 分隔线
        // 测试test3方法 - 传入会游泳的对象
        test3(dog);// 让狗狗游泳(Dog实现了Swimming接口)
        //不可以test3(bird);Bird没有实现Swimming接口
        System.out.println("----------------");// 分隔线
        // 测试test4方法 - 传入会飞的对象
        test4(bird);// 让鸟儿飞翔(Bird实现了Flyable接口)
        //不可以test4(dog);Dog没有实现Flyable接口
    }
    //第二个测试主方法 - 演示继承多态
    public static void main2(String[] args) {
        // 创建一只鸟对象,名字叫"愤怒的小鸟儿",年龄2岁
        Bird bird = new Bird("愤怒的小鸟儿",2);
        // 创建一只狗对象,名字叫"开心的小狗狗",年龄3岁
        Dog dog = new Dog("开心的小狗狗",3);
        // 测试test1方法 - 传入动物对象
        test1(bird);// 让鸟儿吃东西(调用Bird的eat方法)
        test1(dog);// 让狗狗吃东西(调用Dog的eat方法)
        // 这里因为Bird和Dog都是Animal的子类,所以都可以传入啦
    }
}

最后的输出结果为:

愤怒的小鸟儿奋力疾走中  
开心的小狗狗正在飞跑~
----------------
开心的小狗狗正在狗刨~
----------------
愤怒的小鸟儿正在酷酷飞翔

 最后总结一下:

        上述代码展示了一个类继承一个父类,同时实现多种接口。继承表达的含义是 is-a,而接口表达的含义是具有某某特性。


 2. 接口间的继承

        想象你正在设计一个动物园管理系统。最初,你定义了Swimming(可游泳)和Flyable(可飞行)两个基础接口但随着系统发展,你发现有些鸟类既会飞又会潜水(比如企鹅)。这时候,就需要用到接口间的继承啦~重新定义一个接口,这个接口具备Swimming和Flyable这两个接口的功能,让这个新的接口继承Swimming接口和Flyable接口~

例如我们先定义三个接口:

//分别定义了两个个接口
//并分别定义了各自的抽象方法
interface A {
    void testA();
}
interface B {
    void testB();
}

这时候我们想要有一个接口,拥有A和B这两个接口的功能,则这样实现:

//有一个接口,具备A和C接口的功能,extends为扩展
interface C extends A,B {
    //C这个接口具备了B和C的功能,同时也有自己的抽象方法
    void testC();
}

那么当一个类实现我们C接口,需要重写A,B,C里的三个方法:

public class Test4 implements C{

    @Override
    public void testA() {
        
    }
    @Override
    public void testB() {

    }
    @Override
    public void testC() {

    }
}

完整代码讲解

实现接口间继承示例完整的代码如下:

//分别定义了三个接口
//并分别定义了各自的抽象方法
interface A {
    void testA();
}
interface B {
    void testB();
}
//有一个接口,具备A和C接口的功能,extends为扩展
interface C extends A,B {
    //C这个接口具备了B和C的功能,同时也有自己的抽象方法
    void testC();
}
//实现子接口的类必须实现所有继承链上的抽象方法
public class Test4 implements C{

    @Override
    public void testA() {

    }
    @Override
    public void testB() {

    }
    @Override
    public void testC() {

    }
}

        接口C通过extends关键字继承了接口A和B,通过上述代码我们可以总结出接口继承的特点:

  • 一个接口(C)可以继承多个父接口(A,B)
  • 子接口(C)可以继承所有父接口(A,B)的抽象方法
  • 子接口可以定义自己的新方法
  • 接口间的继承相当于把多个接口合并在一起

3. 抽象类和接口的区别

🔴java中抽象类和接口的区别,这是常见的面试题

抽象类和接口都是 Java 中多态的常见使用方式,我们需要知道:

"抽象类管是什么,接口管能做什么"

抽象类关注类的本质,接口关注行为扩展

定义方式上不同

// 抽象类 - 用abstract修饰
abstract class Animal {
    // 可以有具体属性
    String name;
    int age;
    
    // 可以有具体方法
    public void sleep() {
        System.out.println("动物在睡觉");
    }
    
    // 可以有抽象方法
    public abstract void eat(); 
}

// 接口 - 用interface修饰
interface Swimmable {
    // 只能有常量(默认是public static final)
    int a = 5;
    
    // 只能有抽象方法(Java8前)
    void swim();
}

继承/实现方式不同

// 类继承抽象类(extends)
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃狗粮");
    }
}

// 类实现接口(implements)
class Fish implements Swimmable {
    @Override
    public void swim() {
        System.out.println("鱼在游泳");
    }
}

区别表格

特性抽象类接口
关键字abstract classinterface
子类继承/实现使用extends关键字继承抽象类(单继承)使用implements关键字实现接口(多实现)
构造方法
结构组成普通类+抽象方法抽象方法+全局变量
设计理念"是什么"(is-a关系)"能做什么"(has-a能力)
适用场景有共同特征的类

定义类的行为规范

关系一个抽象类可以实现多个接口

接口不能继承抽象类,

但是可以使用extends继承多个父接口

那么我们该选择哪个使用,可以根据以下内容选择:

1. 用抽象类当

  • 多个类有共同属性和方法
  • 需要定义一些默认实现时
  • 例子:不同种类的鸟都有羽毛,都会飞,但飞行方式不同

2. 用接口当

  • 只想定义行为规范时
  • 需要类具备多种能力
  • 例子:鸭子既能游泳(Swimming)又跑(Running)

💡总结一下

        抽象类中可以包含普通方法和普通字段这样的普通方法和字段可以被子类直接使用(不必重写)。

       而接口中,不能包含普通方法子类必须重写所有的抽象方法比如此处的 Animal 中包含一个 name 这样的属性,这个属性在任何子类中都是存在的。因此此处的 Animal 只能作为一个抽象类,而不应该成为一个接口。

class Animal {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
 }

在这其中,也经常会有这些面试题: 

  • 一个类能同时继承类和实现接口吗?

可以的,先extends类后implements接口

  • 接口能继承类吗?

不能的,接口只能继承接口


制作不易,更多内容加载中~感谢友友们的点赞收藏关注~~

如有问题欢迎批评指正,祝友友们生活愉快,学习工作顺顺利利!

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值