Java中的抽象类和接口

这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战

抽象类

抽象类不允许被实例化,只能被继承,所以我们不能通过 new 来创建抽象类的实例。

抽象类可以包含属性和方法,方法必须要有抽象方法,只有含有抽象方法的类才叫做抽象类。

子类继承抽象类,必须实现抽象类中所有的抽象方法。

接口

抽象类本身也是类,只不过是更加抽象的类,而接口就更加牛逼了,比抽象类还要抽象,很多地方都会说抽象类和接口的区别在于 is-a 和 has-a 的区别,大家自己感受一下即可。

接口可以理解为是一种协议,一种标准,我只负责定义,不负责实现,然而在 Java 8 接口中也能定义 default 的方法了。

像我们常见用 repository 层,就会定义一套接口,而 service 层在使用的时候不需要知道具体的实现是怎么样的,底层的数据库是 MySQL,Oracle ,甚至是 Redis 这些都不重要。

抽象类和接口的使用

抽象类在使用的时候需要注意,因为所有的子类都需要实现其所有的抽象方法,所以在定义的时候,一定要保证是父子类的关系,且抽象类中的方法不易过多,而接口的定义,就是为了定义某种标准,可能是数据交互的标准,子类需要实现接口就代表子类拥有了某种能力。

Java 中虽然不支持多重继承,但是我们可以通过多重实现来达到相似的功能。抽象类多用于模板方法,对模板中某一部分进行抽象,而接口则是完全的抽象。

抽象类的应用 - 模板设计模式

模板设计模式就是指固定的套路 。假如你有如下需求 ,计算代码的运行时间 。套路就是记录开始时间 ,运行方法 ,记录结束时间 ,最后一减得出结果 。可以像这样实现 。

public abstract class SupClass {
​
    public abstract void run();
​
    //这就是一个模板,限定为 final 防止子类重写该方法。
    public final void calculateTime(){
        long l1 = System.currentTimeMillis();
        run();
        long l2 = System.currentTimeMillis();
​
        long time = l2 - l1;
        System.out.println("运行时间为 " + time + " 毫秒。");
    }
}
​
​
​
public class Sub1Class extends SupClass{
​
    @Override
    public void run() {
        System.out.println("在计算 Sub1Class 的 run 方法执行时间。。。");
        long sum = 0;
        for (int i = 0; i < 1000000000; i++) {
            sum = sum + i;
        }
    }
}
​
​
​
public class Sub2Class extends SupClass{
​
    @Override
    public void run() {
        System.out.println("在计算 Sub2Class 的 run 方法执行时间。。。");
        long sum = 0;
        for (int i = 0; i < 10000000; i++) {
            sum = sum + i;
        }
    }
}
​
​
// 测试代码
public class TemplateTest {
​
    public static void main(String[] args) {
        SupClass class1 = new Sub1Class();
        class1.calculateTime();
​
        SupClass class2 = new Sub2Class();
        class2.calculateTime();
​
    }
}
复制代码

接口的应用 - 工厂模式

在工厂模式中 ,我们在创建对象时不会对客户端暴露创建逻辑 ,并且是通过使用一个共同的接口( Car )来指向新创建的对象 。这就是一种创建类的方法 。主要体现在工厂中获取对象的方法的内部逻辑 。像下面示例中的 getCar 方法 。

public interface Car {
    void run();
}
​
public class BaoMa implements Car{
​
    @Override
    public void run() {
        System.out.println("我在 BaoMa 里笑。");
    }
​
}
​
public class BigCar implements Car{
​
    @Override
    public void run() {
        System.out.println("我是 BigCar ,我不怕撞!");
    }
​
}
​
public class SmallCar implements Car {
​
    @Override
    public void run() {
        System.out.println("我是 SmallCar ,看起来精致!");
    }
​
}
​
/**
 * 工厂决定以何种形式创建对象,为什么叫工厂,也就是因为,对象如何产生是在这里决定的。
 *
 */
public class CarFactory {
​
    // 这是重点呀,返回的都是同一个接口。这也是多态的体现 向上转型。
    public Car getCar(String type){
          if(type == null){
             return null;
          }        
          if(type.equalsIgnoreCase("SMALLCAR")){
             return new SmallCar();
          } else if(type.equalsIgnoreCase("BIGCAR")){
             return new BigCar();
          } else if(type.equalsIgnoreCase("BAOMA")){
             return new BaoMa();
          }
          return null;
       }
}
​
// 测试类
public class FactoryTest {
​
    public static void main(String[] args) {
​
        CarFactory factory = new CarFactory();
​
        Car car = factory.getCar("smallcar");
        car.run();
​
        Car car2 = factory.getCar("bigcar");
        car2.run();
​
        Car car3 = factory.getCar("baoma");
        car3.run();
    }
}
​
我是 SmallCar ,看起来精致!
我是 BigCar ,我不怕撞!
我在 BaoMa 里笑。
复制代码

总结

抽象类和接口的区别