1 什么是接口
1.1 多继承问题
讨论接口之前,先看一个物品归类的生活实例:
查看这个商品分类,可看出:
1、相同类别的商品具有相似特征,且属性类似:比如“新鲜水果“类、”海鲜水产“类
2、同类标签便于管理统一存储和调度
3、也存在跨类别的分类标签:比如“地方特产“、”国际美食“
4、跨类别的分类标签体现了一个物品属于多种类型的现象,这种现象称为“多继承”
如果用 Java 代码来表现上述情况,会发现,Java的继承可以实现树形分类,但是无法处理跨类别标签:
不过,Java提供了接口,解决了跨类型的继承问题。
1.2 什么是接口
接口在JAVA编程语言中是一个抽象类型,通常以interface来声明。
从面向对象编程的角度,可以将接口理解为对不同类型的事物的共同的行为特征的抽象。例如,鹰和飞机属于不同类型的事物,但是都有飞行的行为特征。
可以把接口看成是特殊的抽象类。
1.3 接口和抽象类
抽象类和接口都属于抽象的概念,它们有一些区别,可以从同类别和跨类别的角度来考虑:
- 同一种类别的公共行为和属性可以抽取到抽象类中。抽象类用于表示一种具有共性的类,可以包含实现的方法和具体的属性。比如,对于喜鹊和老鹰这两种鸟类,它们都属于鸟类的范畴,可以将它们共同的行为和属性抽象到一个抽象类(如Bird)中,以实现代码的重用和扩展。
- 不同种类的公共行为可以抽取到接口中。接口用于定义一组相关的方法,用于表示某种能力或行为。比如,喜鹊、老鹰和飞机都具有起飞和着陆的功能,但它们并不属于同一种类,此时可以将与飞行相关的共同行为抽取到一个接口(如Flyable)中,不同类别的对象可以通过实现该接口来具备飞行的能力。
根据以上原则,对于喜鹊来说,它可以继承自抽象类Bird,以获取鸟类的共性属性和行为,并且还可以实现接口Flyable,以具备飞行的能力。
抽象类和接口的设计原则:
- 将所有子类共有的方法抽象化到父类中,可以使用抽象类。
- 将部分子类中的公共方法抽象化到接口中,适用于不同类别但具有相似行为的对象。
通过合理地使用抽象类和接口,可以实现代码的复用和扩展,并且更好地表示对象之间的关系和行为。选择使用抽象类还是接口取决于具体的设计需求和对象之间的关系。
2 接口的语法
2.1 接口的语法
使用interface定义接口:
1、接口中只能定义常量和方法
- 可以省略常量的修饰词 public static final
- 可以省略抽象方法修饰词 public abstract
2、接口不能实例化创建对象,
3、接口只能被继承,作为父类型被子类型实现
比如,定义飞行接口:
- 包含常量ID
- 包含 3 个抽象方法
代码结构示意如下:
2.2 实现接口
子类使用implements实现接口:必须实现该接口中所有的抽象方法。
具体语法如下所示:
一个类可以实现多个接口:实现的接口直接用逗号分隔。
具体语法如下所示:
定义接口 Flyable 和类 Bird,并类 Plane实现接口Flyable,以及类 Eagle 继承Bird并实现Flyable;编写代码测试接口的用法。
案例示意代码如下所示:
package oop_04.interface01;
/**
* 飞行接口
*/
public interface Flyable {
int ID = 1;
/**
* 起飞
*/
void takeOff();
/**
* 飞行
*/
void fly();
/**
* 着陆
*/
void land();
}
package oop_04.interface01;
public class Plane implements Flyable{
@Override
public void takeOff() {
System.out.println("Plane takeOff...");
}
@Override
public void fly() {
System.out.println("Plane fly...");
}
@Override
public void land() {
System.out.println("Plane land...");
}
}
package oop_04.interface01;
public class Bird {
public void eat(){
System.out.println("eat...");
}
public void sleep(){
System.out.println("sleep...");
}
}
package oop_04.interface01;
public class Eagle
extends Bird implements Flyable{
@Override
public void takeOff() {
System.out.println("Eagle takeOff...");
}
@Override
public void fly() {
System.out.println("Eagle fly...");
}
@Override
public void land() {
System.out.println("Eagle land...");
}
}
package oop_04.interface01;
public class InterfaceDemo1 {
public static void main(String[] args) {
// Flyable flyable=new Flyable(); // 接口不可被实例化
System.out.println(Flyable.ID); // 接口中定义的是静态常量
Flyable eagle = new Eagle(); // 接口类型引用指向实现类的对象
eagle.fly(); // 实际执行实现类重写的方法逻辑
Flyable plane = new Plane(); // 接口类型引用指向实现类的对象
plane.fly(); // 实际执行实现类重写的方法逻辑
// eagle.eat(); // 无法访问实现类特有的方法
}
}
3 接口与多继承
3.1 接口的继承
在Java中,接口之间也可以进行继承,这被称为接口的继承或接口的扩展。
接口的继承允许一个接口继承另一个接口的方法签名。通过继承,子接口可以获得父接口定义的方法签名,并且可以在子接口中添加新的方法签名。子接口继承了父接口的方法签名后,必须提供这些方法的具体实现。
接口的继承使用关键字extends,后面跟着要继承的父接口名称。一个接口可以继承多个接口,多个父接口之间使用逗号分隔。
下面是一个接口继承的示例:
interface Shape {
void draw();
}
interface Circle extends Shape {
double getRadius();
}
interface Colorable {
void setColor(String color);
}
interface ColoredCircle extends Circle, Colorable {
void rotate();
}
在上面的示例中,接口Circle继承了接口Shape,表示Circle接口扩展了Shape接口的方法签名。接口ColoredCircle继承了接口Circle和Colorable,表示ColoredCircle接口扩展了这两个父接口的方法签名,并且可以在子接口中添加新的方法签名rotate()。
接口的继承使得接口之间可以建立层次结构,从而实现方法签名的复用和组合。通过继承,我们可以定义更具体和特定的接口,以满足不同的需求和功能。
3.2 接口与多继承
接口与多继承的关系是一个常见的面试话题。在Java中,类只能继承自一个父类,这是单继承的限制。然而,一个类可以实现多个接口,这就允许了多继承的实现。
多继承的概念意味着一个类可以从多个父类继承属性和方法。然而,在Java中,类只能继承一个父类。这就是为什么Java引入接口的原因,以实现多继承的效果。通过实现多个接口,一个类可以获得多个接口定义的行为和功能,实现了类的多继承。
如下图所示:
在这个示例中,有三个接口:Person(人)、Flyable(可飞行)和Swimmable(可潜水)。
钢铁侠类实现了Person接口,并且还实现了Flyable和Swimmable接口。这意味着钢铁侠类具有Person接口定义的人的行为和属性,同时也具有Flyable接口定义的飞行行为和Swimmable接口定义的潜水行为。
通过实现多个接口,钢铁侠类获得了多个接口定义的行为和功能,实现了类的多继承效果。钢铁侠类可以同时表现出人的特征、飞行的能力和潜水的能力。
接口的多继承使得类可以在不受单继承限制的情况下,获得多个接口定义的功能,提供了更大的灵活性和可扩展性。
3.3 接口和抽象类的区别
接口和抽象类在语法上有一些区别,主要涉及以下几个方面:
声明方式:抽象类使用 abstract 关键字进行声明,使用 class 关键字定义类。接口使用 interface 关键字进行声明。
继承关系:抽象类通过使用 extends 关键字继承其他类或抽象类。一个类只能继承一个抽象类。接口通过使用 implements 关键字实现一个或多个接口。一个类可以实现多个接口。
方法实现:抽象类可以包含实现的方法和抽象的方法。接口只能包含抽象的方法,不包含具体的方法实现。所有的方法都隐式地被声明为抽象方法,不需要使用 abstract 关键字。实现接口的类必须提供方法的具体实现。Java 8 引入了接口中的静态方法和默认方法,使得接口具备了一定的实现能力。