ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

八股文知识索引:Java后端、C++、Python、嵌入式与游戏测试面试指南

八股文知识索引:Java后端、C++、Python、嵌入式与游戏测试面试指南 前阵子帮组里几个学弟学妹做模拟面试聊完一轮下来发现一个共性问题项目经历讲得挺溜一问到 HashMap 扩容阈值、TCP 三次握手为什么不是两次、MySQL 索引下推到底省了哪一步回答就开始变成这个我记得好像是……。这种场面我见得太多了。所以趁着周末我把这两年自己攒的八股文总结重新梳理了一遍从最早那种复制粘贴面试题 标准答案的流水账改成了一份能真正指导复习节奏的知识索引。这份总结覆盖java面试八股文、后端面试八股文、c八股文、python八股文、嵌入式八股文和游戏测试八股文几个主要方向核心目的就一个——让背过的每一条都能在面试时被追问三层还不崩。如果你正准备跳槽或者刚入行想系统补基础这份东西可以直接拿去改吧改吧用。1. 先把定位摆正八股文不是背诵清单是知识索引很多人对面试八股文的反感其实来源于一个误解把它当成了一份需要逐字背诵的课文。我早期也是这么干的打印了三百多页的八股文面试题合集每天早晚各背一小时结果真到面试现场面试官稍微换个问法脑子里的答案就对不上了。问题不在于背得不够而在于背的东西是结论不是链路。1.1 面试官问八股文时他在问什么我后来自己也坐到了面试官的位置上才彻底想明白这件事。技术面试问基础题目的一般有三个第一是确认你的知识边界看你是不是只会自己项目里用到的那一小块第二是看你有没有往下追一层的习惯因为线上出问题时能定位到根因的人永远是稀缺的第三是观察你面对不确定问题时的反应是硬编还是承认不会然后给出推理路径。举个具体的例子。我问HashMap 的默认容量是多少这题本身没什么价值百度一秒就有答案。但如果候选人答完 16我接着问为什么是 16 而不是 15 或者 17这时候就分人了。能答出因为扩容时用位运算代替取模容量是 2 的幂才能保证hash (n-1)等价于hash % n的人说明他看过源码并且理解设计动机答书上都说是 16的人就止步于背诵层面了。所以八股文真正的用法是当作一张待深挖的地图。每一条结论背后都至少还有两层可以追你的复习目标是把这两层提前打通而不是把结论背熟。1.2 三种典型翻车现场我整理这份总结的时候把见过的翻车情况归了三类你可以对照自查一下。第一类是只会结论不会推导。比如问为什么用 B 树不用 B 树做 MySQL 索引答因为 B 树矮。这个答案方向没错但漏掉了最关键的一点B 树的非叶子节点不存数据只存键所以同样 16KB 的页能塞下更多键树高更低磁盘 IO 次数更少同时叶子节点用链表串起来范围查询不用回到根节点。少了这半句面试官就会觉得你是背的。第二类是知识点孤立。比如能背出线程池七个参数但问他你们项目里线程池核心数怎么定的就答不上来。这说明知识没有和工程实践挂钩。第三类是答非所问式堆砌。问 Redis 缓存穿透他把穿透、击穿、雪崩全讲一遍讲了五分钟面试官想听的布隆过滤器 空值缓存反而被淹没了。这类问题最冤明明会但表达不行。提示复习任何一条八股文时强制自己补一句为什么这么设计和什么场景下会出问题。这两句话是区分背诵和理解的分水岭。2. 我的八股文总结是怎么搭骨架的最开始的版本我是按JVM、并发、MySQL、Redis、网络、操作系统平铺直叙列下来的用了一段时间发现效率很低因为不同公司的面试重点差异巨大。面一个做中间件的团队问的全是并发和网络细节面一个业务团队问的都是 MySQL 和缓存。所以第二版我改成了二维分层。2.1 按方向 深度做二维分层横轴是技术方向纵轴是深度层级。深度我分了四层是什么概念定义、怎么用API 和配置、为什么设计动机和源码实现、出问题怎么办故障排查和调优。前三层是面试必答的第四层是加分项。这个分层最大的好处是你可以按时间预算灵活取舍。如果只剩三天复习时间那就只扫是什么和为什么两层确保被问到时能说出完整的因果链如果有一个月就把第四层补上这部分内容在面试中出现的频率不高但一旦出现答得出来就是降维打击。方向上的划分我参照了主流招聘 JD 的分类大致是这样方向核心模块面试占比个人体感Java 后端JVM、并发、集合、MySQL、Redis、Spring、网络最高约七成岗位C/C内存管理、对象模型、STL、编译链接、网络音视频、游戏、基础架构岗为主Python语言特性、GIL、异步、框架、数据处理数据、测试开发、运维方向嵌入式C 语言、Linux 系统编程、驱动、通信协议硬件相关岗位游戏测试测试理论、用例设计、抓包、自动化、性能游戏公司质量保障岗2.2 优先级分级S/A/B/C 四档光分层还不够还得知道哪些必须先啃。我给每个知识点打了优先级标签规则很简单出现频率 × 追问深度。出现频率高、且面试官容易往下追三层的就是 S 档。S 档的东西我列一下这部分是无论如何都要吃透的Java 侧的 HashMap 实现、JVM 内存结构与 GC、线程池参数与执行流程、Synchronized 与 AQS、MySQL 索引与事务隔离级别、Redis 数据结构与持久化、TCP 三次握手与四次挥手、HTTP 与 HTTPS 的差异。这几块几乎是必问项而且每一块都能被追问到很深。A 档是高频但追问浅的比如 Spring Bean 生命周期、MyBatis 原理、Linux 常用命令、设计模式的几个典型应用。这些掌握到能讲清楚流程就够了。B 档是方向相关的比如消息队列的可靠性投递、分布式锁的实现、分库分表的方案。这些看你投的岗位做业务的问得多做工具的几乎不问。C 档是低频的冷门知识比如某些 JDK 版本的特定 Bug、某个框架的历史版本差异。这些不用专门背遇到了现查就行。注意别在 C 档上花时间。我见过有人花两天研究 JVM 某个参数在特定版本下的行为差异结果面试时压根没被问到反而把 S 档的 AQS 讲得磕磕巴巴。时间永远是稀缺资源按优先级投。3. Java 后端八股文的六大核心模块java八股文体系最庞杂我拆成六块。每块我都写了该记什么和容易被追到哪一层你可以对着自己的笔记查漏。3.1 JVM 内存结构与垃圾回收这块的核心是三条线内存区域怎么划分、对象怎么分配、垃圾怎么回收。内存区域要能说清堆、栈、方法区JDK 8 之后是元空间、程序计数器、本地方法栈各自存什么。面试官最爱追的是字符串常量池在哪JDK 7 之后它从方法区挪到了堆里这个版本变化是高频追问点。对象分配要掌握栈上分配、TLAB、Eden 区分配、大对象直接进老年代这一整套流程。这里有个容易被忽略的细节-XX:PretenureSizeThreshold只对 Serial 和 ParNew 收集器有效用 G1 或者 Parallel 的时候是不生效的。这种细节答出来面试官基本会认定你真读过东西。GC 部分必须能讲清分代收集理论和三种基础算法标记清除、标记复制、标记整理的取舍。标记清除会产生内存碎片标记复制浪费一半空间但效率高标记整理没碎片但要移动对象。分代的意义就在于根据对象存活率选择不同算法——新生代存活率低用复制老年代存活率高用标记整理或者标记清除。具体到收集器至少要说清 CMS 和 G1 的差异。CMS 是并发标记清除追求低停顿但有碎片和并发失败Concurrent Mode Failure的问题G1 是分区收集可以预测停顿时间通过-XX:MaxGCPauseMillis设定目标。追问通常会落到你们线上用的哪个、为什么这时候你要能说出业务对停顿的敏感度、堆大小、机器配置这些实际考量。3.2 并发编程JMM、锁、线程池并发是三块里最容易被连环追问的因为它既能问理论也能问代码。JMMJava 内存模型要讲清主内存和工作内存的关系以及 happens-before 规则。volatile 的两个语义——可见性和禁止指令重排——要能对应到具体的底层实现写操作后会插入 store barrier 把缓存刷回主内存读操作前插入 load barrier。单例的双重检查为什么要给实例加 volatile就是防重排导致拿到半初始化对象。锁这块Synchronized 的锁升级无锁→偏向锁→轻量级锁→重量级锁是必问的。要能说清偏向锁的适用场景是同一个线程反复进入同步块一旦有第二个线程竞争就升级。轻量级锁用 CAS 自旋自旋失败到一定次数就膨胀成重量级锁走操作系统互斥量。AQS 是另一个大头重点理解 state 变量加 CLH 双向队列这套机制ReentrantLock 的公平和非公平就差在hasQueuedPredecessors()这个判断上。线程池是必须能手写的。七个参数我直接写出来public ThreadPoolExecutor(int corePoolSize, // 核心线程数 int maximumPoolSize, // 最大线程数 long keepAliveTime, // 空闲存活时间 TimeUnit unit, // 时间单位 BlockingQueueRunnable workQueue, // 任务队列 ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler) // 拒绝策略执行流程是核心线程没满就建核心线程满了就丢队列队列满了就建非核心线程非核心线程也满了就触发拒绝策略。这个顺序很多人记反以为是先建线程再排队。记住一句话先把核心编制用完能排队就排队排队排不下才临时扩编扩编也满了就拒收。四种拒绝策略也要知道AbortPolicy 抛异常默认、CallerRunsPolicy 让提交任务的线程自己跑、DiscardPolicy 静默丢弃、DiscardOldestPolicy 丢掉队列里最老的再试。生产环境一般用 CallerRunsPolicy 做背压或者自定义策略落盘。3.3 集合框架与源码细节HashMap 是绕不过去的。要能讲清数组 链表 红黑树的结构、put 的完整流程、扩容机制、以及为什么阈值是 8 和 6。红黑树转换的阈值 8 是有统计学依据的源码注释里写了在理想的哈希分布下一个桶里节点数达到 8 的概率大约是千万分之六所以设成 8 基本不会真的转换是兜底方案。而退回链表的阈值设成 6 而不是 7是为了留一个缓冲区避免在 7、8 之间反复横跳。扩容这块要能说出 1.7 和 1.8 的差异1.7 是头插法多线程扩容会形成环形链表导致死循环1.8 改成了尾插法规避了这个问题但 ConcurrentModificationException 依然存在并发场景还是得用 ConcurrentHashMap。ConcurrentHashMap 在 1.8 里放弃了分段锁改成 CAS synchronized 锁单个桶头节点锁粒度更细。扩容的时候用 ForwardingNode 标记已迁移的桶多线程可以协助迁移。3.4 MySQL索引、事务、锁索引部分的核心是 B 树和聚簇索引。为什么用 B 树前面提过了矮 范围查询友好。聚簇索引的意思是数据行就挂在主键索引的叶子节点上所以主键查询最快二级索引的叶子节点存的是主键值需要回表。覆盖索引就是让查询需要的字段都在索引里避免回表。最左匹配原则要理解本质联合索引(a, b, c)之所以能用 a、ab、abc是因为索引是按这个顺序排序的跳过 a 直接查 b 就没法用有序性了。但有个例外where b 1 and a 1这种顺序颠倒的写法优化器会自动调整一样能走上索引。事务的四个隔离级别和各自解决的问题要背熟读未提交有脏读读已提交解决脏读但有不可重复读可重复读解决不可重复读但有幻读MySQL 通过间隙锁基本解决了串行化全解决但性能最差。MVCC 的实现要能说清版本链、undo log、ReadView 三者怎么配合。锁这块行锁、表锁、间隙锁、临键锁的区别要清楚。临键锁是记录锁加间隙锁的组合锁的是一个左开右闭的区间这是可重复读下防止幻读的关键。3.5 Redis数据结构、持久化、缓存三大问题Redis 的数据结构不能只背五种基础类型还要知道底层的 SDS、跳表、压缩列表、快速列表这些实现。比如 ZSet 在元素少的时候用压缩列表超过zset-max-ziplist-entries默认 128或者单个元素超过zset-max-ziplist-value默认 64 字节就转成跳表。持久化要能对比 RDB 和 AOF。RDB 是快照恢复快但可能丢数据AOF 是写命令日志实时性好但文件大、恢复慢。4.0 之后引入的混合持久化把 RDB 的全量快照和增量的 AOF 命令结合兼顾了恢复速度和数据安全性。缓存三大问题——穿透、击穿、雪崩——是必须条件反射的。穿透是查不存在的数据用布隆过滤器或者缓存空值击穿是热点 key 过期瞬间大量请求打到数据库用互斥锁或者热点数据永不过期雪崩是大量 key 同时过期解决办法是过期时间加随机扰动。问题触发条件解决方案缓存穿透查询不存在的数据布隆过滤器、缓存空值、参数校验缓存击穿单个热点 key 失效互斥锁重建、逻辑过期、热点预热缓存雪崩大量 key 同时失效过期时间加随机值、多级缓存、熔断降级3.6 Spring 与框架原理Spring 的核心就两块IoC 和 AOP。IoC 要能说清 Bean 的完整生命周期实例化、属性填充、初始化前BeanPostProcessor 前置、初始化afterPropertiesSet、init-method、初始化后AOP 代理在这里生成、使用、销毁。循环依赖是高频问题。Spring 用三级缓存解决一级是完整的单例池二级是早期暴露的原始对象三级是 ObjectFactory 工厂。为什么要三级而不是两级因为如果只用两级AOP 场景下早期暴露的就是原始对象最终注入的也是原始对象代理就丢了。三级缓存里的工厂能在需要时提前生成代理。AOP 要能说清 JDK 动态代理和 CGLIB 的区别JDK 要求目标类实现接口通过反射生成代理CGLIB 是字节码增强生成子类。Spring Boot 2.x 之后默认全部用 CGLIB因为省去了接口限制。4. C 和 C 方向的八股文重点差异c八股文和c语言八股文的体系跟 Java 差别很大Java 是虚拟机托管内存C 是手动管理所以面试重点自然偏到内存和对象模型上。如果你是从 Java 转 C 方向这块要重新打地基。4.1 内存管理与指针必须能区分栈、堆、全局/静态存储区、常量存储区、代码区各自存什么以及栈和堆的生长方向、分配效率差异。malloc/free和new/delete的区别是必问项new会调用构造函数、返回具体类型指针、失败抛异常malloc只分配内存、返回void*、失败返回 NULL。混用会导致析构函数不被调用。智能指针要能说清三种的所有权语义。unique_ptr独占不能拷贝只能移动shared_ptr共享内部有引用计数weak_ptr不增加计数用来打破循环引用。shared_ptr 的循环引用问题是经典陷阱两个对象互相持有对方的 shared_ptr引用计数永远不归零内存泄漏。4.2 虚函数与对象模型虚函数表vtable和虚函数指针vptr的机制要讲清楚每个含虚函数的类有一张 vtable每个对象有一个 vptr 指向它。多态调用时通过 vptr 找到 vtable再按偏移取函数地址。构造函数里调用虚函数不会走多态因为此时 vptr 还没初始化到子类版本。虚析构函数也是必问的。基类指针指向子类对象时如果基类析构函数不是虚的delete 时只会调用基类析构子类资源泄漏。所以基类析构函数要声明为 virtual。4.3 STL 与编译链接STL 部分vector 的扩容机制、map 和 unordered_map 的底层差异红黑树 vs 哈希表、迭代器失效的场景都是高频。vector 扩容一般是按 1.5 倍或者 2 倍扩容后所有迭代器失效unordered_map 的 rehash 也会导致迭代器失效但引用和指针不会失效。编译链接这块从源码到可执行文件要经过预处理、编译、汇编、链接四步。链接又分静态和动态静态链接把所有库打进可执行文件体积大但部署简单动态链接在运行时加载节省空间但有依赖问题。符号表、重定位、强弱符号这些概念做基础架构的岗位会问。5. Python、测试、嵌入式方向的差异化准备python八股文、游戏测试八股文和嵌入式八股文的体系跟前端后端完全不一样硬套 Java 那套是没用的。这三块我简单说说各自的抓手。5.1 Python 方向GIL 和语言特性Python 面试绕不开 GIL全局解释器锁。要能说清它的存在是为了保护引用计数等 CPython 内部状态导致同一时刻只有一个线程执行字节码。所以 CPU 密集型任务要用多进程IO 密集型任务多线程仍然有效因为 IO 等待时会释放 GIL。语言特性方面装饰器、生成器、迭代器、上下文管理器、可变默认参数陷阱这些都要熟。特别是可变默认参数这个坑def f(a[])里的列表在函数定义时创建多次调用共享同一个对象会累积数据。5.2 游戏测试方向用例设计和流程游戏测试八股文的重点不是编程语言而是测试理论。用例设计方法等价类、边界值、因果图、场景法、缺陷生命周期、测试计划与报告的编写这些是基础。游戏特有的部分包括数值验证、战斗逻辑测试、网络同步测试、弱网模拟、兼容性测试、性能压测。比较容易被问的是给你一个抽卡系统你怎么设计测试用例。这题要覆盖概率验证大样本统计、保底机制、边界值抽卡次数为 0、达到保底、道具不足、并发场景多人同时抽、异常场景网络中断、支付回调失败。5.3 嵌入式方向C 语言和系统编程嵌入式八股文的核心是 C 语言基本功加上 Linux 系统编程。C 语言部分重点是内存对齐、位运算、指针与数组的区别、volatile和const的语义、结构体对齐规则。系统编程部分要掌握进程与线程、进程间通信管道、消息队列、共享内存、信号量、多路复用select、poll、epoll 的区别、内存管理虚拟内存、页表、缺页中断。驱动开发岗位还会问字符设备驱动的注册流程、内核态与用户态的数据拷贝方式。通信协议也是重点I2C、SPI、UART、CAN 的差异和适用场景要能对比着说。比如 I2C 是两线制、支持多主多从、速率相对低SPI 是四线制、全双工、速率高但占用引脚多。6. 我的复习实操流程骨架搭好了接下来是怎么执行。我把自己跑通的一套流程写下来你可以直接照着改。6.1 时间分配三七开我把复习时间按三七分三成用来读和整理七成用来输出。很多人反过来花大量时间看书看视频结果一开口就卡壳。输出的形式有三种按效果排序是讲给别人听 自己对着白板讲 默写。具体节奏我一般是这样安排的。第一周扫 S 档和 A 档的是什么层快速建立全景第二周攻 S 档的为什么层重点是源码和设计动机第三周做输出训练每天挑三个知识点限时三分钟讲清楚第四周查漏补缺把前三周标记的疑难点再过一遍。6.2 笔记怎么记三段式结构我的笔记模板固定三段结论一句话说清是什么控制在 50 字以内。推导为什么这么设计或者从问题到方案的完整链路。追问列出可能被继续问的两到三个问题每个附一句话提示。举个例子HashMap 的默认容量那条笔记是这样写的结论默认容量 16负载因子 0.75。 推导容量必须是 2 的幂因为取模可以用hash (n-1)代替位运算比除法快负载因子 0.75 是时间和空间的折中太高冲突多太低浪费空间。 追问为什么阈值是 8 和 6扩容时怎么重新计算下标并发扩容会怎样这种结构的好处是复习的时候能一眼看到知识边界而不是面对一大段文字不知道重点在哪。6.3 模拟面试找真人别自言自语对着镜子练有用但效果远不如找个真人。我在组里拉了个小群每周互相面一次每人半小时全程录音。录完回放的时候会发现很多自己意识不到的问题语速太快、逻辑跳跃、答到一半自己跑偏、口头禅太多。模拟面试的题目不要提前对答案就按真实流程来。面试官要刻意往下追问两层哪怕候选人答对了也要继续问还有呢。这个压力训练非常有效练过几轮之后真面试时的追问就不会那么慌了。7. 常见问题与避坑实录最后把踩过的坑和常见疑问整理一下这部分是纯经验别处不太容易看到。问题我的处理方式背了忘、忘了背循环焦虑改背诵为推导只记因果链不记原话遗忘率高说明是死记没理解知识点太多不知道从哪开始严格按 S/A/B/C 优先级走先吃透八个 S 档模块剩下的遇到再补面试时被问懵了怎么办承认不会然后给出你的推理路径和验证思路面试官看重的是思维过程项目经验和八股文怎么结合每个知识点都往自己项目上挂一个实例比如线程池参数就结合你实际配的值讲面试官一直追问是不是在刁难追问到答不出来是正常流程目的是探边界不是你答错了不同公司问的重点差太多面试前查一下岗位 JD 和团队方向做针对性复习别一套内容打天下有几个坑我要单独拎出来说。坑一只背不写。很多人看线程池那七个参数觉得记住了真让他手写一个 ThreadPoolExecutor 的构造参数顺序就乱了。手写类的东西一定要落到纸上或者编辑器里敲一遍肌肉记忆比脑子可靠。坑二忽略版本差异。JDK 8 和 JDK 17 在 GC、字符串拼接、集合实现上都有差异。面试官如果问你用的哪个版本答不上来就很尴尬。复习的时候顺手记一下关键特性的版本节点成本很低但收益很高。坑三把八股文和项目割裂。我见过基础题答得很好但一问这个知识点你们项目里怎么用的就沉默的人。基础和项目应该是互相印证的比如你项目里用了 Redis那缓存三大问题就一定要能结合业务场景讲。面试官更信任那些能把理论和实践串起来的人。坑四追求全覆盖。八股文的边界是无限大的你永远背不完。与其焦虑覆盖面不如保证重点模块的深度。八个 S 档模块答得滴水不漏比二十个模块各答一半强得多。我自己的体会是八股文总结这东西写到第三版才算能看。第一版是抄题第二版是分类第三版才开始有自己的推导和追问设计。如果你现在还在第一版阶段别急着扩大规模先把手上这些题每条都补上为什么和追问两栏效果比你再多背两百道题要好得多。另外一个小技巧把手机备忘录利用起来通勤路上想到哪个知识点没讲透随手记一句晚上回来补进笔记里积少成多几个月下来这份总结的可信度会完全不一样。
返回列表