开场白
老铁 :接口与抽象类总是会被老铁们一起惦记,昨天我们讨论了接口,那么今天我们就来说说抽象类。那明天了?明天我们就谈谈接口与抽象类的各种比较及其典型应用场景。
什么是抽象类?
继承是面向对象三大特性之一。从继承的层次维度来看,越是顶层的类就越通用,越是底层的类就越具体。想想Java语言中的祖宗级顶层类——Object,它是Java所有类的父类,“会当凌绝顶,一览纵山小”,是不是觉得Object有一种无所不能的超能力?(当然Object不是抽象类 )
越是通用的类,其提供的方法也就越抽象。Java语言提供了定义抽象类的语言特性。所谓抽象类(abstract class)就是一种本身不能被实例的类,但是允许为其类中声明的函数提供实现代码。在这一点上,抽象类与接口有明显的区别,前者可以提供部分实现代码,而后者则不能提供任何实现代码。
下面我们通过代码举例来说明什么是抽象类,先看代码1。
代码1:
//用abstract关键字定义抽象类
abstract class Shape{
private String name;
//部分声明为抽象函数
//具体的业务功能需要继承类实现
public abstract void calArea();
public abstract void draw();
//为部分函数提供代码实现
public String getName(){
return name;
}
}
在代码1中,我们定义了一个抽象类Shape,在该代码中几乎可以包括抽象类的的所有特性,如下所述。
敲黑板
- 抽象类类中包括了两种类型的函数。一种函数带有缺省的代码实现(如上述的getName方法);另外一种函数则无任何实现代码(如calArea、draw方法)。
- 由于抽象类一般不会为所有函数提供完整的实现内容,所以建立其对象实体也无任何意义;为此,抽象类是不能被实例化的。
- 如果有一个类只打算提供部分实现,而将一些功能需要延迟实现,那么就可以采用抽象类技术。
- 抽象类可以提供“模板”的功能。想象一下现实中的模板,一方面在模板中内置了大量可复用的信息,一些个性化信息需要自己填入;另外一方面,模板也为后续信息填入制定了格式规约。类比如下:在抽象类中已经提供实现的方法一般是可以为后续继承类能最大程度复用的代码,当然如果子类不满意,也可以对默认实现进行覆写;对于未被实现的方法,一般是有个性化的、需定制的功能需求,这需要子类自己从业务角度去考虑实现。
- 抽象类中未被实现的函数,前面也需要用abstract进行修饰,这些函数被称为抽象函数;子类继承抽象类后,可以对抽象函数进行实现,如果子类未完成对所有抽象函数的实现,那么该子类仍然是一个抽象类(在类定义时也需要带上abstract的帽子)。
为什么我们需要抽象类?
在回答该问题时,我们先来说一个例子?
代码1中,我们定义了一个抽象类Shape,它的意义在于定义了一个形状这个抽象的概念。我们知道形状是一个很抽象、很笼统的存在。有了这个形状类以后,后面我们就可以定义更加具体的实体类了,比如Circle类(圆)和Rectangle类(矩形)。从我们的常识也知道,计算Circle和计算Rectangle的面积以及绘制它们的方法也是各不相同的。但是,每个形状都会有共同的属性-名字。为此,我们在Shape类中将可通用实现的方法getName进行了实现;而需要继承类个性化实现的方法draw与calArea进行了抽象函数定义,这两个方法需要分别在Circle类与Rectangle中各自实现。
为此,为什么我们要采用抽象类了?
敲黑板
- 抽象类提供了类似“模板”的功能。可提升代码的复用程度,且为继承的子类约定了行为。
- 当我们看到抽象类时,我们就知道有一些抽象方法没有被实现,我们自然会对这些方法进行关注,并且这些抽象方法会被继承的子类所实现。
- 当我们看到一个定义的抽象类引用时,我们自然会联系到相应的代码会用到多态特性。
应用场景
一个父类中的一些函数实现代码同样适用于继承的子类,那么这个父类可被声明为抽象类,在父类中对通用、可复用代码的功能进行实现;而对需要子类个性化实现的功能声明为抽象函数,这些抽象函数留待继承的子类去实现。
转载自公众号:代码荣耀