抽象类(abstract class)和接口(interface)有什么异同?什么时候应该用抽象类,什么是时候应该用接口?

抽象类与其子类是是不是的关系,如鸟是动物,动物是生物。

接口与其实现类是能不能的关系,如接口【飞】,飞机能飞,鸟也能飞。

抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部都是抽象方法。抽象类中的成员可以是private、默认、protected、public的,而接口中的成员全都是public的。抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。

什么时候应该用抽象类,什么是时候应该用接口?

接口适合用于对行为作出定义,在复用性不高的场景下,接口可以满足需要,在复用性高的场景,抽象类可以用来降低接口的实现难度,隔离接口的实现过程。将公共的方法抽象到抽象类中作实现。

举一个例子:

有一个车辆移动接口vehicleMove(),里面有存储能量、点火、移动3个方法。

public interface vehicleMove {

    //存储能量
    void refueling();

    //点火
    void ignition();

    //移动
    void move();

}

现在我们有两台汽油车法拉利、手扶式拖拉机。两台电动车特斯拉,蔚来(充电方式为更换整个电池组),我们要实现所有车的移动。

假设我们直接通过实现接口的方式去解决:

//特斯拉
public class Tesla implements vehicleMove{
    @Override
    public void refueling() {
        System.out.println("充电桩充电");
    }
    @Override
    public void ignition() {
        System.out.println("启动电机");
    }
    @Override
    public void move() {
        System.out.println("电机运转,带动车辆");
    }
}
//蔚来
public class NIO implements vehicleMove {
    @Override
    public void refueling() {
        System.out.println("更换电池组");
    }
    @Override
    public void ignition() {
        System.out.println("启动电机");
    }
    @Override
    public void move() {
        System.out.println("电机运转,带动车辆");
    }

}
//法拉利
public class Ferrari implements vehicleMove{
    @Override
    public void refueling() {
        System.out.println("加汽油");
    }

    @Override
    public void ignition() {
        System.out.println("电子火花塞点火,启动内燃机");
    }

    @Override
    public void move() {
        System.out.println("内燃机运转,带动车辆");
    }
}
//拖拉机
public class Tractor implements vehicleMove{
    @Override
    public void refueling() {
        System.out.println("加汽油");
    }

    @Override
    public void ignition() {
        System.out.println("手摇式点火,启动内燃机。");
    }

    @Override
    public void move() {
        System.out.println("内燃机运转,带动车辆");
    }
}

不难发现,在实现移动的过程中,特斯拉与蔚来,仅仅充电方式不一样,拖拉机与法拉利,仅仅点火方式不一样。此外,在正常的项目中,我们一个接口的方法一般都是十几个或者几十个,那么这个时候,直接使用接口就不那么方便了。

如果我们把公共的实现分别抽象出两个抽象类:

//电动车移动抽象类
public abstract class AbstractElectricVehicle implements vehicleMove{
    @Override
    public void ignition() {
        System.out.println("启动电机");
    }

    @Override
    public void move() {
        System.out.println("电机运转,带动车辆");
    }
}
//汽油车移动抽象类
public abstract class AbstractGasolineVehicle implements vehicleMove{
    @Override
    public void refueling() {
        System.out.println("加汽油");
    }
    @Override
    public void move() {
        System.out.println("内燃机运转,带动车辆");
    }
}

那么这时汽车的移动实现变为:

public class Tesla extends AbstractElectricVehicle{
    @Override
    public void refueling() {
        System.out.println("充电桩充电");
    }
}
public class NIO extends AbstractElectricVehicle{
    @Override
    public void refueling() {
        System.out.println("更换电池组");
    }
}
public class Ferrari extends AbstractGasolineVehicle{
    @Override
    public void ignition() {
        System.out.println("电子火花塞点火,启动内燃机");
    }
}
public class Tractor extends AbstractGasolineVehicle{
    @Override
    public void ignition() {
        System.out.println("手摇式点火,启动内燃机。");
    }
}

可以看出,我们把一些公共的实现,放到了抽象类中,而差异化的实现留给了实现类自己去实现。有了这一层抽象类的概念之后,我们代码的可扩展性也更好了,例如我们需要为汽车添加一些其他公共功能,仅需要新写一个接口,让抽象类实现该接口的公共方法即可,而无需修改所有的汽车实现类。

posted @ 2021-06-07 20:25  Vincent_9527  阅读(194)  评论(0)    收藏  举报