1. 概念和特点
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
值得注意的是,一个抽象类不能直接实例化,但类的其他功能依然存在;既然不能被实例化,那么它必须被继承才能被使用。
2. 为什么需要抽象类
当某个父类只知道其子类应该包含什么方法,但不知道子类如何实现这些方法的时候,抽象类就派上用场了。使用抽象类还有一个好处,类的使用者在创建对象时,就知道他必须要使用某个具体子类,而不会误用抽象的父类,因此对于使用者来说,有一个提示作用。
例如,父类 Pet
被子类 Cat
和 Dog
继承,并且都重写了父类的 eat
方法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
小猫类、小狗类这些子类都重写了宠物类中的 eat
方法,我们知道每种宠物都有吃的行为,宠物表示了一个抽象的概念。那么宠物类的实例化和方法调用就没有了实际意义:
- 1
- 2
我们知道了抽象类不能被实例化,此时可以将父类设定为抽象类,使用 abstract
关键字来声明抽象类,abstract
关键字必须放在 class
关键字前面 :
- 1
- 2
- 3
- 4
如果你尝试实例化抽象类 Pet
,编译器将会报错:
- 1
- 2
- 3
- 4
使用抽象类,我们既可以通过父类和子类的继承关系,来限制子类的设计随意性,也可以避免父类的无意义实例化。
3. 抽象方法
抽象类中可以包含抽象方法,它是没有具体实现的方法。换句话说,与普通方法不同的是,抽象方法没有用 {}
包含的方法体。
抽象类可以定义一个完整的编程接口,从而为子类提供实现该编程接口所需的所有方法的方法声明。抽象类可以只声明方法,而不关心这些方法的具体实现,而子类必须去实现这些方法。
上面我们将 Pet
父类改为了抽象类,其中包含了 eat
方法的具体实现,而实际上这个方法是不需要具体实现的,每种宠物都有各自的具体实现。此时,就可以将 eat
方法改为抽象方法:
- 1
- 2
- 3
我们可以看到抽象方法使用 abstract
关键字声明,它没有方法体,而直接使用 ;
结尾。
子类必须实现父类中的抽象方法,假如 Dog
类继承了抽象类 Pet
,但没有实现其抽象方法 eat
:
- 1
- 2
编译执行代码,将会报错:
- 1
- 2
- 3
- 4
上述报错中可知,如果我们不想在 Dog
中重写抽象方法 eat()
,那么可以将 Dog
也改为抽象类:
- 1
- 2
抽象方法不能是 final
、static
和 native
的;并且抽象方法不能是私有的,即不能用 private
修饰。因为抽象方法一定要被子类重写,私有方法、最终方法以及静态方法都不可以被重写,因此抽象方法不能使用 private
、final
以及 static
关键字修饰。而 native
是本地方法,它与抽象方法不同的是,它把具体的实现移交给了本地系统的函数库,没有把实现交给子类,因此和 abstract
方法本身就是冲突的。
4. 小结
可以使用 abstract
关键字声明抽象类或抽象方法。
抽象类不能被实例化,抽象类中的方法必须被非抽象子类实现,它必须被继承才能被使用。
抽象类中不一定包含抽象方法,但抽象方法一定在抽象类中。