Java 接口和抽象类的共同点和区别
在Java中,接口(Interface) 和 抽象类(Abstract Class) 都是用来提供类的模板、定义公共的行为规范的机制。尽管它们的目的类似,都可以被子类实现或继承,从而提供一定的功能扩展,但它们在使用场景、功能特性以及语法方面存在一些关键的区别。
1. 共同点
- 都不能被实例化:接口和抽象类都不能直接创建对象。你不能通过
new
来实例化它们。 - 都用于定义规范:接口和抽象类都用于定义类应该遵循的规范(方法)。子类可以通过实现接口或继承抽象类来实现这些规范。
- 可以包含抽象方法:接口和抽象类都可以包含抽象方法(没有方法体的方法)。这些方法必须由子类实现。
- 都支持多态:接口和抽象类都可以被用作多态的目标,可以通过接口类型或抽象类类型的引用指向具体类的对象。
2. 区别
方法 | 只能有抽象方法(Java 8 引入了默认方法和静态方法)。 | 可以包含抽象方法,也可以包含普通方法(有方法体)。 |
属性 | 所有字段默认是 public static final ,即常量。 |
可以包含实例变量(可以有不同的访问修饰符)。 |
多重继承 | 可以实现多个接口(支持多重继承)。 | 只能继承一个抽象类(不支持多重继承)。 |
构造器 | 没有构造器。 | 可以有构造器。 |
默认访问修饰符 | 默认是 public 。 |
默认是 protected 。 |
实现方式 | 类通过 implements 关键字来实现接口。 |
类通过 extends 关键字来继承抽象类。 |
适用场景 | 适用于定义行为规范,可以由多个不相关的类来实现。 | 适用于提供一个基础类的功能,允许部分方法有默认实现。 |
3. 接口(Interface)
接口通常用于定义一组公共的、标准的行为规范,任何类都可以通过实现接口来遵循这些规范。接口适合用于不相关的类之间共享公共行为,比如一个动物类与一个电子设备类都可以实现 Playable
接口。
代码示例(接口):
// 定义接口
interface Playable {
void play(); // 接口中的方法默认是 public abstract 的
}
class Dog implements Playable {
@Override
public void play() {
System.out.println("Dog is playing");
}
}
class Robot implements Playable {
@Override
public void play() {
System.out.println("Robot is playing");
}
}
public class InterfaceExample {
public static void main(String[] args) {
Playable dog = new Dog();
Playable robot = new Robot();
dog.play(); // 输出: Dog is playing
robot.play(); // 输出: Robot is playing
}
}
说明:
Playable
是一个接口,定义了play()
方法。Dog
和Robot
类分别实现了Playable
接口,并提供了自己的play()
方法实现。- 接口的好处在于它可以被多个不相关的类实现,体现了 多态性 和 解耦性。
4. 抽象类(Abstract Class)
抽象类用于提供一个共享的基类,其他类可以继承并扩展它。抽象类允许在类中定义部分方法的实现,并保留一些方法作为抽象方法,供子类实现。抽象类适合用于提供通用的功能实现,同时也要求子类提供特定的实现。
代码示例(抽象类):
// 定义抽象类
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
// 抽象方法,必须由子类实现
abstract void sound();
// 普通方法,子类可以继承
public void eat() {
System.out.println(name + " is eating.");
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
void sound() {
System.out.println("Cat meows");
}
}
public class AbstractClassExample {
public static void main(String[] args) {
Animal dog = new Dog("Buddy");
Animal cat = new Cat("Kitty");
dog.sound(); // 输出: Dog barks
dog.eat(); // 输出: Buddy is eating.
cat.sound(); // 输出: Cat meows
cat.eat(); // 输出: Kitty is eating.
}
}
说明:
Animal
是一个抽象类,定义了一个抽象方法sound()
和一个普通方法eat()
。Dog
和Cat
类继承了Animal
类,并实现了sound()
方法。- 抽象类的好处在于它提供了共享的行为(如
eat()
),并允许子类实现特定的行为(如sound()
)。
5. 接口与抽象类的选择
继承方式 | 一个类可以实现多个接口 | 一个类只能继承一个抽象类 |
默认方法实现 | 从 Java 8 开始,接口可以有默认方法实现(default )。 |
抽象类可以有方法的具体实现 |
使用场景 | 用于定义多个不相关类的公共行为,适合多重继承的情况。 | 用于提供共享的基础类,适合有部分实现的场景。 |
访问控制符 | 默认所有方法为 public ,字段为 public static final 。 |
可以有任何访问修饰符(private 、protected 、public )。 |
构造器 | 无法定义构造方法。 | 可以有构造方法 |
选择实例:
-
如果需要定义多个类之间共享的行为,并且不关心它们是否相关,使用接口。例如,你有多个类
Car
、Bicycle
、Scooter
,它们可能都实现Drivable
接口。 -
如果需要提供一个基础类,供多个子类继承,并且可以有部分方法的实现,使用抽象类。例如,你想定义一个基类
Shape
,并为所有形状提供area()
方法的默认实现,同时要求每个形状类实现draw()
方法。
6. 总结
方法 | 只能包含抽象方法或默认方法、静态方法 | 可以包含抽象方法和非抽象方法 |
字段 | 所有字段默认是 public static final 常量 |
可以包含实例变量,访问控制符可以是 private 、protected 、public |
构造器 | 没有构造器 | 可以有构造器 |
继承方式 | 一个类可以实现多个接口 | 一个类只能继承一个抽象类 |
适用场景 | 用于不同类之间共享行为规范,适合多重继承的需求 | 用于创建共享功能的基类,适合类之间有共同的特性时 |
接口和抽象类各自有其优势,选择使用哪个,主要取决于具体需求。如果类之间有公共行为需要实现,并且可能会涉及多重继承,则接口是更好的选择;如果有一些共享的行为需要提供,同时需要约束子类的行为,抽象类更为合适。
Java碎碎念 文章被收录于专栏
来一杯咖啡,聊聊Java的碎碎念呀