每年三四月份都是Java岗求职的硬仗今年小满的春招批次排得挺密我能进第三批笔试也算运气。说实话Java笔试这块儿很多同学对着一堆“八股文”背得滚瓜烂熟可真上了考场发现出题人根本不按套路出牌考的尽是些平时写代码根本不会注意的边角料。这篇文章就把我这次小满JAVA研发岗第三批笔试的完整复盘写出来不藏着掖着从考点分布、典型题目、答题策略到踩坑经验能写多细写多细。无论你是准备秋招的应届生还是想跳槽的Java开发只要能静下心看完起码能少走半个月弯路。1. 笔试整体拆解与考点分布1.1 小满这次笔试到底想筛什么人先说结论小满第三批笔试的难度整体属于中偏上比一般中小厂要扎实但比头部大厂的硬核算法题温和不少。它不追求让你做不出来而是追求让你“会但不完全会”专门挑基础功不扎实的软肋打。整张卷子分四块选择题约30道、填空题约10道、简答题4道和两道编程题。时间给了120分钟说实话并不宽裕尤其是编程题如果前面选择填空犹豫太久后面基本只能赶工。这点大家一定要有心理准备后面我会专门讲时间分配。从考点分布来看Java基础占了差不多35%集合框架15%并发编程15%JVM和内存10%Spring/MySQL合计15%剩下就是算法和杂项。注意一个明显的趋势直接背概念就能拿分的题变少了结合实际场景判断输出的题变多了。比如给你一段代码问输出什么、会不会报错、为什么这种题最见功底。我考完跟几个一起笔试的同学对了下答案普遍反映选择题里“代码阅读题”最难像异常处理流程、泛型擦除、字符串常量池这些光靠背讲义根本扛不住必须真的写过、踩过坑才有感觉。1.2 为什么出题风格在悄悄变化很多同学喜欢刷“八股文题库”觉得背完就能横着走。但从小满这次笔试能明显看出来出题人正在刻意规避那种“定义型题目”。比如很少直接问“什么是多态”而是给你三个类让你判断某个方法调用走的是哪个版本。这种变化的背后逻辑其实很简单公司招人是要干活儿的不是招背书机器的。平时开发中遇到问题能不能快速定位、能不能理解框架源码里的设计思路这些能力在“背定义”的模式下根本测不出来。所以现在的笔试题越来越偏向考“代码行为”就是给你一段可运行或不可运行的代码让你推理它的行为。比如有一道选择题给了个try-catch-finally的代码块finally里有个return问最后返回什么。这种题在IDE里一跑就知道答案但如果你没在脑子里“跑”过代码光靠记忆很容易选错。出题人就是要用这种方式区分“真正写过代码的人”和“只刷过题的人”。1.3 复习优先级怎么排根据这次笔试的实际体验我建议后面备考的同学按这个优先级来分配精力第一梯队必拿分Java基础语法、面向对象、异常处理、常用集合类、字符串相关。这些大概占50%的分数难度不大但要熟练到条件反射的程度。第二梯队拉分区并发编程synchronized、volatile、线程池参数、JVM内存模型和GC、MySQL索引和事务、Spring Bean生命周期。这些是区分度最高的部分也是简答题的重点来源。第三梯队锦上添花算法与数据结构、设计模式、Linux命令、Redis基础。编程题和部分选择题会涉及但不会太偏。有一点要提醒不要因为某块看着会就跳过。我这次就吃了亏觉得枚举类型简单没细看结果选择题里考了枚举的values()方法和ordinal()的关系选项挖得特别细直接给我整不会了。2. 核心考点逐个拆解2.1 Java基础与面向对象别以为你都会这回笔试的Java基础部分覆盖面很广但都从最基础的点上做了延展专门考你理解的“深度”。比如运算符优先级这个大学里学完就忘的东西竟然出现在选择题里问你int a 1; int b a a;最终b等于多少。这类题看着简单但里面既有自增运算符的执行时机又有优先级和副作用的问题不仔细真的会算错。再比如Lambda表达式那几年Java 8的Lambda刚出来时大家都觉着新鲜现在Lambda已经成了开发标配。笔试里考的是一道List排序的题要你用Lambda实现按某个字段倒序排列。这题看着不难但要注意Comparator.comparing的用法。如果平时没写过comparing(Foo::getBar).reversed()这种链式写法很容易在.reversed()的位置上翻车因为reversed()是作用在Comparator对象上的不是作用在方法引用上的。枚举类型也是这次的高频考点。选择题里给了一段枚举的代码里面定义了构造器、字段和一个抽象方法然后问你枚举的实例创建顺序是什么、switch匹配枚举时底层是什么类型。说实话很多人把枚举当成“加了糖的常量类”忽略了一个关键点枚举是final的类每个枚举常量都是该类的一个实例而且是静态final的。所以枚举的构造器不是你想什么时候调就什么时候调的它在类加载时就固定了。还有标识符命名规则也来凑热闹给了一堆字符串让你选哪些能当Java标识符。这种题一般不会故意坑人但要注意关键字不能作为标识符、$和_可以出现在标识符中但有些位置不受推荐。我印象最深的是有个选项是_在Java 9之前_是合法的标识符但Java 9开始单独的下划线被禁止了。这种题纯粹考知识更新如果你没跟进版本特性大概率会选错。2.2 集合框架HashMap和ArrayList是永远的神集合这块小满笔试考得很“主流”但问题问得很刁。先说ArrayList。问的是ArrayList初始容量是多少、什么时候扩容、扩容到原来的多少倍。如果你答上来“初始容量10、添加第11个元素时扩容、扩容为原来的1.5倍”恭喜你只答对了一半。因为Java 8的ArrayList懒加载策略实际上你new ArrayList()的时候底层数组是空的DEFAULTCAPACITY_EMPTY_ELEMENTDATA只有第一次add时才初始化成容量10。这道题就把很多背答案的同学筛掉了。HashMap更是重头戏。选择题里出现了很经典的“JDK 7和JDK 8中HashMap的区别”问链表转红黑树的阈值是多少。答案是链表长度到8转红黑树、红黑树节点数降到6退化为链表。这里有个细节很多人不理解为什么是8和6而不是7和7其实是为了避免在阈值附近反复振荡转换浪费性能。别小看这个位运算级的细节面试官是真会追着问的。另外还考了ConcurrentHashMap问的是JDK 8里它还分段锁吗。答案是已经不再用Segment分段锁了而是改用CAS synchronized锁住桶的头节点。如果只背过“分段锁”三个字看到这题就懵了。集合框架的学习建议不要只背结论要把源码下载下来读一遍关键方法。比如HashMap.putVal()的核心流程、ArrayList.grow()的扩容逻辑读一遍源码比背十篇博客都管用。笔试里那些“题目代码问你输出”的题往往就是从源码行为反推出来的。2.3 并发编程考的是并发三大特性的落地并发这部分小满笔试里的风格很“实操”——给一段多线程代码让你判断哪些地方有线程安全问题。具体来说有一道题是这样的一个类的成员变量是int count两个线程各自循环10000次count问你最终值是多少。答案是“不确定可能小于20000”。然后它问你如果想保证结果正确可以用哪几种方式。选项里有volatile、synchronized、AtomicInteger、LongAdder等。这里就藏着个经典误区volatile能不能保证count的原子性不能。volatile保证的是可见性和有序性但count是“读-改-写”三步操作不是原子的。所以选volatile就错了。正确选法是synchronized或AtomicInteger。这道题我印象很深因为出题人把volatile藏在选项里就是等着那些只背了“volatile保证可见性”就去答题的人往坑里跳。线程池也考了而且考得深。问ThreadPoolExecutor构造方法中各个参数的含义以及任务提交后是怎么排队的。核心线程满了进阻塞队列队列满了开非核心线程线程数到最大值还有任务来就触发拒绝策略——这个流程你得烂熟于心。还额外问了一个有意思的点Executors.newCachedThreadPool()的阻塞队列是什么类型答案是SynchronousQueue因为它是直接交接的不缓存任务这才会导致“来一个任务就开一个线程”的假象。我自己的经验是并发这块不要光背“三特性”“线程状态”而是要自己写一个小Demo用jstack看线程状态用jvisualvm监控锁竞争。当你亲眼看到死锁、看到线程阻塞这些知识点才会真正和你融为一体。2.4 JVM与内存出现了OutOfMemoryError你该怎么查今年好多热词里都出现了java: outofmemoryerror: insufficient memory说实话这行报错太有代表性了凡是搞Java的几乎都见过。小满笔试JVM部分的题有一半就是围绕这类问题出的大题。我记得简答题里有一道是线上服务出现OutOfMemoryError你会怎么排查。这题看似是开放题实际上考察的是完整的排查思路。你得回答出先用jstat看堆内存和GC情况再用jmap -dump导出堆转储文件接着用MAT或VisualVM分析大对象和引用链最后定位到具体代码行。如果堆内存正常但一直OOM还要考虑是不是线程创建过多、是不是堆外内存Direct Memory溢出了。这道题能拿全分的人得有真实的线上排障经验不然就只能写出一句“加大堆内存”的废话。另一道选择题也很有意思问你创建对象时内存分配优先在Eden区还是老年代什么情况下对象会直接进入老年代。这里有个隐藏考点大对象比如一个很大的数组会直接分配到老年代避免在新生代里反复复制。还有长期存活的对象年龄达到15岁CMS默认是6岁会晋升到老年代。但如果你开了-XX:UseTLAB对象会优先在TLAB上分配这个细节也是要命的。JVM这部分的备考我强烈建议大家亲手把JDK自带的jcmd、jstat、jmap这些工具在本地跑一遍。没有实操经验的话你背再多“新生代、老年代”的概念碰到稍微灵活的题目还是会露馅。2.5 Spring与MySQL框架原理也不能只停在“会用”小满笔试对Spring的考察侧重在“原理”二字。有一道选择题问Spring Bean默认的作用域是什么这题很简单选singleton。但下一道题就难度陡增多个单例Bean之间互相依赖会不会产生循环依赖问题Spring能不能解决构造器注入的循环依赖答案是Spring能解决setter注入和字段注入的循环依赖通过三级缓存但解决不了构造器注入的循环依赖因为构造器在实例化阶段就必须完成依赖注入这时三级缓存还没法提供半成品对象。这个考点在真实面试里也是高频题笔试里遇到说明出题人确实懂行。MySQL部分索引和事务是绝对的主角。题目问你InnoDB的索引结构是什么答案是B树而不是B树然后问为什么用B树。这里要注意答全B树所有数据都存在叶子节点非叶子节点只存索引值因此单页能容纳更多索引项树更矮IO次数更少叶子节点之间有指针相连方便范围查询。光答“非叶子节点不存数据”还不够得把范围查询的优势也说出来。事务隔离级别也考了四个级别从低到高是读未提交、读已提交、可重复读、串行化然后重点问InnoDB默认的隔离级别是什么答案是可重复读这个大家基本都知道。但深入一问可重复读怎么解决幻读的答案是通过MVCC Next-Key Lock间隙锁记录锁的组合。如果只背了“MVCC能解决幻读”这种简化的结论一旦被追问“MVCC只能解决快照读的幻读当前读的幻读要靠Next-Key Lock”就很容易卡壳。2.6 编程题快速排序、冒泡排序还有一道链表题这次笔试的两道编程题第一道是“排序题”给定一个数组要求用快速排序实现升序排列。这题看起来很基础但面试官在笔试环境里考快排是有用意的。很多人排序只会调用Arrays.sort()真让你手写快排就抓瞎。我在这里把快排的关键点说一下public void quickSort(int[] arr, int left, int right) { if (left right) { return; } int pivot partition(arr, left, right); quickSort(arr, left, pivot - 1); quickSort(arr, pivot 1, right); } private int partition(int[] arr, int left, int right) { int pivotValue arr[right]; int i left - 1; for (int j left; j right; j) { if (arr[j] pivotValue) { i; swap(arr, i, j); } } swap(arr, i 1, right); return i 1; }这里用的挖坑法或者双指针法都行关键是要能写出稳定的partition逻辑并说清楚时间复杂度最坏情况是什么、怎么避免。快排最坏情况下是O(n^2)比如数组本身有序而你每次都取最后一个元素当pivot这时候就得考虑随机化pivot或者三数取中法。第二道编程题是单链表反转要求用递归和迭代两种方式实现。这两种方法都要会而且笔试环境没有IDE提示得靠手感和肌肉记忆写出来。迭代法用一个prev指针一路往后指递归法思路是“先反转后面所有节点再让当前节点的下一个节点指向自己”。如果对链表不熟很容易在递归的终止条件上写错出现空指针或者环。关于冒泡排序虽然这次编程题没直接考但选择题里出现了“冒泡排序最优时间复杂度是多少”这种题。答案是O(n)前提是在内层循环里加了“本轮是否发生交换”的标志位如果没有交换说明已经有序直接跳出。注意如果没加任何优化冒泡排序的最好时间复杂度也是O(n^2)。这个细节也提醒我们基础排序算法不要背一个版本就完事要理解每种优化的动机。3. 实操过程与答题策略3.1 考前准备把这些环境坑提前踩平笔试是线上进行的所以环境问题很要命。我考前在本地把JAVA_HOME、PATH都重新配了一遍顺手确认了java -version和javac -version的输出因为如果你本机装了多个JDK版本编译版本不一致很容易出现“源发行版17需要目标发行版17”这种报错。这里展开说一下环境变量配置Windows下就是新建JAVA_HOME指到JDK安装目录然后在PATH里加上%JAVA_HOME%\bin。但要注意如果你在PATH里同时配了多个JDK的路径系统会按顺序找到第一个java.exe所以可能你敲java -version是17而IDE里配置的运行环境却是8这种不一致会引发一堆莫名其妙的编译错误。还有一件特别容易坑人的事Lombok报错。很多同学本机装了Lombok插件但线上笔试环境是网页编辑器没有插件处理注解结果代码里用了Data、Slf4j这类注解一编译就报类似“you arent using a compiler supported by lombok, so lombok will not work”的错。遇到这种情况别慌笔试环境不支持Lombok是常态尽量不要在提交的代码里依赖Lombok生成的方法手写getter/setter或者换个实现方式稳一点。另外线上编译器默认编码不一定是UTF-8如果题目的示例代码里带了中文注释你本地跑没问题一提交到平台就可能乱码。这种情况可以在代码文件头加上// -*- coding: utf-8 -*-或者确保编辑器右下角编码格式是UTF-8。VSCode用户还可以在设置里搜files.encoding把它固定成UTF-8能少很多乱码烦恼。3.2 时间分配与答题顺序120分钟我的实际分配是这样的选择题30分钟填空题15分钟简答题35分钟编程题30分钟剩下10分钟检查。听起来挺紧凑实际操作中选择题里那些代码阅读题特别耗时间一道题可能要3到4分钟。我的建议是先做简答题和编程题原因很简单这些题目分值大、按点给分就算你只能写出思路也能拿部分分。如果你先花大把时间做选择题做到最后编程题只剩15分钟那就很被动了。选择题哪怕蒙一下还有25%的正确率兜底简答题和编程题蒙不了靠的是实打实的输出。编程题写完后一定要留时间做“边界测试”。比如给空数组、单元素数组、已经有序的数组分别跑一遍。很多线上笔试平台不会告诉你哪个测试用例没过所以你必须自己把常见边界情况都列出来逐项验证。3.3 代码答题时的输出规范笔试机考的评判标准通常是自动跑测试用例对输出格式极其敏感。我这次第一道排序题要求输入一行整数输出升序排列的整数用空格分隔。如果你最后多打了一个空格或者用了换行结尾某些平台会判“格式错误”。我的习惯是不要在System.out.println()里做复杂的字符串拼接而是用StringJoiner或者StringBuilder组装好再一次性输出。这样既避免了多次IO的性能浪费也容易控制最后一个元素后面没有多余空格。比如StringBuilder sb new StringBuilder(); for (int i 0; i arr.length; i) { if (i 0) { sb.append( ); } sb.append(arr[i]); } System.out.println(sb.toString());还有一个细节如果题目让你输出的是浮点数注意保留几位小数。不同平台的精度要求不一样我一般是先在注释里声明// 输出保留两位小数然后统一用String.format(%.2f, value)避免因精度问题被扣分。3.4 简答题怎么写才能拿高分简答题是很多人容易忽视的失分项。小满笔试的简答题不是让你写长篇大论而是按要点给分所以你写的时候一定要分条、清晰。比如“说说HashMap的put流程”你最好这样组织计算key的hash值通过(n - 1) hash定位到桶的位置如果桶为空直接new Node放入如果桶不为空比较链表头节点的hash和key是否相等相等则覆盖如果不相等遍历链表找到末节点追加如果链表长度超过8则转红黑树插入完成后检查size是否超过threshold超过则扩容。这种答题方式阅卷人一眼就能看到得分点比写成一段话更容易满分。还有一个技巧是遇到“会不会、能不能”这类判断题第一句先给出结论。比如“Spring能不能解决构造器循环依赖”先答“不能”再展开解释。很多阅卷人看的就是那个关键字你放在句首就不用担心他看不见。4. 笔试题外的话面试与后续准备4.1 笔试后面试官可能会追问什么笔试结束不代表战斗结束小满的流程通常是笔试通过后紧接着安排技术面。面试官手里是有你笔试答卷的所以他大概率会拿着你做错的题或者写得含糊的答案追问。这里有个很实用的准备思路笔试结束后立刻把每道题的答案重新过一遍尤其是那些“蒙对了”的题。因为面试官如果要问一定是从这些题目延伸出去问“为什么”。比如选择题里你选了ConcurrentHashMap在JDK 8用的是CAS synchronized面试官就可能追问synchronized锁的是对象还是类、锁升级的过程是什么。这些内容如果笔试后没有趁热打铁梳理一遍到面试时就很难答得流畅。4.2 常见报错与问题速查表我把自己这段时间遇到的、以及跟同学交流收集到的典型问题整理成了表笔试踩坑的人可以参考现象可能原因解决办法源发行版 17 需要目标发行版 17项目JDK版本与IDE编译级别不一致检查Project Structure和Maven的java.version统一JDK版本OutOfMemoryError: Insufficient memory堆内存不足或创建的线程过多用jmap导出堆快照分析同时检查系统可用内存Lombok相关编译报错缺少注解处理器或编译器不兼容确保IDE安装Lombok插件且开启Annotation Processing控制台中文乱码编码不统一统一文件编码为UTF-8设置控制台编码-Dfile.encodingUTF-8ArrayIndexOutOfBoundsException访问了不存在的数组下标检查循环边界Java数组下标从0开始最大为length-1NullPointerException对null对象调用了方法使用Optional或提前判空查看异常堆栈定位行号代码在本地能跑平台报错平台JDK版本或依赖不同尽量只用JDK原生API避免使用第三方库4.3 心态和后续学习路线最后说点掏心窝子的话。笔试这东西准备得再充分也可能遇到知识盲区。我这次有一道关于Comparator.comparing链式调用的选择题就是凭感觉蒙的好在整体没有崩盘。如果你某道题真不会用排除法把明显错的选项划掉把答案锁定在两个选项之间后凭直觉选一个然后把更多时间留给后面能拿分的大题。笔试踩坑之后我给自己规划了三个阶段的学习路线。第一阶段重新精读集合、并发、JVM三块源码动手跑一遍JDK自带工具第二阶段把Spring Boot自动配置、Bean生命周期、MySQL索引下推这些原理吃透写技术笔记第三阶段保持每天手写一道算法题的节奏重点练链表、二叉树、动态规划。这条路线也同步分享给正在备考的朋友希望你能少踩我踩过的坑一次上岸。