java面向对象三大核心特性是封装、继承与多态,封装本质是控制变化与隐藏实现细节(如银行账户需通过deposit/withdraw校验逻辑而非直接改balance),继承体现is-a关系并支持代码复用,多态遵循“编译看左、运行看右”原则,依托jvm虚方法表动态绑定;接口定义能力契约(what),抽象类提供部分实现骨架(what+partial how);设计上优先组合优于继承。

Java面向对象是面试必考模块,但很多候选人答得泛泛而谈,容易踩中几个高频痛点:概念堆砌、混淆封装与访问修饰符、说不清多态的运行机制、分不清接口和抽象类的真实边界。关键不是背定义,而是讲清“为什么这么设计”和“不这么写会出什么问题”。
封装不是加个private就完事了
很多人一说封装就是“属性私有+getter/setter”,这其实只是表象。真正的封装核心是控制变化、隐藏实现细节、保证数据一致性。
- 比如一个银行账户余额,不能直接暴露
balance字段并允许任意赋值;应该提供withdraw()和deposit()方法,在内部校验金额合法性、记录日志、触发通知等 - 如果用public字段,外部代码可能绕过业务规则直接写
account.balance = -1000,系统就失控了 - 封装还体现在“不暴露不该暴露的”——像集合类的内部数组,应返回不可修改副本(
Collections.unmodifiableList()),而不是原始引用
多态必须讲清“编译看左,运行看右”
这是面试官最爱追问的点。光说“父类引用指向子类对象”不够,要能解释JVM底层怎么执行。
- 编译阶段只检查引用类型(左边)有没有该方法声明,不关心实际对象是谁
- 运行阶段才根据实际对象类型(右边)去方法区找对应的方法入口(虚方法表vtable查找)
- 举例:
List list = new ArrayList();编译时只认List接口定义的方法;运行时调用的是ArrayList里重写的add()逻辑,哪怕后面换成LinkedList也不影响上层代码 - 如果子类没重写方法,就沿用父类实现;若父类是抽象方法,则必须由子类实现——这就是扩展性和强制契约的结合
接口 vs 抽象类别再只答“多实现vs单继承”
这个区别背后是设计意图的根本差异。面试官想听你是否理解“契约”和“骨架”的分工。
- 接口定义能做什么(what),强调能力组合。比如一个类既要可序列化(
Serializable),又要可比较(Comparable),就得同时实现多个接口 - 抽象类定义是什么+怎么做一部分(what + partial how),强调类族共性。比如
AbstractList已实现了size()isEmpty()等通用逻辑,子类只需专注get(int)和set(int, E) - Java 8之后接口可以有default方法,但本质仍是契约——default方法是“兜底实现”,不能替代抽象类里的状态管理和复杂初始化逻辑
- 选型建议:描述行为用接口(如
Runnable),描述事物本质且有共享代码用抽象类(如Animal)
继承滥用是真实隐患,得主动规避
很多候选人把继承当万能钥匙,但面试官更看重你对“组合优于继承”原则的理解。
- 继承代表强is-a关系(
Dog extends Animal合理),但Stack extends Vector(JDK早期设计)就是反模式——栈不是向量,只是用到了向量的部分能力 - 一旦父类修改,所有子类都可能被动受影响;而组合关系(
Stack has-a Vector)让依赖更可控 - Spring框架大量使用组合:比如
ApplicationContext不继承BeanFactory,而是持有它;JdbcTemplate内部组合DataSource而非继承 - 回答时可补充:“我写新模块时,优先考虑接口+组合;只有确定存在清晰的‘是一种’且需要复用大量代码时,才谨慎用继承”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











