1. 他凭什么能进面试间?先聊聊谢飞机这个人
那天下午我在某大厂做技术面试官,一天排了五场,前几个都是标准简历模板:名校背景、三段大厂实习、项目里写满高并发和微服务。聊到第四十个问题的时候,我基本能把对方的技术栈猜个八九不离十。直到谢飞机推门进来,我才意识到这天没那么容易结束。
谢飞机的简历其实很有意思。普通二本,没有大厂实习,项目经历写了一个电商后台和一个IM聊天系统,技术栈是Java + Spring Boot + MySQL + Redis,看起来平平无奇。但简历上有两个细节引起了我的注意:一是他在一个开源社区维护过一个小工具,star数不多,但issue回复得很勤快;二是他把自己一段线上故障排查经历写进了自我评价,细节完整,时间线清晰,不像是编的。
后来我才知道,他能走到面试这一轮,靠的确实是这两件事。
先说开源社区那个小工具。它是个基于Netty的轻量级RPC框架,说实话代码量不大,设计也不算复杂,但谢飞机持续维护了八个月,修复了十几个issue,写了使用文档,还录了两个演示视频。面试官看简历,最怕看到那种"参与XX系统开发"一笔带过的描述,反而这种有明确交付物、有持续投入的小项目,能让人产生"这人至少真的写过代码"的判断。
再说那个线上故障排查经历。他说的是某天凌晨线上订单接口突然变慢,平均耗时从80ms飙升到2秒,他通过一步步排查日志、分析慢查询、定位到一条SQL没走索引,最后在凌晨四点修复上线。整个经过写得很有画面感,连当时的告警截图都粘上去了。
所以,我对他最初的预期是:基础可能一般,但实战经验应该不差,值得花四十分钟聊一聊。
现在复盘整场面谈,我最大的感受是:谢飞机确实是个"水货程序员",但这个"水"不是指能力差,而是指他的知识结构和科班出身的候选人完全不一样。他像是从实战里"长"出来的程序员,知道怎么把事情做对,却说不清楚为什么这样做是对的。这种候选人,恰恰是面试中最难评价的类型。
2. 面试官的第一个问题就问了HashMap,他的答案让我差点没绷住
我在Java面试里几乎必问HashMap,不是因为这道题有多新,而是因为它能像X光一样把候选人的基础照得一清二楚。HashMap涉及哈希算法、数据结构、并发安全、扩容机制、红黑树退化,每一个点都能往深处挖。谢飞机的表现,从这里就开始暴露"水"的属性了。
2.1 "HashMap的底层数据结构是什么?"——他的版本和教科书版本
我问他第一个问题:"HashMap在JDK 8里的底层数据结构是什么?"
谢飞机想了想说:"就是数组加链表,链表太长会变成红黑树。PHP也差不多是这样。"
我追了一句:"什么叫链表太长?多长算长?"
他说:"好像是8个吧,到8个就转红黑树。"
表面看他答对了,但那个"好像是"让我立刻警觉。教科书答案是:数组加链表,链表长度达到8且数组长度大于等于64时,链表转为红黑树。谢飞机的答案漏掉了"数组长度大于等于64"这个前置条件,这恰恰是我最想听到的细节。
这里补充一下为什么一定要有"数组长度大于等于64"这个条件。如果数组长度不够,即使链表很长,说明hash碰撞很严重,但此时应该先扩容数组而不是转红黑树。因为红黑树本身有额外的节点结构开销,在数据量不大的时候,遍历链表反而比维护红黑树的平衡更快。JDK源码里写得很清楚,链表长度8只是一个触发阈值,真正的决策逻辑还要看table数组长度。
我问谢飞机:"如果数组长度是16,链表已经到8了,会发生什么?"
他愣了两秒,说:"应该会转红黑树吧?不是到8就转吗?"
我摇了摇头:"不会转,会先扩容到32,再判断。只有扩容之后链表还是长,才会考虑转红黑树。"
他"哦"了一声,然后补了一句:"这个细节我确实没注意,面试回去得补补。"
这句话倒是很实在,比那些不懂装懂、强行编造的候选人强不少。
2.2 他输在了扩容机制,但赢在了"会根据自己的经验联想"
HashMap这题的第二个送命题是扩容机制。我问他:"HashMap什么时候扩容,扩容时元素怎么迁移?"
谢飞机的答案让我眼前一亮:"容量超过负载因子乘以当前容量的时候就扩容,默认负载因子0.75。但迁移的时候不是全部rehash,JDK 8有个优化,元素要么留在原位置,要么移动到原来的两倍位置。"
我问他:"为什么是两倍?"
他说:"因为容量是2的幂,所以取模操作可以用位运算代替,hash值按位与容量减一。扩容之后容量变成原来的两倍,元素的位置就看新增的那一位是0还是1,是0就留在原位,是1就加一个旧容量的偏移。"
这个答案说实话超过了我对"水货"的预期。虽然他前面连红黑树的条件都说不全,但讲到扩容迁移时,他是真懂。他解释的时候还举了个例子,说这就像搬家时先看门牌号的最后一位,是偶数就原地不动,是奇数就搬到隔壁楼,一句话就把JDK 8扩容的精髓讲明白了。
我后来复盘时想到,这个"水货"最典型的特征就是:知识结构像是个筛子,有的地方密不透风,有的地方空洞洞的。扩容这块他之所以懂,可能是真的在线上遇到过容量不够导致的性能问题,自己查过源码;而红黑树转换这种偏理论的知识,他平时用不上,自然就模糊了。
HashMap这一题过后,我对谢飞机的整体判断是:实习水准偏下,但学习能力和实战敏感度,比很多科班候选人要强。这类人如果补上基础理论的短板,进步会非常快;但如果一直靠实战经验裸奔,迟早会在大流量和复杂业务面前翻车。
2.3 面试官视角:HashMap这道题,我到底在筛什么
很多候选人以为我在考背诵,其实我真正想观察的是三件事。
第一,他知不知道"为什么"。能说出数组加链表的大有人在,但能解释清楚为什么链表转红黑树的条件是8和64的人很少。前者是背过八股文,后者是理解了哈希冲突的本质。
第二,他能不能把知识串联起来。HashMap的扩容机制和Redis的渐进式rehash、ConcurrentHashMap的分段锁设计,背后都是同一个思想:用空间换时间,用分片换并发。能把这些串起来讲的人,说明他对技术有体系感,而不是东一榔头西一棒子。
第三,他面对自己不会的东西时的态度。谢飞机不会的部分,他直接说"这个细节我确实没注意",不会的部分,他反而能用自己的经验补充出有理解深度的内容。这一点比答案本身更重要,因为真实工作里,你不会的东西永远比会的多,能不能诚实地承认、快速地补上,往往决定了一个人能走多远。
提示:以后准备HashMap相关问题,别只背结论。试着把"为什么负载因子是0.75而不是0.5或1.0""为什么容量必须是2的幂""为什么链表阈值是8"这几个为什么都讲明白,面试官对你的印象会完全不一样。
3. 问到JVM调优和慢SQL排查时,他差点把天聊死,结果靠一段真实经历翻盘了
HashMap只是开胃菜。中场休息后,我换到了实战场景题——这是最能区分"背题选手"和"真干活的人"的环节。谢飞机的表现,也在这里出现了剧烈的、坐过山车式的波动。
3.1 "线上接口突然变慢了,你怎么办?"——他先给了一个公式化答案
我问谢飞机:"假设线上有个订单查询接口,平时平均耗时100ms,今天突然变成2秒,QPS并没有明显上涨,你怎么排查?"
谢飞机想了一会儿说:"先看监控,看看是哪个环节慢,然后看CPU、内存、磁盘IO,再用Arthas看下有没有锁竞争或者频繁GC。"
这个答案很标准,标准得像教科书里的参考答案。但面试官要的不是标准答案,而是具体的排查链路。我追问:"看监控发现CPU正常、内存正常、GC也正常,你还怎么看?"
他卡住了,想了很久,说:"那就看一下慢查询日志?"
我说:"可以,但要不要看一下数据库的连接池?如果连接池被打满了,即使SQL本身不慢,接口也会被阻塞。还有,这个接口调了下游服务,下游慢了你怎么办?"
这几个追问直接把谢飞机逼到了墙角。他意识到自己刚才的回答只是背了一套排查流程,并没有真正解决过这类问题。他沉默了一会儿,说:"我刚才说的确实太泛了,实际遇到过一次类似的问题,我讲下那个场景吧。"
3.2 他讲的那个凌晨四点的故事,暴露了他的排查思路有严重缺陷
谢飞机讲的是他上班时遇到的一个真实故障。某个深夜,订单查询接口突然变慢,他的第一反应是看监控,发现是一条SQL的耗时从100ms涨到了1.8秒。他打开执行计划,发现那条SQL的表数据量翻了一倍,但索引没变,导致优化器选择了全表扫描。
他当时的解决办法是:给那条SQL涉及的字段加了一个组合索引,加了之后接口很快就恢复了。
我当时就说了一句话提醒他:"你有没有想过,为什么数据量翻倍之后,优化器才开始选择全表扫描?"
他愣住了,说:"可能是统计信息过期了吧。"
我点头:"这才是问题真正的根源。索引一直挂在字段上,优化器之前一直通过索引扫描,突然变成全表扫描,说明MySQL的优化器判断全表扫描的成本更低。数据量翻倍只是诱因,真正的病根是统计信息不合理,导致优化器做出了错误的执行计划选择。你加索引相当于换了条路,但没有解决优化器为什么走错路的问题。如果再过两个月数据量再翻一倍,你可能会需要再造一个新的索引——或者说,下次可能就不是加索引能解决的了。"
谢飞机听到这里,表情明显从自信变成了若有所思。他想了想说:"你说得对,我当时确实只是把表面问题解决了,没有深挖优化器的决策逻辑。如果当时看一眼information_schema里的统计信息,做一下ANALYZE TABLE,可能问题几个月之内都不会复发。"
这个坦诚让他今天在我这里的分数,拉了回来不少。
3.3 从"背流程"到"讲经历",他差的是系统化的方法论
我后来在复盘笔记里写了一段话:谢飞机不是没经验,而是经验没有上升到方法论。
他的排查路径是"遇到问题—查日志—改配置—验证发布",这套路在小公司、流量没那么大的场景下确实够用。但到了大厂,流量高一个数量级,问题就不是单点故障那么简单。接口慢可能是数据库慢,也可能是下游慢,还可能是因为缓存穿透导致数据库被打爆,甚至可能是GC停顿引起的一连串连锁反应。排查问题时需要建立"系统视角",从入口到出口逐层过滤,而不是凭感觉去翻日志。
我给他讲了几个在团队里复用的排查框架:
- 先看整体再看局部:先确认是入口慢还是下游慢,用全链路追踪把时间消耗拆开,再聚焦到最耗时的环节。
- 先看资源再看代码:CPU高不一定代表代码有问题,也可能是一台机器被其他应用抢占资源,先把资源水位看清楚。
- 先看缓存再看数据源:缓存命中率掉了,问题大概率在缓存侧,不要一头扎进数据库排查。
- 先看慢SQL再看锁竞争:慢查询日志定位到SQL之后,还要看有没有锁等待、有没有统计信息过期,这两个因素经常被人忽略。
- 不要反复重启:重启能解决一时的问题,但掩盖了根本原因。线上服务重启一次,现场就毁掉一次。
他说:"你们大厂排查问题都是这样一套一套的吗?"
我说:"也不是,都是踩坑踩出来的。你以为我们一生下来就会看执行计划?也是线上事故教育出来的。"
他听完突然笑了,说:"那我今天也算被教育了一回。这个凌晨四点的故事我一辈子都忘不了。"
3.4 这个环节到的结论:会干活的人,和会总结的人,中间差着一层抽象能力
面试结束后我单独给谢飞机的这个环节打了分:经验真实度满分,方法论的抽象度偏低。
很多候选人给我的感觉是"什么都会一点,但什么都说不深"。谢飞机正好反过来——他是"亲手解决过问题,但说不清解题背后的通用规律"。对面试官来说,这种人其实比前一种更值得培养。因为经验是可以迁移的,只要给他一个方法论框架,他把以前踩过的坑全部套进去,进步速度会非常快。
所以如果你也是那种"项目经验有,但八股文背不好"的程序员,面试前可以刻意训练一件事:把每一个做过的任务,套进"问题—原因—方案—预防"这个框架里讲一遍。你会发现,同样的经历,用框架讲出来,专业度能提升一倍。
4. 最紧张的环节来了:让他手写一个线程安全的计数器,他写了三版才写对
面试推进到第三十分钟,我开始进入写代码环节。说实话,前面的对话虽然起伏大,但我对谢飞机的动手能力还算认可,直到我让他现场写一个线程安全的计数器。
4.1 第一版代码:他用了AtomicInteger,但把compareAndSet的用法写错了
我出的题目很简单:"写一个线程安全的计数器,支持increment和get两个方法。"
谢飞机拿起笔,很快写了第一版:
import java.util.concurrent.atomic.AtomicInteger; public class Counter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.compareAndSet(count.get(), count.get() + 1); } public int get() { return count.get(); } }我看了两秒,差点笑出来。他用了AtomicInteger,方向没错,但compareAndSet(count.get(), count.get() + 1)这个写法在并发场景下是有问题的。因为count.get()调用了两次,在两次调用之间,其他线程可能已经修改了count的值,导致CAS的判断基准已经过期了。
谢飞机自己写完也有点不确定,问我:"这样写应该没问题吧?"
我没直接回答,反问他:"你觉得compareAndSet和incrementAndGet有什么区别?"
他说:"incrementAndGet就是自增,compareAndSet是比较并设置。"
我说:"那你觉得你上面那段代码,在县城并发的情况下,会不会丢更新?"
他沉默了几秒,突然拍了下大腿:"会!compareAndSet里的expected值应该是我读的时候的值,但我连续调用了两次get,第二次get可能已经不是第一次get的那个值了,如果第一次get拿到的是1,这时候别的线程把它改成了2,我第二次get拿到2,compareAndSet发现expected=2,actual=2,就不会更新,相当于这次自增丢了。"
他顿了顿,然后说:"不对,更准确地说,是我第一次get拿到1,准备设置成2,但另一个线程先把它改成了2,我第二次get拿到2,expected和actual相等,compareAndSet就不会执行设置操作,我的自增就白做了。"
我点头:"你这不是挺明白的吗?那为什么不直接用incrementAndGet?"
他挠了挠头说:"可能是习惯性想用CAS演示一下自己懂原理,结果翻车了。"
4.2 第二版代码:他改对了CAS,却又踩了volatile的坑
谢飞机试着重写第二版:
import java.util.concurrent.atomic.AtomicInteger; public class Counter { private AtomicInteger count = new AtomicInteger(0); public void increment() { while (true) { int expected = count.get(); int next = expected + 1; if (count.compareAndSet(expected, next)) { break; } } } public int get() { return count.get(); } }这一版是对的。自旋CAS保证了线程安全,increment方法在失败时会重试,直到成功为止。但他写完这版后,突然问了我一句:"如果用synchronized是不是更简单?"
我说:"是更简单,但那正是我想说的下一件事。你刚才第一版用compareAndSet,其实是为了炫技。第二版用自旋CAS,已经正确了,但你能不能解释一下,为什么这里不能用volatile变量的i++?"
他说:"因为i++不是原子操作,是读改写三步,volatile只能保证可见性,不能保证原子性。"
我又追问:"那volatile能不能保证有序性?"
他想了很久,说:"可以禁止指令重排,但……好像和这个题没什么关系。"
我说:"确实,但如果你想炫技,应该把volatile和synchronized的选择讲清楚,而不是用CAS写一个容易出错的循环。面试官想看的是你在多个方案之间做选择的思考过程。如果你直接给我synchronized版本,我反而会觉得你更稳。"
他沉默了一下,开始写第三版。
4.3 第三版代码:他放弃了花活,老老实实选了synchronized
import java.util.concurrent.atomic.AtomicInteger; public class Counter { private long count = 0; private final Object lock = new Object(); public void increment() { synchronized (lock) { count++; } } public long get() { synchronized (lock) { return count; } } }他写完放下笔说:"其实我想了一下,这个场景下synchronized就够了。自旋CAS在高并发下会导致大量CPU空转,如果竞争激烈,还不如让线程阻塞。而且读方法也需要加锁,不然读到中间状态虽然long在64位JVM下不会出现撕裂写,但如果不加锁,读到的可能是旧值,还是需要一个内存屏障。"
我笑了:"你是学聪明了还是认怂了?"
他说:"是我发现自己刚才太想表现,反而暴露了短板。做技术还是老老实实,先把方案说清楚再动手。"
这三版代码,其实比HashMap那道题更能说明谢飞机的特点。他不是不懂并发,但他对技术方案的选择缺少一种"得体感"——他总想用最复杂的东西证明自己,却忘了最可靠的东西往往是最简单的。这种心态在初中级工程师里很常见,我自己当年也有过一段"逢并发必用CAS、逢缓存必上Redis"的时光,后来被线上故障教育过几回,才慢慢学会"先简单后复杂"的做事原则。
4.4 从这一段看出的关键信号:能不能从"会写"到"会选"
写代码环节结束后,我给谢飞机的评价是:编码能力合格,架构决策能力需要大量实战磨炼。
会写代码的人很多,能在多个方案里选出最适合当前场景的人很少。线程安全计数器用synchronized还是CAS,本身没有绝对的对错,但一个成熟的工程师应该能说出来:在竞争不激烈时,synchronized有锁升级机制,性能和CAS差不多;在竞争激烈时,CAS会空转消耗CPU,synchronized会让线程阻塞;如果追求极致性能,LongAdder可能是更好的选择。
谢飞机最后明白了这个道理,这是我认为他值得给个机会的重要原因。
5. 真正让面试官破防的,不是技术问题,是那句"我可能不适合你们"
面试进行到第四十五分钟的时候,我准备收尾了。按照流程,我应该问最后一个问题:"你有什么想问我的?"这是候选人了解团队的机会,也是我们观察候选人关注点的窗口。谢飞机问了一个我没预料到的问题。
5.1 他问我的那个问题,差点让我把杯子里的水喷出来
谢飞机问我:"我面到这儿,感觉你问的所有问题我都懂,但我都不够深入。你觉得我适合这个岗位吗?"
说实话,在面试中听到这种话的概率极低。大多数候选人要么表现得自信满满,要么在不确定的时候硬撑着。谢飞机这句话的潜台词是:"我知道自己水,但我不知道你到底怎么看我的。"
这个过程有点像你去医院看病,医生问了一堆症状,你突然问医生:你觉得我这病还能治吗?医生没法直接回答,因为他还得继续检查才能下结论。
但这个问题也透露出一个积极的信号:谢飞机对自己的能力边界有清醒的认知。他知道自己哪里行、哪里不行,这种自我认知能力,恰恰是很多看起来很强但实际很虚的候选人最缺的东西。
5.2 我给他的现场反馈:"你不是水,你是水得不够深"
我当时给他的原话是:"你不是水,你是水得不够深。你懂的东西很多,但每一项都只懂了个表面。HashMap你懂,但只懂一半,扩容机制你是真懂,红黑树你就模糊了。慢SQL排查你懂,但只懂到加索引为止,没往下想到统计信息的问题。这就像一个水桶,板子参差不齐,有的长有的短,装不了多少水。"
"一个合格的程序员需要的是桶底不漏,也就是基础理论能兜底;而一个优秀的程序员需要的是每一块板子都能拉到同样的高度,也就是知识体系完整。你现在的问题不是桶太浅,是板子不齐。"
他听完这句话,突然认真起来说:"这个比喻挺形象的。我知道自己短板在哪里了。"
我继续说:"你对技术的热情是真的,从你维护那个小项目就能看出来。你缺的是系统化补课,把Java并发、JVM内存模型、数据库索引原理这些基础一块一块补齐。补完之后,你的实战经验加上理论支撑,会很值钱。"
5.3 我告诉他为什么"水货"反而可能是潜力股
其实面试官之间有一个共识:招聘的时候,最怕的不是"水",而是"装"。
"装"的候选人,简历写满了架构师级别的技术堆叠,问到细节就闪烁其词,用"这个我们内部封装了""那个涉及公司机密不方便说"来打太极。这种人招进来,往往是最坑的,因为他在简历上的每一条承诺,都需要团队花时间验证甚至给他擦屁股。
谢飞机的"水",是真实水平的"水"。他在面试中展现出来的每一项短板都非常具体,每一项长处也都非常具体。这种真实的候选人,我们可以有针对性地给他补课,而不是像"装"的候选人一样,连知识盲区在哪都找不到。
我给团队的建议是:如果要招应届生或者三年以下的初级工程师,与其招一个看似全面但哪里都不突出的"装"人,不如招一个真实有短板但有热情、有学习能力的"水"人。后者可以培养,前者很难教。
6. 面试结果出来了:他拿到了一个让人意外的评级
面试结束后,我在系统里给谢飞机提交了评价。最终结果既不是通过,也不是淘汰,而是给出了一个"有条件通过"的评级。
6.1 面试评分的三个维度:基础理论、实战经验、学习潜力
团队用的面试评分卡有三个维度:理论基础、实战经验、学习潜力。我给谢飞机的打分是这样的:
- 理论基础:6/10。HashMap、JVM、并发这些都能说出个大概,但细节漏洞多,典型的知识体系"散装"状态。
- 实战经验:8/10。真实处理过线上故障,有踩坑记录,动手能力强,写代码速度快。
- 学习潜力:9/10。对自己短板有清醒认知,态度诚恳,沟通流畅,一点就透。
这个组合打分,最终算出来的总评是"偏下中上"。如果用一句通俗的话说,就是:人不错,值得培养,但需要有人带着系统地补课。
6.2 最后一分钟,他又问了一个让我哭笑不得的问题
就在我准备按"结束面试"按钮的时候,谢飞机突然举起手说:"等一下,我还有一个问题。"
我说:"你说。"
他问:"如果我没通过,你能告诉我该补哪些书吗?"
我愣了一下。这个问题,面试两年多,头一次有候选人这么问。
我说:"你先把Java并发编程的实战那本书从头到尾看一遍,再看一遍JVM虚拟机规范对内存模型的部分,然后去把我今天问你的那几个为什么全部搞明白。等你把这些问题都能不看资料讲给别人听,再投下一家。"
他掏出手机,当着我的面把这几句话记了下来。
我微微点了点头,这个候选人有戏。
6.3 面试官最后的话:"你今天没白来,回去补补课,下次再来"
我说:"你今天没白来。至少你知道自己的短板在哪了,这比很多干了两三年还不知道自己不会什么的人强得多。"
他站起来鞠了个躬,走了两步,又折回来说:"那个……我想再确认一下。HashMap链表到8转红黑树,数组长度要大于等于64,对吧?"
我说:"对。你记住的是这个,但你要理解的是为什么。回去把那道题想明白,下次面试你就不会水了。"
他笑着点了点头说:"明白了。今天虽然没拿到offer,但比写十年代码都值。"
提示:面试时主动问面试官"我觉得自己哪里不足",绝对不是一个减分项。大多数面试官都愿意分享真实看法,只要你问的是"我该怎么提升",而不是"我为什么没过"。
7. 半年后的回访:那个水货程序员,现在怎么样了
写这篇文章前,我特意去问了一下团队的后续安排。谢飞机最终走的是专题面试补充通道,补齐了基础理论后,在第二个月的技术面中顺利通过了。目前他在团队里负责一个内部监控系统的开发,虽然不是核心业务线,但维护得很稳。
7.1 他后来的转变:从"散装知识"到"结构化思维"
最近一次周会上,我听到他给同事讲解一个并发问题的排查思路,心里还挺感慨的。他现在讲问题的方式,不再是"我试了A不行然后试了B",而是先讲现象、再讲定位思路、再讲根因分析、最后讲防复发措施。这种结构化的表达方式,明显是这半年刻意训练的结果。
我在会后问他:"现在回忆那场面试,最大的感受是什么?"
他说:"那场面试最大的价值,不是让我拿到了offer,而是让我知道自己哪个桶板最短。面试官问的那些问题,像一面镜子,把我平时工作上用不到的底层细节照得一清二楚。"
7.2 对"水货程序员"的最终认知:水不可怕,怕的是不知道自己水
回到文章标题。谢飞机这个"水货程序员",其实代表了很大一类Java开发者:平时干活能应付,但知识没有体系,理论基础不牢固,遇到深一点的问题就露馅。他们的共同特点是"能做事,但解释不了为什么"。
我自己在面试这两年里的一个核心体会是:面试官不怕你水,就怕你不知道自己水。谢飞机至少知道自己的问题在哪,并且愿意在面试现场承认、去记录面试官的建议。这种态度,比提前背一百道面试题都有用。
所以如果你觉得自己也是一个"水货程序员",我的建议是:
- 一,别慌。水是起点,不是终点。所有被我们称为大佬的人,都有过一段看见HashMap扩容都要怀疑人生的岁月。
- 二,结构化补课。不要零散地看到什么学什么,给自己列一张知识地图:Java基础、并发、JVM、MySQL、Redis、Spring、网络、算法,每一项标记出自己的水平,从最短板开始补。
- 三,面试是免费的体检。每一次面试都是别人花一小时帮你做一次技术能力体检的机会。即使没通过,也要把面试官问到你哑口无言的问题全部记下来,回去一个一个查明白。
- 四,把经验变成方法论。不要满足于"我会修",要追问"为什么坏""怎么不复发""能不能提前预防"。从"会修"到"会总结",中间隔着的就是资深工程师和初级工程师的距离。
8. 面试官现身说法:大厂Java面试中,关于"水货候选人"的几点建议
最后这部分,算是我这个面试官的一点私货分享。
8.1 面试官最怕看到的简历,和最怕遇到的人
我在筛简历时最怕看到三种情况:
- 包装过度:把三天学会的框架写成精通,把参与过的项目写成主导,面试时一问细节就露馅。
- 简历雷同:同样的电商项目,同样的秒杀系统,同样的Redis缓存,同样的"解决了缓存穿透问题"。十份简历八个这种,看一眼就知道是培训班出来的模板。
- 没有聚焦点:什么都写,Java、Python、Go、前端、后端、算法、运维,看起来全会,实际一个都扛不住深挖。
谢飞机虽然基础弱,但他至少有两个聚焦点:一个是他维护了八个月的开源工具,一个是他亲手解决的线上故障。这两件事让他在一堆模板简历里显得有辨识度。
8.2 面试官出题的逻辑:不是考察你那道题会不会,而是看你遇到问题时的思考方式
很多候选人以为面试是"考察知识储备",其实更准确地说,面试是"观察解决问题的思路"。HashMap这道题,如果你能说出"数组加链表",然后是"为什么用链表""为什么负载因子是0.75""扩容时为什么有的元素留在原位有的挪到两倍位置",层层递进下来,我会认为你不仅知道HashMap,还具备把复杂机制拆解清楚的能力。
反过来,如果你背了一堆"红黑树在什么情况下会退化""CAS的ABA问题怎么解决",但真让你现场写一个线程安全计数器时却写错了,那说明你的知识是悬浮的,没有落到代码层面。
谢飞机让我最满意的,恰恰是他能现场写代码,即使在写错之后,也能准确分析出自己的错误根源。这一点比背一百道题都有说服力。
8.3 给准备大厂Java面试的人的最后一句话
如果你的目标是拿到一个互联网大厂的Java开发offer,我的建议是:不要抱着"背题库"的心态去准备,而是先把那些最基础的知识点,按照"是什么—为什么—怎么用—有什么坑"的框架,一个一个弄透彻。那些回答问题的"套路"和"话术",在真正的追问面前,撑不过两轮。
反过来,如果你觉得自己基础差、经验浅、知识散,也不要灰心。我和谢飞机那场面试里看到的,不是"水货"的失败,而是"真实"的价值。在这个圈子里,只要你愿意承认不足、愿意持续补课、愿意把每一次失败变成自己的知识结构里的一块砖,迟早能盖出自己的高楼。