Java 常用语法极简通关(七):继承、方法重写、抽象类与接口

📅 2026/8/11 14:58:57
Java 常用语法极简通关(七):继承、方法重写、抽象类与接口
开篇先看多种设备统一管理的真实需求井下设备五花八门皮带输送机要监测运料量水泵要监测水位局部通风机要监测风速。如果每种设备都从零写一个类设备台账就得这样管BeltConveyor[] belts ...; WaterPump[] pumps ...; Fan[] fans ...; // 巡检一遍写三个循环。新加一种设备再开一个数组、再写一个循环……噩梦。这一章用继承 接口把这个问题漂亮地解决掉。先看最终版代码——三个设备类、一个抽象父类、一个接口然后一个循环统一巡检全部设备// 1. 抽象父类所有设备的共同骨架 abstract class Device { private String name; private String location; Device(String name, String location) { this.name name; this.location location; } public String getName() { return name; } public String getLocation() { return location; } // 每种设备的自检方式不同父类做不了主交给子类 abstract boolean selfCheck(); // 巡检流程是统一的父类直接写好 void inspect() { System.out.print(【 name 】 location → ); if (selfCheck()) { System.out.println(状态正常); } else { System.out.println(异常生成工单); } } } // 2. 接口能联网上报数据的设备遵守同一规范 interface Reportable { String reportData(); // 上报当前运行数据 } // 3. 三个具体设备类 class BeltConveyor extends Device implements Reportable { private int loadPercent; // 载荷百分比 BeltConveyor(String name, String location, int loadPercent) { super(name, location); this.loadPercent loadPercent; } Override boolean selfCheck() { return loadPercent 90; // 超载 90% 判异常 } Override public String reportData() { return getName() 载荷 loadPercent %; } } class WaterPump extends Device implements Reportable { private double waterLevel; // 水仓水位米 WaterPump(String name, String location, double waterLevel) { super(name, location); this.waterLevel waterLevel; } Override boolean selfCheck() { return waterLevel 2.0; // 水位超 2 米判异常 } Override public String reportData() { return getName() 水位 waterLevel m; } } class Fan extends Device { // 老型号风机不能联网上报 private double windSpeed; Fan(String name, String location, double windSpeed) { super(name, location); this.windSpeed windSpeed; } Override boolean selfCheck() { return windSpeed 0.25; // 风速低于 0.25m/s 判异常规程红线 } }入口类统一巡检public class InspectApp { public static void main(String[] args) { // 父类类型的数组装得下所有子类对象 Device[] devices { new BeltConveyor(3号皮带, 东翼大巷, 75), new WaterPump(中央水泵, -580m泵房, 2.3), new Fan(辅扇2号, 三采区回风巷, 0.1) }; // 一个循环巡检所有设备 for (Device d : devices) { d.inspect(); } // 能联网上报的设备统一收集数据 System.out.println( 数据上报 ); for (Device d : devices) { if (d instanceof Reportable) { Reportable r (Reportable) d; System.out.println(r.reportData()); } } } }输出【3号皮带】东翼大巷 → 状态正常 【中央水泵】-580m泵房 → 异常生成工单 【辅扇2号】三采区回风巷 → 异常生成工单 数据上报 3号皮带 载荷 75% 中央水泵 水位 2.3m四种设备、四种自检逻辑、一个循环搞定新增设备类型零改动——这就是本章三大语法继承、抽象类、接口合力干的事。逐个拆。第一部分继承extends——是一个的关系class BeltConveyor extends Device { ... }extends声明皮带输送机是一个设备。继承带来两件事子类自动拥有父类的成员BeltConveyor 没写 name、location但getName()、inspect()拿来就用子类可以加自己的东西loadPercent 是皮带机特有的字段父类没有。super——子类给父类交差BeltConveyor(String name, String location, int loadPercent) { super(name, location); // 先调用父类构造把父类的字段填好 this.loadPercent loadPercent; }子类对象里装着父类的那部分name、location 在父类里填这部分数据是父类构造方法的职责。super(...)就是显式调用父类构造必须写在子类构造方法的第一行。不写的话编译器会偷偷补一个super()调父类无参构造——但如果父类没有无参构造这个偷偷补就会编译报错这是新手必踩的坑。this指我自己对应的super指我爹的那部分。Java 的单继承规矩一个类只能 extends 一个父类——Java 不允许多继承C 那种一个类继承俩爹的玩法没有。想既有这个能力又有那个能力靠接口一个类可以 implements 多个接口这正是接口存在的意义之一下文细说。第二部分方法重写Override——子类给出自己的实现父类的selfCheck()只说要自检没法说怎么自检——皮带看载荷、水泵看水位各不相同。于是父类把selfCheck()声明成abstract只给方法签名没有方法体意思是这事必须有但怎么办子类说了算子类用重写给出自己的版本Override boolean selfCheck() { return loadPercent 90; }重写的规则方法名、参数列表、返回值类型都和父类相同只是方法体换成子类自己的逻辑。Override 注解——免费的保镖方法上面那行Override是注解作用是让编译器帮你核对这确实是个重写。强烈建议永远写上因为它能拦住一类隐蔽 bugboolean selfcheck() { ... } // 本意重写却把 C 写成小写不写 Override这不是重写是定义了一个新方法父类的抽象方法没实现编译报错这个例子还算幸运但如果父类方法不是抽象的这个错拼方法就成了一个永远不会被调用的新方法编译不报错程序行为诡异能查到怀疑人生。写上 Override编译器直接告诉你没重写任何东西当场现形。重写 vs 重载第六章的约定现在对比重载 Overload重写 Override发生在同一个类里父子类之间方法名相同相同参数列表必须不同必须相同返回值无关相同或子类何时决定调用哪个编译期运行时看实际对象软考爱考这一对两行背下来永不再混。第三部分多态——一个循环巡检所有设备的魔法回看主程序这两行Device[] devices { new BeltConveyor(...), new WaterPump(...), new Fan(...) }; for (Device d : devices) { d.inspect(); }这里藏着 Java 面向对象最核心的机制——多态拆开是两步第一步向上转型。Device d new BeltConveyor(...)——声明的类型是父类实际装的是子类对象。这永远合法皮带机本来就是一个设备。第二步动态绑定。d.inspect()内部调selfCheck()时JVM 在运行时看 d 里实际装的是谁就调用谁的 selfCheck装的是水泵就跑水位判断装的是风机就跑风速判断。声明类型决定能调什么方法编译期按 Device 检查实际对象决定跑哪个实现。多态的价值用一句话说透写循环的人不需要知道有哪些设备类型新增设备类型也不用改循环。Device[]这个数组对一切子类敞开扩展只加类、不改旧代码——这就是著名的开闭原则的语法基础后面所有设计模式都建立在这之上。第四部分抽象类与接口——怎么选抽象类abstract classabstract class Device的两个特征不能 newnew Device(...)编译报错。道理很直白——设备是个抽象概念现场没有一台光设备只有具体的皮带机、水泵可以混搭既有写完的方法inspect所有子类共用又有抽象方法selfCheck子类各自实现还能有字段和构造方法。抽象类的典型角色就是本案例这样定流程骨架 留变化点。inspect 把打印 → 自检 → 给结论的流程焊死变化的部分怎么自检掏成抽象方法留给子类。这个父类定流程、子类填细节的模式有个大名鼎鼎的名字——模板方法模式设计模式的入门第一课。接口interfaceinterface Reportable { String reportData(); }接口比抽象类更纯只规定能力不提供实现接口里的方法默认 public abstract连关键字都能省。它表达的是能做什么而不是是什么皮带机、水泵能联网上报 → implements Reportable老风机不能 → 不实现什么都不影响。一个类可以 implements 多个接口class A implements B, C这弥补了单继承的限制。用接口做能力筛选instanceof 与向下转型主程序的数据上报环节if (d instanceof Reportable) { // 这台设备具备上报能力吗 Reportable r (Reportable) d; // 向下转型转回接口类型 System.out.println(r.reportData()); }instanceof判断一个对象实际是不是某个类型包括接口返回 boolean(Reportable) d向下转型父类引用转子类/接口和基本类型的强转一样有风险——d 实际是 Fan 时强转会抛 ClassCastException。所以标准姿势永远是先 instanceof 探路再强转。抽象类还是接口一句话选型是一个is-a、有共享代码要复用 → 抽象类能做什么can-do、只约定能力 → 接口。皮带机是一个设备且 name、location、巡检流程要共享 → extends Device。能联网上报是一种能力和设备的种类无关以后摄像头、传感器也能上报→ implements Reportable。实际项目里两者经常像本案例一样搭配使用骨架靠抽象类能力靠接口。常踩的坑排雷清单父类只有带参构造子类构造不写 super编译器偷偷补的super()找不到无参父构造编译报错。解法子类构造第一行显式super(...)。重写时方法名拼错、参数没对齐没写 Override 时这就是个静默 bug。养成重写必加 Override 的条件反射。想 new 抽象类或接口都不行。抽象类型只能做声明的类型对象必须 new 具体子类。向下转型不做 instanceofClassCastException 是运行期异常编译不管炸在现场。以为接口能有字段接口里只能有常量public static final自动带不能有普通成员变量要状态用抽象类。重载重写混淆回第三部分那张表再看一眼。本章小结继承extends单继承子类自动获得父类成员构造链靠第一行的super(...)衔接。重写Override父子类同签名运行时动态绑定——多态的发动机注解必写。多态 父类引用装子类对象 运行时按实际对象选方法它是对扩展开放、对修改关闭的语法地基。抽象类不能实例化定骨架留变化点接口纯能力约定可实现多个用 instanceof 强转做能力筛选。选型口诀是一个用继承能做什么用接口。语法主体到这里就齐了。但程序跑起来还会遇到另一个维度的麻烦文件不存在、数字解析失败、网络中断……这些计划外的状况 Java 有一套专门机制应对。下一章异常处理——try-catch-finally、throw 与 throws让你的程序经得起真实环境的折腾。