在 Java 中,可以通过两种形式来达到抽象的目的,一种上一篇的主角——抽象类,另外一种就是接口。
一: 定义
接口(Interface)在计算机科学中是一个重要的概念,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
接口(interface)是抽象方法和常量值的定义的集合。
从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
public interface Electronic {
// 常量
String LED = "LED";
// 抽象方法
int getElectricityUse();
// 静态方法
static boolean isEnergyEfficient(String electtronicType) {
return electtronicType.equals(LED);
}
// 默认方法
default void printDescription() {
System.out.println("电子");
}
}
来看一下这段代码反编译后的字节码。
public interface Electronic
{
public abstract int getElectricityUse();
public static boolean isEnergyEfficient(String electtronicType)
{
return electtronicType.equals("LED");
}
public void printDescription()
{
System.out.println("\u7535\u5B50");
}
public static final String LED = "LED";
}
1)接口中定义的变量会在编译的时候自动加上 public static final
修饰符
2) 没有使用 private
、default
或者 static
关键字修饰的方法是隐式抽象的,在编译的时候会自动加上 public abstract
修饰符。
3)从 Java 8 开始,接口中允许有静态方法,静态方法无法由(实现了该接口的)类的对象调用,它只能通过接口名来调用
4)接口中允许定义 default
方法也是从 Java 8 开始的
二: 接口的继承
类实现接口时,需要实现接口中声明的所有抽象方法。这些方法没有默认的实现,因此实现类必须明确提供方法体。
方法签名(方法名、参数列表、返回类型)必须与接口中定义的方法一致。
假设我们有一个接口 Animal
,定义了动物应该有的方法:
// Animal.java
public interface Animal {
void makeSound();
void eat();
}
然后我们有一个类 Dog
,它实现了 Animal
接口:
// Dog.java
public class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
@Override
public void eat() {
System.out.println("Dog is eating.");
}
public static void main(String[] args) {
Dog myDog = new Dog();
myDog.makeSound(); // Output: Woof!
myDog.eat(); // Output: Dog is eating.
}
}
-
Animal 接口:
Animal
接口定义了两个方法makeSound()
和eat()
,它们没有实际的方法体,只是方法声明。
-
Dog 类:
Dog
类使用implements Animal
表示它实现了Animal
接口。- 必须提供
makeSound()
和eat()
方法的具体实现。在Dog
类中,makeSound()
方法输出狗的叫声"Woof!"
,eat()
方法输出狗在吃东西的信息。
三: 多接口实现
一个类可以实现多个接口,用逗号分隔,如 class ClassName implements Interface1, Interface2
.
对于每个接口,类必须提供该接口中所有方法的实现
假设我们有两个接口 Walkable
和 Swimmable
,分别定义了行走和游泳的行为:
// Walkable.java
public interface Walkable {
void walk();
}
// Swimmable.java
public interface Swimmable {
void swim();
}
然后我们有一个类 Human
,它同时实现了 Walkable
和 Swimmable
接口:
// Human.java
public class Human implements Walkable, Swimmable {
@Override
public void walk() {
System.out.println("Human is walking.");
}
@Override
public void swim() {
System.out.println("Human is swimming.");
}
public static void main(String[] args) {
Human person = new Human();
person.walk(); // Output: Human is walking.
person.swim(); // Output: Human is swimming.
}
}
接口 Walkable
和 Swimmable
:
Walkable
接口定义了 walk()
方法,用来描述可以行走的对象。
Swimmable
接口定义了 swim()
方法,用来描述可以游泳的对象。
Human 类:
Human
类使用 implements Walkable, Swimmable
表示它同时实现了 Walkable
和 Swimmable
接口。
必须提供 walk()
和 swim()
方法的具体实现。在 Human
类中,walk()
方法输出人在行走的信息,swim()
方法输出人在游泳的信息。
四: 抽象类和接口的区别
有抽象方法的类被称为抽象类,也就意味着抽象类中还能有不是抽象方法的方法。这样的类就不能算作纯粹的接口,尽管它也可以提供接口的功能——只能说抽象类是普通类与接口之间的一种中庸之道。
1. 相同点
接口的使用和抽象类的使用基本类似。
- 接口和抽象类都不能实例化,只能被继承实现。
- 接口和抽象类都可以包含抽象方法。实现接口和抽象类的类都必须实现这些抽象方法。
- 声明的抽象方法都不需要具体的方法体
2. 不同点
变量 | 构造方法 | 关键字 | 继承\实现 | 多继承 | 方法 | |
抽象类 | 子类可调用 | abstract class | 只能被类或抽象类继承 | 不支持 | 可以有方法体的方法 | |
接口 | public static final | 没有构造方法 | interface | 既可以被接口继承,也能被类或抽象类实现 | 可以继承多个父接口 | 不可以有方法体的方法 |
3. 总结区别:
- 抽象类用于类之间的继承关系,其目的是为了代码重用和扩展。它可以有构造方法、成员变量和非抽象方法的实现。
- 接口定义了类应该具备的方法,强调类的行为而非身份。类通过实现接口来达到多态的目的,提供一种规范。
- 类可以同时继承一个抽象类并实现多个接口,这允许灵活的代码设计和组织。