抽象类
2020年8月6日大约 3 分钟约 912 字
1. 背景
某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确地知道这些子类如何实现这些方法。例如:
- 如定义了一个
Shape
类,这个类应该提供一个计算周长的方法calPerimeter()
,但不同Shape
子类对周长的计算方法是不一样的,即Shape
类无法准确地知道其子类计算周长的方法。 - 既然如此,那是不是可以直接让
Shape
类不管它,即不提供calPerimeter()
方法呢?这不是一个好思路:假设有一个Shape
引用变量,该变量实际上引用到Shape
子类的实例,那么这个Shape
变量就无法调用calPerimeter()
,必须将其强制类型转换为其子类类型,即使用多态,才可调用calPeriemter()
方法,这降低了程序的灵活性。 - 如何既让
Shape
类里包含calPerimeter()
方法,又无须提供其方法实现呢?使用抽象方法即可满足该要求:抽象方法是只有方法签名、没有方法实现的方法。
2. 抽象类与抽象方法
抽象类和抽象方法:
- 抽象类/方法必须使用
abstract
修饰,且抽象方法不能有方法体; - 有抽象方法的类只能被定义成抽象类,但抽象类却可以没有抽象方法;
- 抽象类不能被实例化:无法使用
new
调用抽象类的构造器来创建抽象类的实例; - 抽象类中定义的构造器不能用于创建实例,主要是用于被其子类调用;
定义抽象方法只需在普通方法上增加abstract
修饰符,并把普通方法的方法体全部去掉,然后增加一个分号;
即可。如下:
抽象方法:
public abstract void test();
普通方法:
public void test() {}
abstract
不能用于修饰构造器,也不能修饰成员变量、局部变量,即不存在这些概念的抽象版。
抽象类和抽象方法可以更好地发挥多态的优势,使得程序更加灵活。
3. 和abstract冲突的关键字
关键字 | 冲突原因 |
---|---|
final | 当使用abstract 修饰类/方法时,表明这个类/方法只能被继承/重写,而final 的作用恰好相反,因此,final 和abstract 永远不能同时使用 |
private | 同 final |
static | static 修饰的方法表明这个方法属于该类本身,即通过类即可调用该方法,因而若一个static 方法被定义成abstract 时,将导致通过该类来调用该方法时出现错误(调用了一个没有方法体的方法),因此static 和abstract 不能同时修饰某个方法,即没有所谓的“类抽象方法” |
不过
static
和abstract
不是绝对互斥的,它们可以同时修饰内部类。
4. 抽象类体现的模板模式的思想
抽象类体现的是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为方式。
抽象类:
public abstract class SpeedMeter { // 转速 private double turnRate; public SpeedMeter() { } // 计算周长 public abstract double calGirth(); public void setTurnRate(double turnRate) { this.turnRate = turnRate; } // 定义计算速度的通用算法:速度=周长*转速 public double getSpeed() { return calGirth() * turnRate; } }
子类:
public class CarSpeedMeter extends SpeedMeter { private double radius; public CarSpeedMeter(double radius) { this.radius = radius; } public double calGirth() { return radius * 2 * Math.PI; } public static void main(String[] args) { CarSpeedMeter csm = new CarSpeedMeter(0.34); csm.setTurnRate(15); System.out.println(csm.getSpeed()); } }