Java 中的抽象类和接口

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

抽象类

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

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

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

接口

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

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

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

抽象类和接口的使用

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
csharp复制代码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 方法 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
typescript复制代码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 里笑。

总结

抽象类和接口的区别

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%