尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

准备Java面试时我重新梳理的知识清单

准备Java面试时我重新梳理的知识清单 当我决定重新准备Java面试时第一件事就是把那些自以为熟悉的概念全部推翻。工作三年写过的业务代码堆起来能绕工位两圈可真要回答“HashMap为什么是线程不安全的”时我却支支吾吾脑子里只剩下一堆碎片化的“背过但没懂”。这次梳理不是为了背八股文而是为了把每一块知识钉回它该在的位置上。面试官真正想看的是你有没有构建出属于自己的技术坐标系而不是你背会了多少篇面经。以下这份清单是我在深夜对着电脑屏幕一遍遍追问“为什么”后留下的精华。它不追求覆盖全部考点只求每一个条目都能经得起“再问三层”的拷问。JVM别把调优参数当成救命稻草我第一个重新面对的是JVM。以前面试被问“JVM调优”我条件反射般背出-Xms、-Xmx、-XX:UseG1GC然后等着面试官点头。但现在我明白调优的前提是能读懂GC日志而读懂GC日志的前提是理解内存模型与垃圾回收算法之间的因果关系。于是我把Java内存模型重新画了一遍程序计数器、虚拟机栈、本地方法栈、堆、方法区以及元空间。画完之后才意识到-Xss设置的是栈容量而栈溢出问题往往不是参数太小而是递归调用设计有误——用迭代替代递归才是根治。接着是垃圾回收。以前分不清Minor GC和Major GC的触发条件现在干脆把“可达性分析”当作用户故事来理解GC Roots就是博物馆的入口对象是展品引用是通道。没有通道连接的展品就该被清走。至于那些“存活时间较长”的对象进入老年代不是靠运气而是依据动态年龄判定和分配担保规则。当我真正推演了一遍对象从Eden到Survivor再到Old区的路径才发现MaxTenuringThreshold只是上限实际JVM会根据内存使用动态调整——背参数不如理解参数。最后我给自己设计了一个灵魂拷问如果线上突然频繁Full GC你会怎么查我给出的答案不是“加内存”而是“先dump堆用MAT分析看是不是有大对象或内存泄漏”。不会定位问题的调优都是拿健康换业绩的自杀式操作。并发锁的是对象考的是脑子Java并发是我重新梳理时最痛苦也最通透的部分。痛苦在于“无锁编程”“CAS”“AQS”这些词每一个都像懂连起来就变成浆糊。通透在于我终于搞明白了synchronized和ReentrantLock的本质区别不是“公平/非公平”那么肤浅——它们的核心差异是锁的获取策略和释放方式的灵活性以及能否响应中断。面试官问“你用过Lock吗”不是想听你说lock()和unlock()而是想知道你是否理解tryLock与lockInterruptibly在真实业务中如何避免死锁。然后是高并发三剑客volatile、AtomicInteger、ThreadLocal。我曾以为volatile解决了所有可见性问题直到看到“复合操作仍需加锁”时才恍然大悟——volatile保证的是单个读/写的原子性和可见性但它管不了i这种读改写三步混操作。而ThreadLocal的内存泄漏风险也绝不是因为“用了不remove”而是因为线程池里的线程被复用导致上个请求的Entry一直挂在ThreadLocalMap里。搞懂这些才是从“会用”到“会设计”的跨越。我最后亲手写了一个简单的AQS子类模拟CountDownLatch的实现。写完后什么“共享模式”“独占模式”都不需要背了因为那些状态位和等待队列就是你手里的代码。并发编程的面试题背套路永远答不到点上能画出状态流转图才是真功夫。集合从代码实现反推设计者的意图当我重新看HashMap源码时发现以前背的“链表转红黑树阈值是8”其实是个伪命题。阈值8是经过泊松分布计算出的“意外碰撞概率”但真正触发转换的条件还包括桶中节点数超过8且数组长度大于64。面试官问“为什么是8”不是考你背数字而是考你知不知道泊松分布和链表查询退化的性能代价。而且红黑树不是为“好看”存在的它的插入、删除、查询都是O(log n)当链表过长时用树形结构摊薄成本。ConcurrentHashMap的演进更让我明白“编程没有银弹”。JDK7用分段锁JDK8改用CASsynchronized锁头节点不是哪个更好而是锁粒度越细并发度越高但CAS的失败重试和扩容时的迁移逻辑也会带来新的复杂度。我试着梳理JDK8扩容时高并发下的“协助扩容”机制发现每个线程帮助迁移元素后还要检查是否完成——这套设计已经接近分布式系统的思维了。ArrayList和LinkedList的对比也远不是“数组和链表”那么粗暴。数组的随机访问O(1)是表面优势真正的优势是CPU缓存局部性友好链表的插入删除O(1)也必须带上“找到位置”的前提否则全是空话。我从此不再背“LinkedList适合插入删除”而是先问“你要插入的位置在不在头部或尾部”。SpringIOC不是面试官想要的答案Spring框架几乎是Java面试的必考项但当我重新梳理“什么是IOC”时发现教科书式的回答“控制反转”已经不能打动人。IOC的本质是对象生命周期管理权的转移是把“new”的权利从代码中抽离交给容器统一工厂。但面试官更想听的是为什么需要控制反转因为依赖关系如果由各个模块自己负责就会产生硬编码耦合而容器可以做到统一扫描、注入、代理增强。更深入一层是Bean的生命周期。从PostConstruct到InitializingBean从BeanPostProcessor到Aware接口每个阶段都有对应扩展点。我画了一张Bean生命周期流程图标出每个阶段可以干什么——比如BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization一个用于设置自定义元数据一个用于生成代理对象。Spring AOP的原理就藏在BeanPostProcessor里动态代理不是AOP的全部真正重要的是代理在Bean初始化完后被注入而代理链的责任链设计把切面逻辑织入业务方法。还有循环依赖。以前只知道“三级缓存”但为什么是三级为什么不能是二级我深入源码才知道一级缓存存单例成品二级缓存存早期暴露对象尚未完成属性填充三级缓存存ObjectFactory用于生成代理的工厂。如果不存在AOP二级缓存就够了但三级缓存的存在是为了在循环依赖中延迟生成代理对象避免代理被提前生成造成类型不匹配。这种设计的巧思光靠背是绝对感受不到的。MySQL索引失效的七十二变作为业务开发最常打交道的数据库MySQL的面试题往往从一条慢SQL开始。我重新整理了索引失效的常见场景发现很多“失效”实际上是因为优化器对成本的计算。最经典的“最佳左前缀法则”不是死规则而是因为联合索引的B树是按照键的顺序排列的跳过最左列就直接破坏了排序的比较基准。至于like %xx为什么失效因为B树无法利用索引做从右往左的范围查找。但我更想说的是索引失效不等于全表扫描优化器可能选择覆盖索引或者索引跳跃扫描面试时别把话说死。事务隔离级别和锁的关系也要重新理解。RR可重复读在MySQL默认隔离级别下间隙锁是如何防止幻读的我推导了一遍SELECT ... FOR UPDATE加锁过程对命中的记录加行锁对区间加间隙锁从而阻止其他事务插入。但这个代价是并发吞吐下降。于是有人用MVCC的一致性快照读来规避锁。MVCC是快手锁是慢刀真正的高并发方案要做取舍。还有一条我想撕掉以前标签的SQL“ORDER BY id DESC LIMIT 1”真的快吗如果没有索引MySQL只能全表扫描然后排序即使id是主键也不会自动倒序扫描——除非优化器走了索引。业务上“我只想取最新一条”的写法很可能是隐藏的性能杀手。Redis缓存不是万能药Redis在面试中常被问“缓存穿透、击穿、雪崩”但当我重新梳理时发现这些概念背后的核心是“缓存与数据库的一致性”问题。缓存穿透用布隆过滤器挡的是恶意请求缓存击穿用互斥锁保护的是热点key过期瞬间缓存雪崩用随机过期时间错峰失效。但真正考验工程师的是如何在保证性能的前提下设计出可容忍数据不一致的方案。比如更新数据库后删除缓存在极端情况下可能导致并发下缓存还是旧值这就要靠延迟双删或消息队列重试。Redis的持久化机制——RDB和AOF——也不再是“快照和日志”那么简单。RDB是二进制堆快照文件紧凑但可能丢失最后一次备份后的数据AOF是命令追加日志可配置always每秒刷盘但文件体积大、恢复慢。还有一种混合持久化AOF重写时先写RDB头再追加增量命令兼顾了恢复速度和数据安全度。这些细节不是背出来的而是在实际使用Redis中踩坑踩出来的。另外Redis分布式锁用SET NX EX还是Redlock我不再迷信Redlock因为它要求大多数节点可用且存在时钟漂移问题。在大多数业务场景下使用Redis单节点加锁配合业务层幂等控制已经足够了。盲目的分布式共识反而引入不必要的复杂度。分布式CAP是理论Paxos是现实终于梳理到分布式系统。这里我最大的收获是CAP理论不是三选二而是在分区发生时的“二选一”分区永远会存在所以你必须决定在不稳定网络下优先保证一致性还是可用性。传统分布式事务用2PC/3PC但2PC的同步阻塞和协调者单点问题使其并不适合高并发场景。TCC、Saga等最终一致性方案成为主流——它们放弃强一致换来高可用和性能。消息队列在分布式中的作用被我重新认识不只是“解耦和削峰”更重要的是“分布式事务的最终一致性基石”。本地消息表加消息队列或者事务消息都能在业务数据库和消息中间件之间达成最终一致前提是消息必须幂等。幂等性设计不是给消息加个唯一ID就算完而是要在消费端查重、防重、记录处理状态。面试被问“如何保证消息不重复消费”时最愚蠢的回答是“不发送重复消息”。Paxos和Raft是分布式共识的标配。我不再背诵算法流程而是思考为什么Raft比Paxos更易理解——Raft把共识问题分解为选主、日志复制、安全性三个子问题并且强化了领导者的角色牺牲了一些灵活性换来了可读性。面试官问“讲一下Raft的选举过程”时如果能画出任期和投票的相互交互比背日志复制条目要深刻得多。网络与操作系统被忽视的地基很多Java工程师只关注框架却忘记最底层的网络和OS知识。我这次特意复习了TCP三次握手、四次挥手并把它映射到HTTP的连接管理上。为什么HTTP/1.1默认长连接因为TCP握手开销比业务数据本身更大。为什么HTTP/2有头部压缩和多路复用因为请求头重复传输浪费带宽而队头阻塞源于HTTP/1.1的串行响应。这些知识在排查慢接口时非常有用。线程模型是另一个重灾区。我之前背过“IO多路复用”但真正理解select/epoll的区别——epoll用事件驱动注册回调没有轮询的O(n)开销——才明白Netty为什么能扛住百万连接。你说自己会Netty却解释不清NIO和BIO差别面试官只能认为你在Demo工程里写过HelloWorld。操作系统层面的“用户态/内核态切换”不仅影响BigDecimal的精度更影响网络读写的性能。每次系统调用都有上下文切换成本所以Netty用内存映射或直接内存来减少拷贝每次锁竞争都可能进入内核态所以JVM偏向锁和轻量级锁的优化试图在用户态解决大部分同步问题。这些知识看似无关实际在排查CPU飙高、锁开销时都是洞察真相的钥匙。重新出发从清单到体系当我真正把这份清单写完已经过了整整两周。我没有背任何一道题而是从每个技术点出发沿着“是什么—为什么—什么时候用—有什么权衡”的路径向下挖掘。面试临近时那些清晰的知识树会自发地向你输出答案因为你不是在卖记忆而是在展示思考过程。我要把这份清单贴在显示器边不是为了考试而是为了时刻提醒自己技术栈是一条河流所有的框架都只是浪花底层逻辑才是水源。最后我想分享一个观察面试中最稀缺的不是知道多少答案而是提问时那种“我知道你问什么也知道我为什么要这样学”的笃定感。准备清单的过程本身就是一个重建知识底座的过程。当你不再焦虑“会不会考到”而是觉得“随便你问我都能聊出设计权衡”时Java面试这道坎就已经被你甩在身后了。
返回列表