从零开始学Java:第11章 继承、多态与抽象类
第11章 继承、多态与抽象类前面我们学了类、对象和封装。现在的问题是如果多个类有共同特征怎么办比如系统里有不同员工普通员工。经理。销售。他们都有姓名、工号、基础工资也都有计算工资的能力。但不同员工计算工资的方式可能不同。如果完全分开写会有重复publicclassDeveloper{privateStringid;privateStringname;privateintbaseSalary;}publicclassManager{privateStringid;privateStringname;privateintbaseSalary;}继承就是为了解决一类“共同特征 不同行为”的问题。一、继承的基本写法父类publicclassEmployee{privatefinalStringid;privatefinalStringname;protectedintbaseSalary;publicEmployee(Stringid,Stringname,intbaseSalary){this.idid;this.namename;this.baseSalarybaseSalary;}publicStringgetId(){returnid;}publicStringgetName(){returnname;}publicintcalculateSalary(){returnbaseSalary;}}子类publicclassManagerextendsEmployee{privatefinalintbonus;publicManager(Stringid,Stringname,intbaseSalary,intbonus){super(id,name,baseSalary);this.bonusbonus;}OverridepublicintcalculateSalary(){returnbaseSalarybonus;}}extends表示继承。Manager是Employee的子类。super(...)调用父类构造方法。二、继承到底继承了什么子类会继承父类可访问的方法和字段。private 字段不会被子类直接访问但仍然属于对象的一部分。ManagermanagernewManager(M001,小王,10000,3000);System.out.println(manager.getName());System.out.println(manager.calculateSalary());getName来自父类calculateSalary被子类重写。三、protected给子类访问的边界父类中protectedintbaseSalary;protected允许子类访问。但不要滥用 protected。字段暴露给子类后子类也可能破坏父类规则。更稳的方式是父类提供受控方法protectedintgetBaseSalary(){returnbaseSalary;}入门阶段先知道 protected 是一种访问控制但实际设计中要谨慎使用。四、方法重写子类可以重写父类方法OverridepublicintcalculateSalary(){returnbaseSalarybonus;}Override建议总是写。它能让编译器帮你检查是否真的重写了父类方法。如果你写错方法名OverridepublicintcalcSalary(){returnbaseSalarybonus;}编译器会报错因为父类没有这个方法。没有Override错误可能悄悄存在程序不会按你以为的方式运行。五、多态父类引用指向子类对象EmployeeemployeenewManager(M001,小王,10000,3000);System.out.println(employee.calculateSalary());变量类型是Employee实际对象是Manager。调用calculateSalary时会执行 Manager 重写后的方法。这就是多态。多态的价值是调用方只依赖父类不关心具体子类。publicstaticvoidprintSalary(Employeeemployee){System.out.println(employee.getName()工资employee.calculateSalary());}可以传 Developer也可以传 Manager。printSalary(newEmployee(E001,小李,8000));printSalary(newManager(M001,小王,10000,3000));调用方不需要写一堆 if 判断类型。六、动态绑定EmployeeemployeenewManager(M001,小王,10000,3000);employee.calculateSalary();编译时看变量类型是 Employee运行时看实际对象是 Manager。真正执行哪个方法由运行时对象决定。Employee引用Manager对象执行Manager.calculateSalary这就是多态背后的直觉。七、抽象类父类只定义共同规则如果 Employee 本身不应该直接创建而只是所有员工的抽象概念可以写抽象类。publicabstractclassEmployee{privatefinalStringid;privatefinalStringname;publicEmployee(Stringid,Stringname){this.idid;this.namename;}publicStringgetName(){returnname;}publicabstractintcalculateSalary();}抽象方法没有方法体publicabstractintcalculateSalary();子类必须实现publicclassDeveloperextendsEmployee{privatefinalintmonthlySalary;publicDeveloper(Stringid,Stringname,intmonthlySalary){super(id,name);this.monthlySalarymonthlySalary;}OverridepublicintcalculateSalary(){returnmonthlySalary;}}抽象类不能直接 new// new Employee(E001, Tom); // 错误八、继承适合什么场景继承适合明显的“是一个”关系Manager 是 Employee Developer 是 Employee Dog 是 Animal Circle 是 Shape如果关系不是“是一个”不要硬继承。错误例子Car 继承 Engine车不是发动机。车拥有发动机。这应该用组合。九、继承的风险继承不是越多越好。风险包括父类改动影响所有子类。子类可能依赖父类内部细节。层级太深后难理解。为了复用几行代码硬继承会让模型变形。比如BaseActivity - LoginActivity - VipLoginActivity - SpecialVipLoginActivity层级越深行为越难判断。所以后面会讲“组合优于继承”。十、本章实战图形面积计算抽象类publicabstractclassShape{publicabstractdoublearea();}圆形publicclassCircleextendsShape{privatefinaldoubleradius;publicCircle(doubleradius){this.radiusradius;}Overridepublicdoublearea(){returnMath.PI*radius*radius;}}矩形publicclassRectangleextendsShape{privatefinaldoublewidth;privatefinaldoubleheight;publicRectangle(doublewidth,doubleheight){this.widthwidth;this.heightheight;}Overridepublicdoublearea(){returnwidth*height;}}使用多态publicclassShapeApp{publicstaticvoidmain(String[]args){Shape[]shapes{newCircle(3),newRectangle(4,5)};for(Shapeshape:shapes){System.out.println(shape.area());}}}调用方只关心 Shape 有 area 能力不关心具体是圆还是矩形。十一、常见错误1. 为了复用字段硬继承不是“有相同字段”就该继承。要看是不是“是一个”的关系。2. 忘记 super子类构造方法需要调用父类构造方法。3. 重写方法不写 Override容易因为拼写错误导致没有真正重写。4. 父类设计太重父类放太多逻辑所有子类都被迫继承。5. 把继承当唯一复用方式很多复用更适合组合。十二、本章练习定义抽象类Animal包含makeSound()。定义Dog、Cat继承 Animal实现不同声音。写方法printSound(Animal animal)传入不同动物。定义抽象类Payment包含pay(int amountCent)。思考手机拥有电池应该是继承还是组合十三、本章总结你需要掌握继承用extends。子类可以复用父类可访问方法。super调用父类构造方法。子类可以重写父类方法。Override建议总是写。多态是父类引用指向子类对象。实际执行哪个方法由运行时对象决定。抽象类可以定义共同规则。继承适合“是一个”关系。继承不能滥用组合经常更稳。