
每年秋招的Java笔试风格差异大到像两个物种。有的公司喜欢海量八股选择有的上来就手撕hard题还有些玩情景题。贝壳找房2024年秋招Java工程师第一批笔试我踩完坑之后觉得非常值得复盘整体属于“基础面广、算法够用、Java考得深”的路子今天就把考了什么、怎么准备、踩了哪些坑一次说清楚。这篇内容适合两类人马上要参加后续批次笔试的应届生以及准备跳槽、想摸底大厂笔试难度的Java开发。我会把笔试中涉及的Java核心考点、算法思路、计算机基础题都拆开讲不只是报答案重点说清楚“为什么这么答”和“我当时哪里想岔了”。1. 笔试整体情况与题型分布贝壳这批笔试是线上双机位总时长90分钟题量不算小。题型分成三块单选多选混排的选择题、2道手撕代码题、若干简答/改错题。选择题侧重Java基础、并发编程、JVM、Spring和MySQL代码题走的是经典算法路子没有特别偏门的ACM题型。先说整体感受贝壳的笔试不像某些互联网大厂那样疯狂堆算法题量它更在意基础扎实度。选择题涉及的知识面很广我在刷题时复习过的Java内存模型、HashMap原理、线程池参数、Spring Bean生命周期几乎都考到了。而代码题难度中等偏上2道题从读题到写完自测我大概剩了20分钟做选择题中的疑难题和检查时间分配上需要提前心里有数。这里放一下我记忆中的题型分布参考模块题量分值占比主要考查方向选择题单选多选约20题40%Java基础、并发、JVM、Spring、MySQL、Redis简答/改错题2题20%代码阅读、设计思路、异常排查算法编程题2题40%数据结构、动态规划、字符串处理、排序提示线上笔试的三方监控一般都开着切屏会警告超过一定次数直接算作弊。我全程老老实实只开着一个本地代码编辑器窗口也建议你用本地IDE练习提前适应这种“本地写题网页提交”的节奏。时间分配上我建议拿到卷子先花一分钟扫一遍所有题目心里对代码题难度有个预判优先做自己有把握的。我的策略是先花10到15分钟快速过掉选择题里不涉及计算和代码推导的送分题然后集中40分钟做算法题剩下的时间回头啃选择题中的多选和疑难项。实际上多选的坑比单选多得多多选漏选、错选都不得分确实需要留出时间。2. Java核心考点拆解贝壳笔试偏爱哪些八股这次笔试的Java部分明显能看出出题人对“并发”和“容器”两个板块有执念。我回忆了一下选择题里涉及HashMap的至少出了三题线程池出了两题JVM内存模型和垃圾回收各一题。难怪每年Java面试八股文永远绕不开HashMap不是没道理的。2.1 HashMap原理不是只背“数组加链表”就完事选择题里面有一道是这样的JDK 8的HashMap在什么条件下链表转红黑树答案是链表长度大于等于8且数组长度大于等于64。很多人只记得8忽略了数组长度64的前提我还在评论区见过有人争这个细节其实源码注释写得很清楚TREEIFY_THRESHOLD只是触发树化的第一个条件如果数组长度不到64是会先resize而不是直接转树。还有一道问put操作的完整流程计算hash-定位桶下标-判断是否空桶-判断是否树节点-遍历链表查找key-不存在则尾插-判断是否达到树化阈值-判断是否扩容。这个流程背下来不难难的是理解为什么用高低位异或扰动函数。实际上源码里(h key.hashCode()) ^ (h 16)这一步是把哈希值的高16位和低16位做异或目的是让高位信息也能参与低位运算降低哈希碰撞概率。当数组长度比较小时能参与取模运算的只有低几位如果没有扰动碰撞概率会明显上升。扩容相关的那道选择题也很典型默认加载因子0.75为什么不是0.5或者10.5浪费内存空间1则碰撞概率太高0.75是空间和时间的一个折中这是从统计学上泊松分布推导出来的。这里提醒一下准备后续笔试的朋友不只要记住默认值还要能说出“为什么是这个值”贝壳笔试的很多选择题就是在这个“为什么”上挖坑。2.2 线程池状态机、核心参数与拒绝策略线程池的考题更偏向实际运用。有一道选择题给了四个参数配置问你哪种配置可能导致任务排队时间过长甚至OOM。核心点就是如果核心线程数太小队列又用得是无界队列比如LinkedBlockingQueue默认构造那么当任务量陡增时线程数永远只停留在corePoolSize因为核心线程没满就不会创建新线程任务全堆积在队列里内存就容易被撑爆。这里我还想多写一点我把newFixedThreadPool为什么不推荐用也和这道题联系到一起了因为这个线程池用的就是无界队列即使设置了很大的maximumPoolSize也形同虚设不会触发拒绝策略容易导致无限排队。关于线程池的执行顺序我喜欢用一个生活化类比来记线程池就像一个饭店核心线程数是正式员工队列是等待区最大线程数是临时工上限拒绝策略是实在接待不了时的处理方式。来一个客人先看正式员工有没有空有就接待没有就引导到等待区排队等待区满了才考虑招临时工这里有个细节招临时工的顺序是在等待区满了之后才会进行的如果临时工也满了才启用拒绝策略。笔试里有一道判断题就考了这个顺序新任务到达时先判断线程数是否小于核心线程数不是先丢队列顺序反了就直接答错。拒绝策略这块AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy它们各自的适用场景也要清楚。我记得有一道多选题就是给场景选策略如果任务绝对不能丢应该选什么答案是CallerRunsPolicy因为它在拒绝时会直接在调用者线程里执行任务相当于把压力回传给调用方而不是静默丢弃。而AbortPolicy是默认策略直接抛异常DiscardPolicy和DiscardOldestPolicy都是丢任务的前者丢新的后者丢最老的要看场景选。2.3 JVM内存区域与垃圾回收画图记忆比死记硬背强JVM相关考题是典型的“画图就能得分”的题目。选择题问的是哪个区域不会发生OutOfMemoryError答案是程序计数器。理由也很简单程序计数器是线程私有的它的空间需求在类加载时就已经确定了用来存下一条字节码指令的地址大小恒定不会随着运行时数据增长而变化。我在复习时把运行时数据区画了张图线程共享的堆和方法区线程私有的虚拟机栈、本地方法栈、程序计数器每次看图比死记硬背快得多。垃圾回收的题问的是CMS的回收阶段。CMS全称Concurrent Mark Sweep它的回收过程分为初始标记、并发标记、重新标记、并发清理四个阶段。其中初始标记和重新标记需要STW并发标记和并发清理跟用户线程并发执行。题目问哪个阶段会STW很多人的误区是以为CMS全程并发实际上初始标记和重新标记都会暂停用户线程。CMS在Java 9之后被标记为废弃很多企业已经切换到G1了但笔试考老知识点仍然频繁。注意看题时要看清楚问的是“哪个阶段会STW”还是“哪个阶段不会STW”。我在做这道题时第一眼差点选错这些词汇陷阱在笔试中经常出现建议每道选择题把题干关键词圈出来再选。3. 手撕代码题2道经典题的思路与Java实现贝壳的算法题没有特别劝退属于“认真准备过笔试就能写出正解”的难度。第一道考的是分组排序的变体第二道是动态规划。两道题我都在本地IDE里写完并自测了几个用例才提交功夫在平时建议你平时练习时也养成写完手写几个测试用例的习惯。3.1 字符串分组排序变体题目大意是给定一个字符串数组将字母异位词由相同字符重排形成的词分到同一组每组内部按字典序排序最终按每组第一个元素排序输出。这道题考察两个点一是如何高效判断两个字符串是异位词二是如何保持分组内部和分组的整体顺序。判断异位词最常用两种方案一是将字符串转成字符数组后排序排序结果相同的就归为一组时间复杂度O(nlogn)胜在实现简单二是用长度为26的int数组记录每个字母出现次数将数组转成字符串作为key时间复杂度O(n)更优但代码略长。实际写题时我采用了第二种因为笔试环境里的输入规模没有明说稳妥起见选择线性复杂度方案。实现思路遍历字符串数组对每个字符串统计字符频率把统计结果转成key存入HashMapvalue是对应的字符串列表。关键点在于如何把int数组转成稳定的key我用的方案是拼接成“1#0#3#...”这种带分隔符的字符串防止出现歧义比如[1,23]和[12,3]这种边界情况。分组完成后对每个组内列表排序再对所有组的首个元素排序确定组的输出顺序。这题相对基础不过它考察了字符串处理、哈希表、排序的综合运用要求对类库API熟悉。平时不常写字符串处理题的话可能在中途卡在“如何优雅地把频率数组转成key”上。3.2 编辑距离动态规划的状态定义与转移方程第二道题是编辑距离原题描述是给定两个单词word1和word2计算将word1转换成word2所需的最少操作数可以插入一个字符、删除一个字符、替换一个字符。这是动态规划里的入门经典题也好多年没在笔试里遇见了看到题目时还愣了一下。状态定义比较直观dp[i][j]表示word1的前i个字符转换成word2的前j个字符需要的最少操作数。初始化也比较简单dp[0][j]j不断的插入dp[i][0]i不断的删除。转移方程按最后一个字符是否相等分两种情况word1[i-1] word2[j-1]当前字符不用操作直接dp[i][j] dp[i-1][j-1]不相等时取三种操作的最小值加1插入dp[i][j-1]在word1第i个字符后插入一个与word2[j-1]相等的字符删除dp[i-1][j]删除word1第i个字符替换dp[i-1][j-1]将word1第i个字符替换成word2[j-1]。两层循环填完整个二维数组后dp[m][n]就是答案。注意这里两个字符串可能都为空边界条件要处理干净。我写了大约30分钟的Java代码含注释你自己写的时候建议先从状态定义开始理清思路再动手。3.3 笔试中的算法取舍与测试意识代码题不会只考“写对”还会考察“在有限时间和内存下的取舍”。第一题我用HashMap统计频率空间复杂度O(n)每个key最长不超过26个字符可以接受。第二题用二维数组存状态如果两个字符串长度都到1000二维数组就是100万级别没问题但如果长度到一万这题就应该优化成滚动数组了。幸好笔试时发现长度范围控制在500以内二维数组直接过。我当时写完每道题都手动构造了三个测试用例正常情况、边界情况、特殊输入。比如编辑距离那道我测试了word1为空、word2为空、两字符串完全相同这三种边界。笔试时因为测试用例能加分花两分钟做自测完全值得总比提交后开始担心边界没过要强。4. 计算机基础考察数据库、Redis与网络知识选择题中数据库和Redis的题量也不少这点和贝壳的业务场景挺匹配房产信息、用户关系、搜索推荐这些业务都重度依赖存储层。笔试把MySQL和Redis放在一起考看得出对后端基本功的要求不是停留在“会用CRUD”的层面。4.1 MySQL索引失效场景与事务隔离级别数据库部分有一道选择题问哪些情况会索引失效选项包括隐式类型转换、对索引列使用函数、like语句前缀通配符、or连接非索引列。这些都是高频考点我在面试中也常碰到。具体说隐式类型转换会让MySQL忍不住调用CAST函数导致索引列上套了函数索引就失效了where name like %张这种前缀模糊查询无法走索引因为B树的索引顺序是从左往右匹配的前缀不确定就定位不了where a 1 or b 2如果b没有索引优化器可能会选择全表扫描因为它要同时满足两边的条件没法走一个索引把所有结果都捞出来。事务隔离级别也考了一道填空题加选择题组合MySQL默认的隔离级别是Repeatable Read可重复读这一点和标准SQL默认的Read Committed不同。可重复读能解决脏读和不可重复读但没法完全解决幻读InnoDB通过间隙锁Next-Key Lock在部分场景下解决了幻读但不代表所有场景都彻底避免了。我回忆笔试中的那道选择题它问的是“脏读、不可重复读、幻读分别在哪个隔离级别下被解决”很多人背了隔离级别名称但没区分清楚不可重复读和幻读的差异不可重复读针对同一行数据多次读取结果不一致幻读针对的是新增或删除导致的结果集数量变化。4.2 缓存三大问题穿透、击穿、雪崩Redis部分的题比较常规考到了缓存穿透、缓存击穿、缓存雪崩的区别和应对方案。我猜是因为贝壳找房的房源地库查询对缓存依赖度很高线上对缓存稳定性的要求自然不低。我把三者的区别整理成了一张表后续去其他公司笔试也用得上问题场景应对方案缓存穿透查询不存在的数据每次都落到数据库布隆过滤器拦截、缓存空值短TTL缓存击穿热点key失效瞬间大量请求打到数据库互斥锁重建缓存、逻辑过期、热点key永不过期缓存雪崩大量key同时过期或Redis宕机过期时间加随机值、集群高可用、限流降级还有一道考察Redis数据结构的题问的是实现排行榜用什么结构。答案是ZSet有序集合因为ZSet底层是跳表加哈希表支持按score排序和范围查询时间复杂度O(logn)级别比在内存里自己维护一个有序列表高效得多。贝壳这种业务里房源价格排序、热门小区排行确实都是ZSet的典型应用场景。4.3 TCP与HTTP的送分题背后还有深层考点网络部分的题相对简单一些TCP三次握手为什么是三次而不是两次TCP和UDP的区别是什么HTTP和HTTPS的端口、安全性差异不过后面跟了一道稍微进阶的浏览器的同源策略和HTTP跨域问题。Java后端虽然不直接处理浏览器CORS但Spring的CORS配置、网关层的跨域处理都是实际开发中绕不开的笔试考察这个说明出题人比较看重工程实践。做这类题时别只背概念适当结合项目经验讲更好毕竟笔试题的题干里经常有“开发中遇到……怎么排查”这种场景化描述。5. Spring与工程实践贝壳笔试里的业务视角Spring相关的题目在选择题里大概有五道左右比重不小。涉及的内容包括Bean生命周期、自动装配流程、事务传播行为、Spring Boot自动配置原理。和我预想的不同这些题不像“八股背诵”那么死板更像是给一个具体场景让你判断容器或事务会怎么走。5.1 Spring Bean生命周期与循环依赖有一道题给了一段代码包含两个互相依赖的Bean然后问在默认单例模式下能不能正常启动。答案是能因为Spring三级缓存机制解决了单例Bean的循环依赖问题。三级缓存分别是一级缓存存放完整实例二级缓存存放早期暴露的未完全初始化的Bean三级缓存存放代理工厂。Bean在实例化后、属性填充前就会把ObjectFactory放入三级缓存这样A依赖B时B在创建过程中依赖A就能从三级缓存中拿到早期引用避免死循环。但这道题的扩展选项很有迷惑性如果其中一个Bean是Async代理循环依赖还能不能解决答案在某些情况下是不能。因为代理对象的创建时机会影响循环依赖的解决路径这类题如果没有真正看过三级缓存源码很容易做错。我复习时把三级缓存、Bean的实例化到初始化的整个过程都手画了一遍才对这类变体题有把握。5.2 Spring事务传播行为REQUIRED和REQUIRES_NEW的区别事务传播行为考了两道题。一道是经典的自调用问题一个类的内部方法通过this调另一个带Transactional的方法事务会不会生效答案是不会。因为事务是通过AOP代理实现的内部调用走的是this本身没有经过代理对象事务注解自然凉了。解决方法是注入自身代理或者把方法拆到另一个Bean中。另一道则是问REQUIRED和REQUIRES_NEW在嵌套调用下的行为。REQUIRED表示如果当前存在事务就加入不存在就新建REQUIRES_NEW则是无论当前有没有事务都挂起当前事务新建一个独立事务。选择题里的坑在于“方法A调用方法BA抛异常B在REQUIRES_NEW下已经提交最终数据结果如何”。答案是A回滚、B不回滚。如果没理解透传播行为看到A抛异常就容易脑补成“都回滚”。这类题其实在笔试中出现率很高做对了能拉开差距毕竟很多人对事务的实际控制边界不够敏感。5.3 让八股变成工程直觉我的复盘方法笔试结束我发现一个规律贝壳的Spring题基本不是单独考某个注解的用途而是给一个开发场景让你判断运行结果。这就意味着死记硬背不够得有“运行时视角”。我复盘时特意写了一套模拟场景——线上接口偶尔报错日志里看到事务回滚、缓存数据不一致然后逼自己从Bean生命周期、事务传播、Redis过期策略的角度去推断原因。这个习惯帮我理清了知识点之间的联系也帮我在后续面试中把“背过的八股”讲成了“踩过的坑”。6. 笔试备考策略一个月滚出高频考点的完整路线如果你也准备冲秋招后续批次或者其他公司的Java岗笔试我的核心建议是按“算法高频题、Java八股、计算机基础、框架原理”四条线并行刷不要先啃完一本大部头再开始做真题时间根本不够。我备考用的资料其实很固定算法刷题用LeetCode热门100题加剑指OfferJava看《Java并发编程的艺术》里的关键章节JVM看《深入理解Java虚拟机》前几章加在线总结Spring直接看源码解析类文章加自己打断点验证。每看完一块知识我会用一小时把相关知识点整理成自己的话逼自己输出避免“看书全会、做题全废”。时间安排上我建议如果只有四周可以这样分第一周集中攻算法把数组、链表、树、动态规划这几类基础题过一遍第二周主攻Java基础加并发每天做10道选择题加2道手写代码第三周看MySQL、Redis、操作系统和网络配合项目思考底层原理第四周整卷模考掐时间按真实笔试环境做2到3套模拟题练节奏和心态。我这次贝壳笔试的准备过程其实也基本走了这个节奏只是当时把更多时间给了算法导致Spring部分有几道题做起来有点犹豫复盘后才补上。笔试当天有一点比较重要提前调试好电脑、浏览器和摄像头。贝壳笔试用的是国内常见的在线考试平台有模拟测试入口我提前一晚完整跑通了流程。别因为网络波动或者摄像头权限没开影响进入考试的心态笔试已经开始扣的一分钟就是浪费。另一个细节是用本地IDE写代码时注意类名和主方法签名笔试平台要求提交的代码通常是纯Java类不是整个工程命名不规范可能导致编译失败。7. 复盘总结从贝壳笔试反推训练重点考完复盘时我把自己定位成一个“基础还有欠缺但方向对了”的候选人。贝壳这批笔试给我最大的启示是Java后端笔试真正拉开差距的不是算法难题而是对并发和JVM底层机制的熟练掌握程度。选择题里最让我拿不准的是那些“源码里默认参数为什么这么设计”的变体题面熟但没深究就容易错。一个比较深刻的感受是贝壳的笔试题目整体偏向“业务复杂度不高但工程素养要求不低”的风格。它的算法题不偏难怪但选择题的考察角度比很多公司更细尤其是HashMap、线程池、Redis这三种高频组件的细节。如果你平时只是会用这些API而不看底层原理做起来确实会比较吃力。我也发现自己还有几个明确短板要补Spring事务自调用问题的底层AOP原理我虽然知道答案但论述时不够严谨缓存雪崩和击穿的代码级解决方案我没有完整实现过只是背了答案。后续我会针对每个盲区写一篇带代码的Demo再整理成笔记把知识补扎实。秋招笔试只是第一关把每一次笔试都当成查漏补缺的机会后面的面试才有底气。最后分享一个对我来说很管用的方法每次笔试完不管你发挥如何立刻花半小时把错题和不确定的题记录下来哪怕只是草稿纸拍个照然后当天或第二天对照资料把每一个盲点弄清楚。这次贝壳笔试结束后我重新整理了HashMap扩容和线程池拒绝策略的笔记顺手把编辑距离的滚动数组优化也写了一遍。复习的黄金时间就是考后的48小时千万别等全部笔试结束再统一复盘那时候细节早就忘得差不多了。