java面向对象核心是封装、继承、多态和抽象,封装通过访问控制实现边界管控与可维护性,继承用于“is-a”关系下的代码复用,多态依赖父类引用+子类重写实现运行时动态绑定,抽象类侧重“是什么”,接口侧重“能做什么”。

Java面向对象面试题答得准,关键不是背定义,而是讲清楚“为什么这么设计”和“用在哪儿”。光说“封装就是private加getter/setter”容易被追问:那不加private行不行?加了又有什么实际好处?下面分四块说透核心问题。
封装:不是为了藏,是为了可控
封装的本质是划定边界——哪些能改、谁可以改、改的时候要经过什么检查。比如一个账户余额字段设为private,不是怕别人看见,而是防止直接赋值导致负数或超限。真正体现封装价值的,是配合setter做校验:
- 在setBalance()里判断金额是否为正、是否超过信用额度
- 修改属性时自动记录日志或触发事件(如余额变动通知)
- 后续想加审计功能,只需改setter,调用方代码完全不动
如果所有字段都public,任何地方都能随意赋值,出问题根本没法追溯源头。
继承:只在“is-a”成立时才用
别一上来就extends。先问自己:子类真的是父类的一种吗?比如“鸵鸟”继承“鸟”,但不会飞——这就违反里氏替换原则。真正适合继承的场景是:
- 共性明确且稳定:比如“支付方式”抽象出统一的pay()方法,微信支付、支付宝各自实现
- 需要复用大量已有逻辑:数据库连接池中GenericObjectPool作为基类,提供回收、验证等通用流程
- 框架扩展点:Spring的InitializingBean、DisposableBean就是典型模板式继承
多数情况下,优先考虑组合(has-a)而非继承。比如“汽车”有“发动机”,而不是“汽车”继承“发动机”。
多态:靠的是“父类引用 + 子类对象 + 方法重写”三件套
面试官常考运行时多态,答案不能只写“同一个方法不同表现”。要说明触发条件:
- 必须有继承或实现关系(Animal a = new Dog() 或 List list = new ArrayList())
- 子类重写了父类/接口中的非static、非private、非final方法
- 调用发生在运行期:JVM查对象实际类型,再定位到对应方法字节码
顺便提一句,重载(overload)是编译期决定的,跟多态无关;而重写(override)才是多态的基石。
抽象类 vs 接口:选哪个看你要解决什么问题
这不是语法题,是设计题。关键区别在于语义:
- 抽象类表达“是什么”:Animal是哺乳动物,它有心跳、呼吸这些共性行为,还能提供默认实现(比如哺乳动物都胎生)
- 接口表达“能做什么”:Flyable、Swimmable、Chargeable,关注能力契约,不关心具体是谁
- Java 8之后接口可加default方法,但依然不能有构造器、不能存状态(字段只能是public static final)
新项目优先用接口定义能力,用抽象类沉淀模板逻辑。两者不互斥,可以同时用:接口定义行为,抽象类实现部分通用逻辑,具体类继承抽象类并实现接口。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











