抽象类和接口的区别
方面 | 抽象类 | 接口 |
---|---|---|
结构组成 | 普通类+抽象方法 | 抽象方法+静态常量(final static) |
权限 | 各种权限 | 默认public |
子类使用 | 使用extends关键字继承抽象类 | 使用implements关键字实现接口 |
关系 | 一个抽象类可以实现若干接口 | 接口不能继承抽象类,但是接口可以使用extends关键字继承多个父接口 |
子类限制 | 一个子类只能继承一个抽象类 | 一个子类可以实现多个接口 |
- 抽象类和接口都是 Java 中多态的常见使用方式。
- 核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法。
抽象类的使用
abstract class Test{
abstract public void example();
}
在 example方法前加上 abstract 关键字,表示这是一个抽象方法。同时抽象方法没有方法体(没有 { },不能执行具体代码)。
对于包含抽象方法的类,必须加上 abstract 关键字表示这是一个抽象类。
- 抽象类不能直接实例化。
Test test= new Test();
// 错误
'JavaTest' 为 abstract;无法实例化
- 抽象方法不能使用 private 关键字。
abstract class JavaTest{
abstract private void example();
}
//错误
非法的修饰符组合: 'abstract' 和'private'
- 抽象类中可以包含其他的非抽象方法,也可以包含字段。这个非抽象方法和普通方法的规则都是一样的,可以被重写,也可以被子类直接调用。
public class Test {
public static void main(String[] args) {
Bird bird = new Bird("小鸟");
System.out.println(bird.name);
bird.live();
}
}
abstract class Animal {
String name;
abstract void live();
public Animal(String name) {
this.name = name;
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
@Override
void live() {
System.out.println("我有生命!");
}
}
//运行结果
小鸟
我有生命!
接口的使用
在上面的例子中,可以将Animal设计为一个接口
public class Test {
public static void main(String[] args) {
Bird bird = new Bird();
bird.live();
}
}
interface Animal{
void live();
}
class Bird implements Animal {
@Override
public void live() {
System.out.println("我有生命!");
}
}
//运行结果
我有生命!
- 使用 interface 定义一个接口
- 接口中的方法一定是抽象方法,因此可以省略 abstract
- 接口中的方法一定是 public,因此可以省略 public
- 使用 implements 继承接口。此时表达的含义不再是 “扩展”,而是 “实现”,一个类可以实现多个接口
- 在调用的时候同样可以创建一个接口的引用,对应到一个子类的实例.
- 接口不能单独被实例化
扩展(extends) vs 实现(implements)
扩展指的是当前已经有一定的功能了,进一步扩充功能.
实现指的是当前啥都没有,需要从头构造出来.
先继承父类,再实现接口,可以实现多个接口,但只能直接继承一个父类
class Animal {
String name;
}
interface Live {
void live();
}
interface Run {
void run();
}
//继承Animal类,实现Live和Run接口
class Bird extends Animal implements Live, Run {
@Override
public void live() {
System.out.println("我有生命!");
}
@Override
public void run() {
System.out.println("我会跑!");
}
}
抽象类存在的意义是为了让编译器更好的校验,像 Animal 这样的类我们并不会直接使用,而是使用它的子类。万一不小心创建了 Animal 的实例,编译器会及时提醒我们。