0 写在前面
抽象类和接口有共同点,也有不同点。在实际应用中,我们有时会不知道什么时候用抽象类,什么时候用接口。因此,我们有必要了解抽象类和接口的相关知识以及它们之间的区别,这样以后在二者之间的选择将更有依据。
1 抽象类
在面向对象中,我们通过类来描绘对象,但并不是所有的类都是用来描绘对象的。如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类是实现多态的一种机制,抽象类可以包含具体方法,也可以包含抽象方法(由继承它的子类实现这些方法)。
抽象类的特性总结如下:
- 抽象类不能被实例化;
- 抽象类可以有构造方法;
- 抽象方法必须由其子类重写(Override);
- 只要包含有一个抽象方法的类,就必须定义为抽象类;
- 抽象类可以包含有具体实现的方法,也可以不包含抽象方法;
- abstract 不能与 private 、 static 、 final 、 native 并列修饰同一个方法。
注:关于子类实例化时抽象类是否被实例化的问题
抽象类不能实例化,子类实例化时会初始化父类,因为子类继承了父类的变量、方法等,但初始化不等同于实例化。实例化是指用类创建对象,为对象开辟内存空间。
举个栗子:
Duck 是一个抽象类,里面包含构造方法、抽象方法、具体方法。
GreenHeadDuck 继承自抽象类 Duck ,所以它必须重写抽象类 Duck 的抽象方法 display ,而具体方法 Quack 、 Swim 可以不重写。
/* Duck.java */
public abstract class Duck{ //抽象类Duck
public Duck(){ //构造方法
}
public abstract void display(); //抽象方法:外观
public void Quack(){ //具体方法:嘎嘎叫
System.out.println("--gaga--");
}
public void Swim(){ //具体方法:游泳
System.out.println("--swimming--");
}
}
/* GreenHeadDuck.java */
public class GreenHeadDuck extends Duck{ //GreenHeadDuck继承自抽象类Duck
@Override
public void display(){ //重写父类的抽象方法
System.out.println("**GreenHeadDuck**"); //我是绿头鸭
}
}
2 接口
接口是抽象类的延伸,不同于 C++ , Java 是不允许类的多重继承的(即不能有多个父类),通过接口可以解决 Java 中的多继承问题。
接口在 Java 中用关键字 interface 实现,注意接口并不是类,所以不能实例化。如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法,而且必须实现接口的所有方法。接口更像是一种形式,接口本身不实现任何具体功能。
接口的特性总结如下:
- 接口不能有构造方法;
- 接口的所有方法自动被声明为 public ,而且只能为 public ;如果使用 protected 、 private 将导致编译错误;
- 接口可以定义“成员变量”,并自动转为 public final static ,即常量;
- Java 8 之前接口只能有抽象方法,Java 8 之后允许接口有非抽象方法实现,需要用 default 关键字,这种特征又叫做扩展方法;
- 实现接口的非抽象类必须实现接口的所有方法,而抽象类不需要;
- 不能实例化接口,但可以声明一个接口变量,它必须引用一个实现该接口的类的对象,可以使用 instanceOf 来判断一个类是否实现了某个接口,如:
if(object instanceOf ClassName){
doSth();
}
举个栗子:
Behavior 为我们定义的一个接口,里面没有构造方法,只有抽象方法,也没有具体实现的方法。
类 FlyBehavior 实现了接口 Behavior ,所以它必须实现接口 Behavior 的所有方法,如方法 fly 。
/* FlyBehavior.java */
public interface Behavior { //接口Behavior
void fly();
}
public class FlyBehavior implements Behavior { //FlyBehavior类实现接口Behavior
@Override
public void fly() { //实现接口中的方法fly
System.out.println("--GoodFly--");
}
}
注:Java 8 新特性:接口允许有非抽象方法。
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
3 抽象类与接口的区别
前面我们分别讲解了抽象类和接口的相关概念、特性,你会发现抽象类和接口还是有所区别的。
抽象类表示一个类中可能已经有一些方法的具体定义,但可能一些方法还未具体定义,需要由其子类去定义;接口则仅仅定义各个方法的界面(方法名、参数列表、返回类型),并不关心方法的具体实现细节。
抽象类与接口的相似之处:
- 都不能实例化;
- 包含未实现的方法声明(抽象方法);
- 派生类必须实现未实现的方法;
抽象类与接口的区别之处:
- 一个类可以实现无限个接口,但只能继承一个抽象类;
- 抽象类可以存在非抽象方法,而接口不能存在非抽象方法;
- 抽象类中的成员变量可被不同的修饰符来修饰,而接口中的成员变量默认为静态变量;
- 抽象类是对象的抽象,而接口是一种行为规范。
4 抽象类与接口的选择
- 如果想要实现多重继承,则必须选择接口。因为 Java 不支持类的多重继承。
- 如果对某些方法需要其有默认实现,则选择抽象类;因为抽象类中可以包含非抽象方法。
- 如果功能变动频繁,则选择抽象类。因为如果选择接口,一次改动可能需要改动所有实现了该接口的类。
参考资料: