抽象类与接口的区别



0 写在前面

抽象类和接口有共同点,也有不同点。在实际应用中,我们有时会不知道什么时候用抽象类,什么时候用接口。因此,我们有必要了解抽象类和接口的相关知识以及它们之间的区别,这样以后在二者之间的选择将更有依据。


1 抽象类

在面向对象中,我们通过类来描绘对象,但并不是所有的类都是用来描绘对象的。如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。

抽象类是实现多态的一种机制,抽象类可以包含具体方法,也可以包含抽象方法(由继承它的子类实现这些方法)。

抽象类的特性总结如下:

  1. 抽象类不能被实例化;
  2. 抽象类可以有构造方法;
  3. 抽象方法必须由其子类重写(Override);
  4. 只要包含有一个抽象方法的类,就必须定义为抽象类;
  5. 抽象类可以包含有具体实现的方法,也可以不包含抽象方法;
  6. 子类的抽象方法不能与父类的抽象方法重名;
  7. 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 实现,注意接口并不是类,所以不能实例化。如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法,而且必须实现接口的所有方法。接口更像是一种形式,接口本身不实现任何具体功能。

接口的特性总结如下:

  1. 接口不能有构造方法;
  2. 接口的所有方法自动被声明为 public ,而且只能为 public ;如果使用 protected 、 private 将导致编译错误;
  3. 接口可以定义“成员变量”,并自动转为 public final static ,即常量;
  4. Java 8 之前接口只能有抽象方法,Java 8 之后允许接口有非抽象方法实现,需要用 default 关键字,这种特征又叫做扩展方法;
  5. 实现接口的非抽象类必须实现接口的所有方法,而抽象类不需要;
  6. 不能实例化接口,但可以声明一个接口变量,它必须引用一个实现该接口的类的对象,可以使用 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 抽象类与接口的区别

前面我们分别讲解了抽象类和接口的相关概念、特性,你会发现抽象类和接口还是有所区别的。

抽象类表示一个类中可能已经有一些方法的具体定义,但可能一些方法还未具体定义,需要由其子类去定义;接口则仅仅定义各个方法的界面(方法名、参数列表、返回类型),并不关心方法的具体实现细节。

抽象类与接口的相似之处:

  1. 都不能实例化;
  2. 包含未实现的方法声明(抽象方法);
  3. 派生类必须实现未实现的方法;

抽象类与接口的区别之处:

  1. 一个类可以实现无限个接口,但只能继承一个抽象类;
  2. 抽象类可以存在非抽象方法,而接口不能存在非抽象方法;
  3. 抽象类中的成员变量可被不同的修饰符来修饰,而接口中的成员变量默认为静态变量;
  4. 抽象类是对象的抽象,而接口是一种行为规范。


4 抽象类与接口的选择

  1. 如果想要实现多重继承,则必须选择接口。因为 Java 不支持类的多重继承。
  2. 如果对某些方法需要其有默认实现,则选择抽象类;因为抽象类中可以包含非抽象方法。
  3. 如果功能变动频繁,则选择抽象类。因为如果选择接口,一次改动可能需要改动所有实现了该接口的类。


参考资料:


 
comments powered by Disqus