哈喽大家好呀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和agesuper(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和agesuper(name, age);}//实现父类的eat()方法@Override//表示重写public void eat() {System.out.println(name+"吃狗粮啦~");}//实现Running接口的run()方法@Overridepublic void run() {System.out.println(name+"正在飞跑~");}//实现Swimming接口的swim()方法@Overridepublic 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和agesuper(name, age);}//实现父类的eat()方法@Overridepublic void eat() {System.out.println(name+"吃鸟粮");}//实现Flyable接口的fly()方法@Overridepublic void fly() {System.out.println(name+"正在酷酷飞翔");}//实现Running接口的run()方法@Overridepublic 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和agesuper(name, age);}//实现父类的eat()方法@Overridepublic void eat() {System.out.println(name+"吃鸟粮");}//实现Flyable接口的fly()方法@Overridepublic void fly() {System.out.println(name+"正在酷酷飞翔");}//实现Running接口的run()方法@Overridepublic 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和agesuper(name, age);}//实现父类的eat()方法@Override//表示重写public void eat() {System.out.println(name+"吃狗粮啦~");}//实现Running接口的run()方法@Overridepublic void run() {System.out.println(name+"正在飞跑~");}//实现Swimming接口的swim()方法@Overridepublic 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{@Overridepublic void testA() {}@Overridepublic void testB() {}@Overridepublic 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{@Overridepublic void testA() {}@Overridepublic void testB() {}@Overridepublic 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 {@Overridepublic void eat() {System.out.println("狗吃狗粮");}
}// 类实现接口(implements)
class Fish implements Swimmable {@Overridepublic void swim() {System.out.println("鱼在游泳");}
}
区别表格
特性 | 抽象类 | 接口 |
---|---|---|
关键字 | abstract class | interface |
子类继承/实现 | 使用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接口
-
接口能继承类吗?
❌ 不能的,接口只能继承接口
制作不易,更多内容加载中~感谢友友们的点赞收藏关注~~
如有问题欢迎批评指正,祝友友们生活愉快,学习工作顺顺利利!