Java 核心语法与 JVM 内存模型
基础不牢,地动山摇|掌握语言本质是高薪 Offer 的前提
1.1 Java 基础:封装、继承、多态的本质
📝**封装**:通过 private 成员变量 + getter/setter 实现数据隐藏,配合访问控制符(public/private/protected/default)构建安全边界。
📝**继承**:单继承+多实现接口,避免深度继承层次(≤3层为佳),优先用组合代替继承(“组合优于继承”原则)。
📝**多态**:运行时绑定(dynamic binding),通过方法重写(override)实现,底层依赖虚方法表(vtable)——JVM 在类加载时构建 vtable,方法调用时通过对象头指针查找。
示例:多态的典型陷阱
class Animal {
void speak() { System.out.println("Animal speaks"); }
}
class Dog extends Animal {
void speak() { System.out.println("Dog barks"); }
void fetch() { System.out.println("Dog fetches"); }
}
public class Test {
public static void main(String[] args) {
Animal a = new Dog(); // 向上转型
a.speak(); // 输出:Dog barks ✅(重写)
// a.fetch(); // ❌ 编译错误:Animal 无 fetch()
}
}
💡向上转型后只能调用父类声明的方法,这是 Java 类型系统安全性的体现。
1.2 JVM 内存区域与线程模型
📝程序计数器(PC Register):线程私有,记录当前执行的字节码指令地址;唯一无 OOM 区域。
📝Java 虚拟机栈(JVM Stack):线程私有,存储栈帧(局部变量表、操作数栈、动态链接、返回地址);栈深度溢出(StackOverflowError) vs 内存不足(OOM)。
📝本地方法栈(Native Method Stack):服务于 native 方法(如 JNI 调用)。
📝堆(Heap):线程共享,存储对象实例;新生代(Eden+S0+S1)与老年代比例默认 1:2;GC 分代依据是“对象生命周期”。
📝方法区(Method Area):JDK8 后为元空间(Metaspace),存储类元数据;不再受限于 JVM 内存,使用本地内存。
| 区域 | 是否线程共享 | OOM 风险 | 典型异常 |
|---|---|---|---|
| 程序计数器 | 否 | 无 | 无 |
| 虚拟机栈 | 否 | 高(栈深) | StackOverflowError |
| 本地方法栈 | 否 | 中 | OutOfMemoryError |
| 堆 | 是 | 高 | java.lang.OutOfMemoryError: Java heap space |
| 元空间 | 是 | 低(本地内存耗尽) | java.lang.OutOfMemoryError: Metaspace |
1.3 垃圾回收(GC)机制
📝判断对象存活:
• 引用计数法(Java 不采用,因循环引用问题)
• 可达性分析(根对象:GC Roots → 对象引用链断开 → 可回收)
📝GC Roots 包括:
• 虚拟机栈中引用的对象
• 类静态属性引用的对象
• 常量引用的对象
• 本地方法栈中 JNI 引用的对象
📝GC 算法:
• 标记-清除(产生碎片)
• 标记-复制(新生代,需分半内存)
• 标记-整理(老年代,避免碎片)
• 分代收集(主流方案:新生代用复制 + 老年代用标记整理)
📝垃圾收集器对比:
Serial:单线程,Client 模式默认;
ParNew:Serial 多线程版,配合 CMS;
Parallel Scavenge:高吞吐量,自适应调节策略;
CMS:低停顿,但内存碎片多;
G1:Region 分区 + 可预测停顿(JDK9 默认)。
1.4 类加载机制与双亲委派模型
📝加载过程:
① 加载(Load):将 class 文件二进制流读入内存 → 生成 Class 对象
② 验证(Verify):文件格式、元数据、字节码、符号引用验证
③ 准备(Prepare):为静态变量分配内存并设置默认值(如 int→0)
④ 解析(Resolve):将常量池符号引用转为直接引用
⑤ 初始化(Initialize):执行
📝双亲委派模型:
💡自定义类加载器需重写
loadClass(),但必须先委托父加载器,否则破坏安全模型。
集合框架(Collection & Map)
高频考点:ArrayList vs LinkedList、HashMap 源码、ConcurrentHashMap 原理
ArrayList vs LinkedList vs Vector
| 特性 | ArrayList | LinkedList | Vector |
|---|---|---|---|
| 底层结构 | 动态数组 | 双向链表 | 动态数组(线程安全) |
| 随机访问 | ✅ O(1) | ❌ O(n) | ✅ O(1) |
| 插入/删除 | ❌ O(n)(需移动) | ✅ O(1)(定位后) | ❌ O(n) |
| 线程安全 | ❌ | ❌ | ✅(synchronized 方法) |
| 扩容机制 | 1.5 倍(JDK8+) | 不扩容 | 2 倍 |
💡实际开发中,ArrayList 占 90% 以上场景;LinkedList 仅用于频繁头尾插入/删除(如队列实现)。
HashMap 源码深度解析
📝JDK7:数组 + 链表(哈希冲突 → 链地址法)
📝JDK8:数组 + 链表 + 红黑树(链表长度 ≥8 且数组长度 ≥64 → 转红黑树;避免哈希退化)
📝关键参数:
• 初始容量:16(2 的幂)
• 负载因子:0.75(时间与空间平衡点)
• 树化阈值:8
• 链表恢复阈值:6(避免频繁树化/退化)
哈希碰撞攻击示例
// 故意构造哈希冲突的 key,导致 HashMap 性能退化为 O(n)
Map map = new HashMap<>();
for (int i = 0; i < 10000; i++) {
map.put("a" + (i % 16), "value" + i); // 仅 16 个 key,碰撞严重
}
💡生产环境应避免固定模式的 key,或改用 ConcurrentHashMap。
📝ConcurrentHashMap(JDK8):
• 取消 Segment 分段锁,改用 CAS + synchronized
• Node 数组 + 链表/红黑树
• 线程安全操作:put/get/remove
• size 计算:baseCount + counterCells(避免全量统计)
Set 实现类对比
- HashSet:基于 HashMap,元素唯一(依赖 hashCode + equals)
- LinkedHashSet:维护插入顺序(HashMap + 双向链表)
- TreeSet:基于 TreeMap(红黑树),自然排序或定制比较器
💡TreeSet 使用场景:需排序/范围查询(如成绩排名、时间区间统计)
并发编程(线程与锁)
核心考点:synchronized、volatile、CAS、线程池、JUC 工具类
线程状态与生命周期
NEW → RUNNABLE(start())→ BLOCKED(等待锁)→ WAITING/TIMED_WAITING(wait/sleep/join)→ TERMINATED(run() 结束)
💡BLOCKED 是线程在等待 synchronized 锁;WAITING 是主动等待通知(如 wait())。
volatile 关键字
• 可见性:线程修改后立即写回主内存,其他线程读取时强制从主内存加载
• 禁止重排序:通过内存屏障(LoadLoad / StoreStore)实现
• 不保证原子性:如 i++ 仍需加锁
💡典型应用:单例模式的双重检查锁(DCL)
CAS 与原子类
• 底层:CPU 原子指令(lock cmpxchg)
• 问题:ABA 问题(用版本号解决 → AtomicStampedReference)
• 常用类:AtomicInteger、LongAdder(高并发下性能更优)
// LongAdder 高并发计数示例
LongAdder counter = new LongAdder();
executor.submit(() -> {
for (int i = 0; i < 100000; i++) {
counter.increment();
}
});
线程池(ThreadPoolExecutor)参数详解
| 参数 | 含义 | 推荐值 |
|---|---|---|
| corePoolSize | 核心线程数 | CPU 核心数 + 1(CPU 密集型) 或 2×CPU 核心数(IO 密集型) |
| maximumPoolSize | 最大线程数 | 根据任务特性调整 |
| keepAliveTime | 非核心线程存活时间 | 60s(默认) |
| workQueue | 任务队列 | SynchronousQueue(直接提交) LinkedBlockingQueue(无界,慎用) ArrayBlockingQueue(有界,推荐) |
| rejectedExecutionHandler | 拒绝策略 | AbortPolicy(抛异常) CallerRunsPolicy(调用者线程执行) |
💡避免使用 Executors 工厂方法创建线程池(隐藏队列容量问题),应手动构造 ThreadPoolExecutor。
Spring 全家桶(IoC / AOP / 事务)
高频考点:Bean 生命周期、循环依赖、事务传播机制
4.1 Spring IoC 容器与 Bean 生命周期
① 实例化(Instantiation)
通过反射或 CGLIB 创建 Bean 对象(此时属性未赋值)
② 属性赋值(Populate)
依赖注入(DI):@Autowired、@Value、set 方法
③ 初始化(Initialization)
执行 BeanPostProcessor.postProcessBeforeInitialization()
→ InitializingBean.afterPropertiesSet()
→ 自定义 init-method
→ BeanPostProcessor.postProcessAfterInitialization()
④ 使用(Ready)
Bean 已就绪,可被调用
⑤ 销毁(Destruction)
DisposableBean.destroy() + 自定义 destroy-method
📝循环依赖:
• 构造器注入 → 无法解决(抛异常)
• setter 注入 → 三级缓存(singletonObjects / earlySingletonObjects / singletonFactories)
4.2 AOP 原理与代理模式
📝JDK 动态代理:基于接口,生成代理类实现相同接口
📝CGLIB 代理:基于继承,生成子类重写方法(final 方法无法代理)
📝通知类型:
• @Before:前置通知
• @After:最终通知
• @AfterReturning:返回通知
• @AfterThrowing:异常通知
• @Around:环绕通知(最强大)
// 示例:日志切面
@Aspect
@Component
public class LogAspect {
@Around("execution(* com..service.*.*(..))")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long time = System.currentTimeMillis() - start;
System.out.println("Method " + joinPoint.getSignature() + " took " + time + "ms");
return result;
}
}
4.3 Spring 事务传播机制
| 传播行为 | 说明 | 示例场景 |
|---|---|---|
| REQUIRED | 有事务则加入,否则新建 | 默认值,90% 场景 |
| REQUIRES_NEW | 挂起当前事务,新建事务 | 日志记录(独立事务) |
| NESTED | 在当前事务中创建保存点 | 部分失败可回滚子事务 |
| NOT_SUPPORTED | 挂起当前事务,以非事务方式执行 | 查询缓存(避免事务开销) |
💡事务失效常见原因:
• 方法非 public
• 同类中方法调用(绕过代理)
• 异常被 catch 捕获未重新抛出
• 数据库引擎不支持事务(如 MyISAM)
数据库(MySQL)与缓存(Redis)
核心考点:索引原理、事务隔离级别、缓存穿透/击穿/雪崩
5.1 MySQL 索引原理
📝B+ 树结构:
• 只有叶子节点存储数据(非叶子节点仅存索引)
• 叶子节点间有指针连接(范围查询高效)
• 磁盘预读(页大小 16KB)
📝最左前缀原则:
索引 (a,b,c) 可匹配:
• a、a+b、a+b+c
• 但不能单独匹配 b 或 c
📝覆盖索引:查询字段全部在索引中,避免回表
索引失效场景
- 使用函数或表达式:
WHERE YEAR(create_time)=2023 - 隐式类型转换:
WHERE phone='13800138000'(phone 为 int) - LIKE 以 % 开头:
WHERE name LIKE '%java%' - OR 条件未全走索引
5.2 Redis 缓存问题与解决方案
缓存穿透
问题:请求不存在的数据 → 缓存未命中 → 查询数据库 → 未命中 → 缓存不写入 → 大量请求压垮 DB
解决方案:
• 布隆过滤器(BloomFilter)拦截非法 key
• 缓存空对象(设置短 TTL,如 5min)
• 参数校验(防恶意请求)
缓存击穿
问题:热点 key 过期瞬间 → 大量请求同时打到 DB
解决方案:
• 逻辑过期(异步刷新)
• 分布式锁(仅一个线程查询 DB)
• 永不过期 + 定时刷新
缓存雪崩
问题:大量 key 同时过期 → DB 瞬间压力骤增
解决方案:
• 过期时间加随机值(如 3600±600s)
• 热点数据永不过期
• 限流降级(Sentinel)
分布式系统与微服务
高阶考点:CAP 理论、分布式 ID、服务治理、分布式事务
6.1 分布式 ID 生成方案
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| UUID | 简单、全局唯一 | 无序、长度长、存储浪费 | 日志、非核心业务 |
| 数据库自增 | 有序、简单 | 单点瓶颈、分库分表困难 | 单库小系统 |
| Snowflake | 高性能、有序、分布式 | 依赖时间、时钟回拨问题 | 主流方案(如百度 UIDGenerator) |
| Redis INCR | 高性能、有序 | 依赖 Redis 可用性 | 高并发写入场景 |
6.2 分布式事务解决方案
① 2PC(两阶段提交)
协调者询问参与者是否可提交 → 全部同意则提交 → 强一致性,但单点故障风险高
② 3PC(三阶段提交)
增加 CanCommit 阶段 + 超时机制 → 降低阻塞,但一致性减弱
③ TCC(Try-Confirm-Cancel)
业务侵入式:Try(预留资源)、Confirm(确认)、Cancel(回滚)
• 优点:高性能
• 缺点:开发复杂(需实现回滚逻辑)
④ Seata(AT 模式)
无侵入:基于本地事务 + 全局协调器
• RM(资源管理器):分支事务
• TM(事务管理器):全局事务
• TC(事务协调器):状态存储
6.3 CAP 理论与 BASE 理论
📝CAP 定理:
一致性(C):所有节点数据一致
可用性(A):每个请求都能响应
分区容错性(P):网络故障下系统仍可用
分布式系统只能同时满足其中两点
📝BASE 理论( compromise 方案):
• Basically Available:基本可用
• Soft State:允许状态延迟
• Eventually Consistent:最终一致
💡电商系统:高可用 + 最终一致(如订单状态同步)
真实企业面试题汇编
来自 BAT/TMD 等一线大厂最新面试反馈|附解题思路与评分标准
7.1 字节跳动:HashMap 红黑树转换条件
问题:为什么链表长度 ≥8 且数组长度 ≥64 才转红黑树?
参考答案:
• 红黑树节点占用空间是链表节点的 2 倍(含左右子节点 + 颜色位)
• 当链表长度 <8 时,链表查询 O(n) 与红黑树 O(log n) 性能差距不大,且链表更省空间
• 阈值 64 是避免小数组频繁树化(数组扩容成本低)
• 面试官评价:✅ 考察源码深度 + 性能权衡思维(9/10 分)
7.2 阿里巴巴:分布式锁实现与 Redis RedLock
问题:RedLock 为何不推荐?如何实现高可用分布式锁?
参考答案:
• RedLock 问题:
- 多节点网络延迟导致锁失效
- 客户端故障时锁未释放
• 推荐方案:
- 单 Redis 实例 + Lua 脚本(原子性)
- Redisson 的 Watchdog 机制(自动续期)
- Zookeeper/Etcd 实现更可靠(强一致性)
面试官评价:✅ 有生产经验 + 技术选型能力(10/10 分)
7.3 腾讯:Spring Boot 自动配置原理
问题:@EnableAutoConfiguration 如何工作?如何排除不需要的自动配置?
参考答案:
• 原理:
1. @Import(AutoConfigurationImportSelector.class)
2. 读取 classpath:/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
3. 按 @Conditional 注解过滤(如 @ConditionalOnClass)
• 排除方式:
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
或在 application.yml:
spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
网友们还关心
高频搜索问题深度解答|覆盖新人入门到进阶
网站地图
快速导航至核心内容模块