由于多态的存在,每个子类都可以覆写父类的方法
class Person{public Strnig run(){...}}class Student extends Person{@Overridepublic String run() {....}}class Teacher extends Person{@Overridepublic String run() {...}}
从Person派生的Student和Teacher都可以覆写run方法。
如果父类Person的run方法没有实际意义,能去掉方法的执行语句?
class Person{public void run();//compile error}
答案是不行,会导致编译错误,因为定义方法的时候,必须实现方法的语句。
能不能去掉父类的run()方法?
答案还是不行,因为去掉父类的run() 方法,就失去了多态的特性。例如,runTwice()就无法编译
public void runTwice(Person p){p.run();p.run();}
如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是为了让子类覆写方法,那么,可以把父类的方法声明为抽象方法
class Person{public abstract void run();}
把一个方法声明为abstract,表示它是一个抽象方法,本身没有实现任何方法语句。因为这个抽象方法本身是无法执行的。所以,Person类也无法被实例化。编译器会告诉我们,无法编译Person类,因为它包含抽象方法。
必须把Person类也声明为abstract,才能正确编译它。
abstract class Person{public abstract void run ();}
抽象类
如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。
因为无法执行抽象方法,所以这个类也必须申明为抽象类。
使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类。Person p = new Person();
无法实例化的抽象类有什么用?
因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
例如,Person类定义了抽象方法run(),那么实现子类的时候,就必须覆写run()方法
public class Main{public static void main(String[] args){Person p = new Student();p.run();}}abstract class Person{public abstract void run();}class Student extends Person{@Overridepublic void run(){System.out.println("student run");}}
面向抽象编程
当我们定义了抽象类Person,以及具体的Student、Teacher子类的时候,我们可以通过抽象类Person类型引用具体的子类的实例Person s = new Student();Person t = new Teacher();
这种引用的好处在于,我们对其进行方法调用的时候,并不关心Person类型变量的具体子类
同样的代码,如果引用的是一个新的子类,我们仍不关心具体类型Person e = new Employee();e.run();
这种尽量引用高层类型,避免引用实际子类类型的方式。称之为面向抽象编程。
面向抽象编程的本质就是
- 上层代码只定义规范
- 不需要子类就可以实现业务逻辑
- 具体的业务逻辑由不同的子类实现,调用者并不关心。
练习:
用抽象类给一个有工资收入和稿费收入的小伙伴算税。
//
public class Main{public static void main(String[] args){Income[] incomes = {new SalaryIncome(3000),new StateCouncilSpecialAllowance(12000)};System.out.println(totalTax(incomes));}public static double totalTax(Income[] incomes) {double total = 0;for(Income income:incomes){total += income.getTax();}return total;}}abstract class Income{protected double income;public Income(double income){this.income = income;}public abstract double getTax();}class SalaryIncome extends Income{public SalaryIncome(double income) {super(income);}@Overridepublic double getTax() {if(income <= 5000) {return 0;}return (income - 5000) * 0.2;}}class StateCouncilSpecialAllowance extends Income{public StateCouncilSpecialAllowance(double income) {super(income);}@Overridepublic double getTax() {return income * 0.2;}}
小结:
- 通过
abstract定义的方法是抽象方法,它只有定义,没有实现。抽象方法定义了子类必须实现的接口规范 - 定义了抽象方法的class必须被定义为抽象类,从抽象类继承的子类必须实现抽象方法
- 如果不实现抽象方法,则该子类仍是一个抽象类。
- 面向抽象编程使得调用者只关心抽象方法的定义,不关心子类的具体实现。
