工作几年,面过不少试,也被面过不少次,大部分面试都平平淡淡,问几个技术问题、聊聊项目经历就结束了。但有一次面试让我印象特别深刻,甚至可以说是被问到崩溃。

那是我跳槽的时候面的一家大厂,投的是高级开发工程师的岗位。本来以为自己工作几年,技术还不错,面试应该没问题,结果从下午两点面到五点,三轮技术面加一轮HR面,被问得怀疑人生,走出大楼的时候整个人都是懵的,甚至一度怀疑自己是不是不适合做程序员。

今天想把那次经历记录下来,不是为了吐槽面试官或者公司,而是为了反思和总结。那次面试虽然让我很受挫,但也让我看清了自己的很多不足,对我后来的技术成长和职业发展都有很大的影响。希望这篇文章能给正在找工作或者准备面试的朋友一些启发。

一、面试前的准备

先说说面试前的情况。当时我在一家中小公司工作了三年,做的是后端开发,技术栈是Java + Spring + MySQL,项目经验主要是电商和支付相关的。觉得在当前公司遇到了瓶颈,技术成长变慢了,就想跳到大厂去锻炼一下。

投简历之前,我也做了一些准备:

  • 把Java核心知识点过了一遍,集合、并发、JVM这些。
  • 把Spring、MyBatis等常用框架的原理看了看。
  • 把MySQL的索引、事务、锁这些知识点复习了一下。
  • 把自己做过的项目整理了一下,准备了项目介绍和难点分析。
  • 刷了一些算法题,主要是LeetCode上的中等难度题目。

当时自我感觉还不错,觉得该准备的都准备了,面试应该没问题。现在回头看,那些准备都太表面了,很多知识点只是知道"是什么",但没有深入理解"为什么"和"怎么用",项目经验也没有提炼出深度,这也是后来面试被问崩的主要原因。

简历投出去之后,很快就收到了面试邀请,约了一个周三的下午两点面试。我还特意请了一天假,上午又过了一遍知识点,中午吃了饭就过去了。现在想想,当时还是太轻敌了。

二、第一轮技术面:基础不牢,地动山摇

第一轮是个年轻的面试官,看起来工作三五年的样子,态度还不错,先让我做了自我介绍,然后就开始问技术问题。

刚开始问的都是比较基础的问题,比如HashMap的原理、ConcurrentHashMap怎么实现线程安全的、ArrayList和LinkedList的区别,这些我都答上来了,心里还挺得意的,觉得面试也不过如此。

但很快,问题就深入了。面试官问:"你说ConcurrentHashMap在JDK1.8之后用了CAS + synchronized来保证线程安全,那为什么不用ReentrantLock?synchronized和ReentrantLock的区别是什么?什么场景下用哪个?"

这个问题我就有点懵了。我知道synchronized和ReentrantLock的一些区别,比如synchronized是JVM层面的,ReentrantLock是API层面的,ReentrantLock可以公平锁、可以中断、可以超时获取,但ConcurrentHashMap为什么用synchronized不用ReentrantLock,我还真没想过。我支支吾吾说了一些,比如synchronized性能优化了、更轻量之类的,但明显答不到点上,面试官也没追问,笑了笑就继续下一个问题了。

然后又问了JVM的问题:"你说你了解JVM垃圾回收,那说说G1垃圾收集器的原理,它和CMS的区别是什么?G1的Region是怎么划分的?为什么要分Region?大对象怎么处理?"

这个问题我又卡住了。我知道G1是基于Region的,也知道它和CMS的一些区别,但Region具体怎么划分、为什么这么划分、大对象怎么处理,这些细节我就说不清楚了。我只能说个大概,很多细节都答不上来。

接下来问了MySQL的问题:"你说你了解MySQL索引,那说说B+树的结构,为什么索引用B+树而不用B树或者红黑树?联合索引的最左前缀原则是什么?什么情况下索引会失效?"

B+树和最左前缀我还能答上来,但索引失效的场景我就说得不全了,只说了几个常见的,比如用了函数、用了like左模糊、类型转换,但还有一些场景比如or连接、!=、not in这些,我就没说全。

第一轮面了大概一个小时,结束的时候面试官说:"基础还可以,但有些知识点理解得不够深入,回去再看看。"然后就让我等下一轮。

我当时心里就有点慌了,觉得自己答得不好,但还抱有希望,觉得可能下一轮会好一点。现在回头看,第一轮的问题其实都是基础,但我只是停留在"知道"的层面,没有深入理解,所以一被追问就露馅了。基础不牢,地动山摇,这句话真的没错。

三、第二轮技术面:项目没深度,被问到崩溃

第二轮是个资深的工程师,看起来工作七八年了,态度比较严肃,没有太多寒暄,直接让我介绍项目。

我选了自己最得意的一个项目,就是支付系统的开发,介绍了项目的背景、架构、我负责的模块、遇到的难点和解决方案。我自认为介绍得还不错,条理清晰,也突出了自己的贡献。

但面试官听完之后,开始追问,这一追问就把我问崩了。

第一个问题:"你说你们的支付系统用了分布式事务,用的是TCC模式,那说说TCC的三个阶段分别是做什么的?你们的Try阶段是怎么实现的?Confirm和Cancel阶段如果失败了怎么办?有没有出现过空回滚或者悬挂的问题?怎么解决的?"

这个问题我就开始冒汗了。TCC的三个阶段我知道,但具体到我们项目里Try阶段怎么实现的,我只能说个大概,因为当时我主要负责的是业务逻辑,分布式事务的框架是另一个同事搭的,我只是用,没有深入研究过实现细节。Confirm和Cancel失败了怎么处理,我知道有重试机制,但具体怎么重试、重试失败了怎么办,我就说不清楚了。空回滚和悬挂的问题,我甚至都没听说过,只能说"我们没遇到过这个问题"。

面试官看我答不上来,也没为难我,继续问下一个问题:"你说你们的系统做了分库分表,用的是Sharding-JDBC,那说说你们的分片策略是什么?为什么这么分?跨分片的查询怎么处理?分布式ID怎么生成的?有没有出现过跨库事务的问题?"

这个问题我更懵了。分片策略我知道是按用户ID取模,但为什么这么分,我只能说"因为查询大多是按用户ID查的",但更深入的分析,比如数据量增长了怎么办、热点用户怎么处理,我就没想过。跨分片查询我们基本没做,因为业务上避免了,但面试官追问如果必须要跨分片查询怎么办,我就答不上来了。分布式ID用的是雪花算法,但雪花算法的原理、时钟回拨怎么处理,我也说不清楚。

接下来又问了几个问题,都是关于项目的深入追问,比如:

  • "你说你们做了缓存,用的是Redis,那缓存和数据库的一致性怎么保证的?有没有出现过缓存穿透、缓存击穿、缓存雪崩的问题?怎么解决的?"
  • "你说你们做了限流,用的是什么算法?令牌桶和漏桶的区别是什么?分布式限流怎么实现的?"
  • "你说你们做了消息队列,用的是RabbitMQ,那消息的可靠性怎么保证的?有没有出现过消息丢失或者重复消费的问题?怎么解决的?"

这些问题,我每个都只能答个皮毛,一被深入追问就答不上来了。因为很多东西我只是用了,知道怎么配置、怎么调用,但没有深入理解原理,也没有思考过为什么这么做、有没有更好的方案、出了问题怎么排查和解决。

第二轮面了大概一个半小时,到后来我已经有点崩溃了,脑子一片空白,很多问题都不知道怎么回答,只能说"这个我没深入研究过""这个我们没遇到过"。面试官的表情也越来越严肃,最后说:"你的项目经验有,但深度不够,很多东西只是会用,没有理解原理,也没有自己的思考。"然后就让我出去等。

我走出会议室的时候,整个人都是懵的,手心全是汗,甚至有点想哭。那是我第一次在面试中被问得这么惨,感觉自己这几年的工作都白做了,什么都不会。

四、第三轮技术面 + HR面:硬着头皮撑完

休息了大概十分钟,又来了第三轮技术面,是个架构师级别的面试官,看起来工作十年以上了。

经过第二轮的打击,我当时已经有点破罐子破摔了,心里想着"反正已经这样了,随便吧"。但还是硬着头皮继续面。

第三轮的问题更偏向架构和设计,比如:

  • "如果让你设计一个秒杀系统,你会怎么设计?从前端到后端,从数据库到缓存,说说你的思路。"
  • "如果让你设计一个短链接系统,你会怎么设计?怎么生成短码?怎么存储?高并发下怎么优化?"
  • "你怎么理解微服务架构?微服务的优缺点是什么?你们项目里是怎么做服务拆分的?"

这些问题,我因为第二轮被打击得有点懵,而且本身架构设计的经验也不多,所以答得也不好,只能说个大概的思路,很多细节都考虑不到。但第三轮的面试官比较有耐心,会引导我思考,我答不上来的地方他会给提示,然后一起讨论。虽然答得不好,但至少没有第二轮那么崩溃。

第三轮面了大概一个小时,结束之后就是HR面。HR面就比较常规了,问问为什么离职、职业规划、期望薪资、对公司的了解之类的,我机械地回答了一下,脑子已经不太转了。

全部面完已经五点多了,从下午两点到五点,面了三个多小时。走出大楼的时候,天已经有点暗了,我站在路边,感觉整个人都被掏空了,甚至一度怀疑自己是不是不适合做程序员,是不是该转行。

五、面试后的反思和总结

那次面试之后,我低落了好几天,觉得自己很失败,工作几年了还这么菜。但低落之后,我开始冷静下来反思,这次面试虽然失败了,但也让我看清了自己的很多问题,这些问题如果不解决,下次面试还是会失败。

我总结了自己的几个主要问题:

1. 基础不扎实,很多知识点只知其然不知其所以然。比如ConcurrentHashMap为什么用synchronized不用ReentrantLock、G1的Region怎么划分、索引失效的所有场景,这些基础知识点我都只是知道个大概,没有深入理解原理。面试官一追问就露馅了。

2. 项目没有深度,很多东西只是会用,没有理解原理,也没有自己的思考。比如分布式事务、分库分表、缓存、消息队列,这些我在项目里都用过,但只是会配置、会调用,没有深入理解原理,也没有思考过为什么这么做、有没有更好的方案、出了问题怎么排查。面试官一深入追问项目细节,我就答不上来了。

3. 架构设计能力不足,缺乏全局视野和系统思考。对于秒杀系统、短链接系统这类设计题,我只能说个大概的思路,很多细节和边界条件都考虑不到,缺乏从全局角度设计系统的能力。

4. 表达能力有待提高,很多东西我知道,但说不清楚。面试的时候,有些知识点我其实是了解的,但一紧张就说不清楚,逻辑混乱,没有条理,让面试官觉得我不懂。

想清楚了这些问题之后,我开始制定学习计划,针对性地提升自己:

  • 补基础:把Java核心、JVM、并发、MySQL、Redis、消息队列这些核心知识点重新学一遍,这次不是只看"是什么",而是深入理解"为什么"和"怎么实现的",每个知识点都要能讲清楚原理。
  • 深挖项目:把自己做过的项目重新梳理一遍,每个技术点都要搞清楚原理,思考为什么这么做、有没有更好的方案、出了问题怎么排查和解决。把项目里的难点和亮点提炼出来,形成自己的项目故事。
  • 练设计:多看一些架构设计的文章和案例,练习系统设计题,培养全局视野和系统思考能力。每个设计题都从需求分析、架构设计、技术选型、性能优化、容错降级等多个角度去思考。
  • 练表达:把每个知识点和项目经验都用自己的话讲出来,录音或者讲给朋友听,看看能不能讲清楚、讲得有条理。多模拟面试,锻炼临场表达能力。

那段时间我每天下班之后都学习到很晚,周末也在学习,虽然很辛苦,但能感觉到自己在进步。大概过了三个月,我又面了几家公司,这次就顺利多了,很多之前答不上来的问题都能答上来了,最后也拿到了满意的offer。

六、一些面试的建议

最后,结合那次被问崩的经历和后来的面试经验,给正在找工作或者准备面试的朋友一些建议:

1. 不要轻敌,认真准备每一次面试。不管你觉得自己技术多好,都要认真准备,大厂的面试深度和广度都不是小公司能比的,不要用小公司的标准来要求自己。

2. 基础很重要,一定要扎实。大厂面试很看重基础,很多问题都是基础知识点的深入追问。不要觉得基础简单就不重视,基础不牢,再高深的技术也都是空中楼阁。

3. 项目要有深度,不要只停留在"会用"的层面。面试官问项目,不是想听你做了什么,而是想听你怎么做的、为什么这么做、遇到了什么问题、怎么解决的。每个技术点都要深入理解原理,有自己的思考和总结,这样才能在面试中经得起追问。

4. 不要不懂装懂,不会就说不会。面试中遇到不会的问题很正常,不要不懂装懂、胡编乱造,面试官一眼就能看出来,反而印象更差。坦诚地说"这个我没深入研究过",然后可以说说自己的理解或者类似的知识,比硬撑着答要好得多。

5. 面试是双向选择,不要太卑微。面试是公司在选你,也是你在选公司,不要因为想进这家公司就放低姿态、卑微讨好。保持平等和自信,展示真实的自己就好。即使面试失败了,也不代表你不行,只是可能不匹配而已。

6. 每次面试之后都要复盘和总结。不管面试成功还是失败,都要复盘,想想哪些问题答得好、哪些答得不好、为什么答不好、怎么改进。每次面试都是一次学习和成长的机会,不要面完就忘了。

七、写在最后

那次被面试官问到崩溃的经历,虽然当时很受挫、很打击信心,但现在回头看,反而是我职业成长的一个转折点。它让我看清了自己的不足,也让我有了明确的学习方向和动力。如果没有那次打击,我可能还在自我感觉良好,不会有后来的快速成长。

所以,如果你也在面试中被问到崩溃、被打击到怀疑人生,不要灰心,不要否定自己。这不是坏事,它说明你遇到了比你强的人,看到了自己的不足,这正是成长的机会。把这次经历当成一次反馈,针对性地提升自己,下次一定会更好。

面试只是人生中的一小段经历,它不能定义你的价值,也不能决定你的未来。真正重要的是持续学习、持续成长的能力。只要你一直在进步,就一定能找到适合自己的机会,成为更好的自己。

与所有正在求职路上奋斗的朋友共勉。