1. 为什么一份四年前的试卷今天还值得翻出来看
秋招圈有个挺有意思的现象:每年都有新人把往年的考题当成"过期资料"处理,尤其是像2019年这种时间跨度不小的试卷,很多人觉得技术更新快,旧题没参考价值。但我在带团队、做技术面试的过程中反复验证过一个结论——基础题的命门从来没变过。
唯品会2019秋招开发A类试卷,恰好是这类"看着旧、内核不过时"的典型样本。那一年唯品会的技术面试还处在从"大而全"向"业务纵深"过渡的阶段,试卷里的考点分布既能反映电商业务对后端开发的真实要求,也保留了计算机基础学科最核心的考察逻辑。就算放到今天,你会发现在校招笔试里反复出现的依然是HashMap、线程池参数、SQL索引失效、TCP握手过程这几样东西,只是换了层皮。
这篇文章我打算做几件事:把这份试卷按题型和考点拆开,逐个分析命题意图和答题要点;再把笔试之后面试官可能的追问方向也梳理一遍;最后聊一聊备考的人应该怎么利用这类试卷做针对性训练。如果你正在准备大厂开发岗的秋招笔试,或者刚转行做后端开发想摸摸自己的底子,这份拆解能帮你省下不少盲目刷题的时间。
先说一句:试卷原题的完整内容我没有逐一保留,但A类试卷在2019年前后的大厂笔试中结构相当稳定,下面的分析基于同类岗位笔试的普遍规律和考点频次整理,你可以把它当作一份"高仿真题解析"来用。
2. 试卷的基本盘:题型分布与时间分配策略
2.1 A类试卷到底"类"在哪
很多同学第一次看到"开发A类试卷"这个命名会有点懵,这里解释一下。在唯品会那年的校招流程里,笔试试卷按照岗位方向分成A、B、C等类别,A类对应的是核心后端开发岗,B类一般是前端或者客户端,C类通常是测试或运维方向。A类试卷最显著的特征是:算法题占比高、考察范围广、对Java技术栈有明确倾斜。
为什么要有这种分类?因为后端开发是所有技术岗里对综合能力要求最苛刻的,你既要懂业务逻辑的代码实现,又要扛住高并发场景下框架和中间件的选型压力,还得在出问题时能往下钻到操作系统层面排查。所以A类试卷不会只考"会不会写代码",它要筛的是"能不能在电商业务里把代码写稳"。
2.2 从时间分配反推考点权重
参考同期笔试的通用设定,整份试卷时长一般在90到120分钟,题量控制在30到40道之间。典型的结构大致是:
| 题型 | 题量 | 单题分值 | 涉及范围 | 建议用时 |
|---|---|---|---|---|
| 单选题 | 15-20道 | 2-3分 | Java基础、计算机网络、操作系统、数据库 | 30分钟 |
| 多选题 | 5-8道 | 3-4分 | 同上,但更偏陷阱识别 | 15分钟 |
| 编程题 | 2-3道 | 15-25分 | 数据结构、算法、场景模拟 | 40-50分钟 |
| 简答/设计题 | 1-2道 | 10-20分 | 系统设计、SQL编写、问题排查思路 | 15分钟 |
关键信息在最后两列。选择题看起来量大,但均摊下来每题只有一分多钟的思考时间,它考的是知识点的条件反射——看到"volatile是否保证原子性"这种题,你根本不该犹豫超过十秒。真正拉开分差的是编程题和设计题,很多人前面选择题做太嗨,到编程题只剩二十分钟,结果算法题直接白卷交上去,这是笔试里最亏的失误。
2.3 我建议的答题顺序
个人经验是拿到试卷先花两分钟通读全部题目,然后把编程题在脑子里过一遍,判断哪道最有把握,优先做它。原因很现实:笔试系统一般会实时保存答案,但算法题一旦时间不够,你连部分分都难拿到,而选择题哪怕蒙也有概率得分。顺序上我习惯"编程题先行,设计题次之,选择填空最后"——当然如果你选择题基础特别扎实,也可以先快扫一遍选择题,把有十足把握的答案锁定,再去做编程题,回头再琢磨剩下的。
这种策略不是为了炫技,而是因为编程题的思维状态是"热"的,你刚坐下来头脑最清醒,最适合处理复杂逻辑;做到后面大脑疲劳了再去写代码,出错率会明显上升。
3. 选择题里的高频陷阱:Java、并发与JVM
3.1 Java基础:考的不是语法,是内存模型
A类试卷Java基础部分的高频考点,翻来覆去就是那几个:String和StringBuilder的区别、Integer缓存区间、equals与hashCode的契约、ArrayList和LinkedList的增删查复杂度、HashMap的扩容机制。这些题目单拎出来都不难,但笔试里会用组合方式制造陷阱。
举个例子。有一类经典问法:"下列哪些表达式返回结果为true?"然后列出:
Integer a = 127; Integer b = 127; System.out.println(a == b); // true,因为Integer缓存区间默认是-128到127 Integer c = 128; Integer d = 128; System.out.println(c == d); // false,超出缓存区间,会在堆上新建对象这题考的是Java自动装箱的缓存机制。你以为自己知道"128不等于128",但很多人栽在没记清缓存上限是127还是128。这就是典型的基础不牢——不是不会,是记混了。
再比如HashMap,2019年那会儿Java 8已经普及了,所以考点会集中在数组+链表+红黑树的结构上。常考的有:默认初始容量16、加载因子0.75、链表转红黑树的阈值是8、红黑树转回链表的阈值是6。还有一个容易被忽略的点:为什么转红黑树的阈值是8?因为源码注释里提到,在随机哈希码下,桶中节点数量服从泊松分布,链表长度达到8的概率已经极低(约千万分之六),所以这个阈值不是拍脑袋定的,而是统计学上的概率考量。知道这个原理,就算题目换个数字考泊松分布的近似概率,你也能有个大概判断。
3.2 并发编程:线程池参数是必考题
唯品会是电商平台,大促场景下并发是家常便饭,所以A类试卷的并发部分从来不是随便考考。核心考点集中在两处。
第一是ThreadPoolExecutor的核心参数。题目常这样出:一个线程池有核心线程数5、最大线程数10、阻塞队列容量100,当第106个任务提交时会发生什么?答案是要区分拒绝策略。默认的AbortPolicy会抛RejectedExecutionException,但如果是CallerRunsPolicy,任务会回退到提交它的线程执行。很多人只记了"队列满就拒绝",却忘了拒绝策略有四种,而且不同策略对系统行为的影响截然不同。
第二是synchronized和volatile的语义差异。这里有一个非常经典的判断题:volatile修饰的变量在多线程环境下自增操作是否线程安全?答案是不安全。原因是volatile只保证可见性和禁止指令重排,但不保证原子性,count++这种read-modify-write操作需要三步,中间依然可能被其他线程插入。这个考点在笔试里出现频率极高,面试环节也经常被拿出来追问。
3.3 JVM:垃圾回收与内存分区
JVM的选择题不会考太深,但会覆盖三个方向:内存区域划分、垃圾回收算法、常见参数配置。
内存区域这边,常考的是程序计数器、虚拟机栈、本地方法栈、堆、方法区各自存什么。有一道绕不开的题是"哪些区域不会发生OutOfMemoryError",答案是程序计数器,因为它占用的空间极小,且是唯一一个规范里没有规定OOM情况的区域。很多人会把方法区漏掉,其实方法区是会发生OOM的,尤其是大量动态生成类的时候(比如CGLIB代理)。
垃圾回收算法这块,主要对比标记-清除、标记-复制、标记-整理这三种算法的适用场景。核心记忆点是:标记-清除有碎片问题,标记-复制适合新生代因为存活对象少、复制成本低,标记-整理适合老年代因为要避免碎片同时还要保证内存连续。另外G1收集器在2019年已经是热点,要能说清它和CMS的核心区别是"基于Region分区"和"可预测停顿时间模型"。
4. 数据库与SQL:电商业务的地基
4.1 索引失效的经典场景
数据库是后端笔试的权重担当,选择题和编程题里都可能出现。选择题部分考得最密集的是索引失效场景,这几乎是每年必考。
常见的索引失效情况包括但不限于:对索引列使用函数运算(如WHERE SUBSTR(name, 1, 1) = 'a')、隐式类型转换(如字段是varchar但条件传了数字)、LIKE以通配符开头('%abc')、使用OR连接非索引列、范围查询右侧的列索引失效(联合索引最左匹配原则)。
笔试经常会这么考:给出一张用户表,字段有id、user_name、phone、age,分别建了索引,然后列出四条SQL,问哪一条能走到索引。要拿这种分,不能死记硬背,得理解最左匹配原则的本质:联合索引底层的B+树是先按第一列排序,再按第二列排序,所以查询条件里没有第一列的时候,索引树根本无从查起。
4.2 SQL编程题:连表查询和分组统计的套路
A类试卷的SQL题一般不会太难,但会结合电商场景。典型的题目是:订单表orders(order_id, user_id, amount, create_time),用户表users(user_id, user_name),要求统计每个用户的订单总额和订单数,并按金额降序排列。
SELECT u.user_name, COUNT(o.order_id) AS order_count, SUM(o.amount) AS total_amount FROM users u LEFT JOIN orders o ON u.user_id = o.user_id GROUP BY u.user_id, u.user_name ORDER BY total_amount DESC;这个条目看起来简单,但有几个隐藏得分点:第一,用LEFT JOIN而不是INNER JOIN,这样才能保留没有下过单的用户;第二,GROUP BY后面要同时包含u.user_id和u.user_name,否则在ONLY_FULL_GROUP_BY模式下会报错;第三,ORDER BY用别名total_amount是允许的。
另一种高频题型是分页查询,比如"查询每门课程成绩排名前3的学生"。这种题要用到窗口函数ROW_NUMBER()或者关联子查询。窗口函数写法更干净:
SELECT student_id, course_id, score FROM ( SELECT student_id, course_id, score, ROW_NUMBER() OVER (PARTITION BY course_id ORDER BY score DESC) AS rk FROM scores ) t WHERE rk <= 3;如果你还在用MySQL 5.7或更早版本,窗口函数可能用不了,那就得用"自连接+计数"的思路实现同样效果。笔试的时候先确认一下题目要求的数据库版本,再决定用哪种写法。
5. 计算机网络与操作系统:藏在选择题里的"硬骨头"
5.1 TCP三次握手与四次挥手:不背结论,推流程
网络部分有两类题出现频率最高:TCP连接管理相关,以及HTTP状态码相关。TCP的题经常是结合"为什么"来考的,比如"为什么握手是三次,而不是两次或四次"。
答案的核心在于确认双方收发能力的对称性。第一次握手客户端发出SYN,服务端收到后知道客户端的发送能力正常;第二次握手服务端回SYN+ACK,客户端收到后知道服务端的收发能力都正常,同时也确认了自己的发送能力正常;第三次握手客户端回ACK,服务端收到后才知道客户端的接收能力正常,同时也确认了自己的发送能力正常。如果是两次握手,服务端没法确认客户端的接收能力是否正常,一旦客户端没有收到第二次握手的包,服务端却建立了连接,就会造成资源浪费。这个推演过程比死记"三次握手"四个字重要得多。
三次挥手在笔试里也经常被问到,但更常考的是TIME_WAIT状态。为什么要等2MSL(最大报文段生存时间)?两个原因:第一,确保最后一个ACK报文能到达服务端,如果丢了还能重传;第二,让旧连接中迟到的数据报文在网络中消失,避免干扰新连接。选择题里喜欢问"TIME_WAIT出现在主动关闭方还是被动关闭方",答案永远是主动关闭方。
5.2 HTTP与HTTPS:电商场景下的必答题
电商平台对数据安全要求高,所以HTTPS相关题目几乎必考。核心问题是HTTPS的握手过程,以及对称加密和非对称加密分别用在哪里。
记住一个关键点:HTTPS握手时先用非对称加密协商出对称会话密钥,后续数据传输全部用对称加密。原因很直白:非对称加密(如RSA)性能差,不适合大数据量传输;对称加密(如AES)性能好,但需要解决密钥分发问题。非对称加密正好用来安全地传递对称密钥。选择题可能会考"HTTPS默认端口是443,HTTP是80"这种送分题,也可能考"证书的作用是什么"——答案是验证服务器身份,防止中间人攻击,而不是用来加密数据的。
5.3 操作系统:进程线程和死锁
操作系统在开发A类试卷里占的比重不高,但偶尔会有一两道。重点集中在进程与线程的区别、死锁的四个必要条件、进程间通信方式。
死锁这边4个条件是经典考点:互斥、持有并等待、不可剥夺、循环等待。只要破坏其中一个,死锁就能解除。选择题可能会给一个场景问你"下列哪种方式可以避免死锁",答案经常是"一次性申请所有资源"(破坏持有并等待)或者"设置资源编号并按序申请"(破坏循环等待)。进程间通信方式要能区分:管道、消息队列、共享内存、信号量、Socket,其中共享内存速度最快,因为不需要内核态与用户态之间的数据拷贝。
6. 编程题实战拆解:三种逃不过的算法考法
6.1 链表和树的边界条件操练
编程题第一类考法是数据结构的操作题,难度接近LeetCode中等偏下。链表反转是入门,但笔试不会直接让你反转整个链表,一般会变体——比如"反转链表中从m到n的区间"或者"每K个节点一组反转"。
做这类题的关键,说白了就是把指针操作的每一步都想清楚,特别是在处理prev、curr、next三个指针的衔接顺序时,稍微乱一步就会形成环或者断开。我的建议是笔试前把链表反转的迭代写法练到能默写,因为很多复杂链表题都是在这个基础上扩展的。
树的部分,高频题包括二叉树的层序遍历、最近公共祖先、前序中序重建二叉树。其中最近公共祖先(LCA)是一个很好的考察点,因为它既能用递归思路解,也能用非递归的迭代思路解,面试官可以从你选择的方法判断你是"背了模板"还是"理解了本质"。递归版本:
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if (root == null || root == p || root == q) return root; TreeNode left = lowestCommonAncestor(root.left, p, q); TreeNode right = lowestCommonAncestor(root.right, p, q); if (left != null && right != null) return root; return left != null ? left : right; }这个递归里有一个容易被忽略的点:如果p或q中有一个是根节点,递归会直接返回根节点,不再往下走。这是正确的,因为根节点本身就是p和q的祖先。理解了这个语义,你才算真正吃透了代码。
6.2 动态规划:从暴力递归到状态压缩的完整推导
编程题第二类必考的是动态规划。唯品会这种电商公司,特别喜欢用"背包类"和"区间类"的题目来考察候选人。为什么会这样?因为电商业务里大量问题本质上是资源分配问题——比如促销满减的最优组合、仓库配货的最优方案,都是DP的变体。
以经典的0-1背包为例,笔试中你会遇到的基本形式是:有n个物品,每个物品有重量w[i]和价值v[i],背包容量为W,求最大价值。标准解法是二维DP:
int[][] dp = new int[n + 1][W + 1]; for (int i = 1; i <= n; i++) { for (int j = 0; j <= W; j++) { if (j < w[i]) { dp[i][j] = dp[i - 1][j]; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]); } } }但如果你只写到这个程度,在笔试里只能拿基础分。真正能拉开差距的是你能不能想到滚动数组优化到一维:因为dp[i][j]只依赖上一行的数据,所以可以压缩成dp[j]表示容量为j时的最大价值,但需要注意内层循环必须从大到小遍历,否则会覆盖掉上一轮的旧值,导致同一物品被重复放入。这个"倒序"的细节,笔试和面试都爱问,说清楚为什么,说明你是真的理解状态转移而不是背代码。
6.3 场景模拟题:把业务逻辑翻译成代码
第三类编程题是场景模拟,这类题最贴近唯品会的业务生态。典型题目是:"实现一个简单的购物车结算系统,给定商品单价和购买数量,计算实际应付金额,满199减30,满299减50,且优惠券和满减不能同时使用。"这种题本身算法不复杂,但考查的是代码结构的清晰度和异常处理能力。
我见过不少候选人在这种题上翻车:商品列表为空时取价格报空指针、金额相加用int导致溢出、满减和优惠券的逻辑写成一坨if-else嵌套。笔试系统跑测试用例的时候,这些边界条件全是扣分点。
高频场景题还有LRU缓存淘汰策略,这个真的是每年必考,尤其适合用LinkedHashMap实现,但面试官更想看到你用HashMap+双向链表手写一版,因为这样能考察你对O(1)读写要求的理解。
7. 系统设计题:秒杀场景的应对思路
7.1 这道题为什么年年出现
简答或设计题部分,2019年A类试卷很有代表性的一道是"设计一个秒杀系统"。很多同学看到这种题就慌,觉得没做过高并发项目,没法答。但笔试里的系统设计题其实不是让你真的写一整套微服务架构,而是考察你有没有基本的架构意识和取舍能力。
秒杀系统的核心矛盾是:短时间内大量请求涌入,但商品库存是有限的,不能超卖也不能少卖。答题的时候最忌讳一上来就堆技术名词:Redis、MQ、分库分表,堆了一堆却说不清每个组件解决什么问题。
7.2 一个能拿分的答题框架
我建议按下面这个思路组织答案:
第一层,流量控制。前端做限流和按钮置灰,防止用户疯狂点击刷新;网关层做IP维度限流,拦截明显异常的请求。
第二层,请求削峰。用消息队列把秒杀请求异步化,先快速返回"请求已接收",再慢慢消费队列里的请求。注意消息队列在这里不是用来提升性能的,而是用来保护下游数据库的——这属于"削峰填谷"的作用,很多人会答错成"提高并发能力"。
第三层,库存扣减。不要在数据库层面用UPDATE stock SET count = count - 1 WHERE count > 0这种方式直接扣,因为行锁竞争会非常剧烈。常规做法是预先加载库存到Redis,用Redis的原子递减操作来扣减,库存扣完就拒绝后续请求,最后再异步把最终结果同步回数据库。这里考察的关键点是"预扣库存+超时释放"的流程设计。
第四层,数据一致性。要说明Redis和MySQL之间的最终一致性怎么保证,比如用对账任务定期核对,发现不一致时补偿。
只要把这四层结构答清楚,即使你没在高并发项目里实战过,面试官也能看得出你有系统性的思考。
7.3 顺着设计题面试官会追问什么
笔试之后的面试环节,面试官大概率会顺着你的答案追问细节。问得最多的是"Redis库存扣减完了,数据库怎么同步"以及"消费者服务挂了怎么办"。这两个问题考的都是你对分布式系统失效场景的敏感度。第一个问题可以用"订阅消息+定时对账双通道"来解;第二个问题,回答"MQ的ack机制和重试策略"是基础,能答出"保证消费者幂等性,防止重复扣减"才是加分项。
8. 用这份试卷规划你的刷题路线
8.1 先做一次"体检",再决定优先级
拿到像唯品会2019秋招A类试卷这样的资料,不要一上来就埋头刷完整套题。我建议先自己掐时间做一遍,然后按"判断类题正确率、代码题通过率、设计题完整度"三个维度给自己打分。你会发现自己的短板非常明显:有人选择题能拿90%,但编程题第一题就卡住;有人代码写得飞起,但数据库判断题错一半。
根据短板定优先级:选择题拖后腿,就集中刷Java基础和并发编程的题目,每天固定练30道判断题并整理错题本;编程题不过关,就按"链表、树、DP、模拟"四个专题刷LeetCode高频题,每天至少2道并保证能讲清每道题的思路;设计题没头绪,就找常见的电商场景题(秒杀、购物车、订单超时取消、优惠券发放)逐个写架构思路,写的过程中逼自己画出组件和数据流向。
8.2 2019年的高频考点放到今天哪些仍然有效
很多人问,2019年的试卷内容会不会已经过时了?我的判断是:核心算法和计算机基础完全没过时,但部分技术选型层面的东西需要更新。
快照一下:
| 2019年考点 | 现在的变化 | 备考建议 |
|---|---|---|
| HashMap、ConcurrentHashMap实现 | Java 8之后的实现基本稳定 | 依然要重点掌握,尤其红黑树相关 |
| JDK 1.8的Stream | 现在JDK 17都普及了 | 新题会考var、records等新语法,但核心集合框架不变 |
| Spring Boot 2.x | Spring Boot 3.x已经发布 | 笔试选择通常不深考框架版本,面试才会问到 |
| MySQL 5.7索引优化 | MySQL 8.0的窗口函数更常考 | 窗口函数建议重点练,尤其排名类题目 |
| 秒杀系统设计 | 方案没变,但更强调幂等和分布式事务 | 答题框架可以直接复用,补充幂等设计细节 |
所以这份试卷的价值不在"押题",而在于让你看清校招笔试对基础能力的要求是持续且稳定的。
9. 踩坑实录:我从面试官视角看到的典型翻车现场
笔试这件事,站在面试官这边看,能发现很多候选人自己意识不到的共性问题。我在这里总结几个真实场景,希望能帮你避开同样的坑。
第一个翻车点是选择题改答案。在线笔试系统里,有些同学做完之后反复回头看,把本来对的答案改错了。尤其在多选题上,规则是"少选得部分分、错选不得分、多选不得分",你要做的不是追求每个选项都选全,而是确保你勾选的每一个选项都有十足把握。拿不准的选项宁可少选,也不要赌。
第二个翻车点是不做复杂度的评估。编程题提交之后,系统会跑测试用例,不仅有正确性用例,还有大数据量用例。很多人代码逻辑对了,但用了两层遍历导致超时,只能拿一半的用例通过。建议在动手写代码前,先把数据规模看清楚,估算你的算法复杂度。比如输入规模是10^5,O(n^2)基本必挂,这时候哪怕思路朴素一点,也要先想能不能降一个数量级。
第三个翻车点是设计题只写方案不写取舍。设计题给的是一个开放性问题,你能想到Redis、MQ、分库分表这些组件是好事,但面试官更希望看到你说清楚"为什么不直接同步调用""为什么库存要预扣"。答题的时候,每提出一个技术方案,都补一句"这样做解决了什么问题、带来了什么副作用、怎么规避",整个答案的档次立刻不一样。
第四个翻车点是时间分配失衡。我见过太多人前面选择题做太仔细,编程题一看时间只剩20分钟,心态崩溃,最后交上去两行注释。前面也强调过,编程题的分值占比是最高的,选择题蒙几道题最多丢十分,编程题空着直接丢二十分起步。所以无论如何,留足40分钟做编程题是优先级最高的事情。
10. 笔试之外:这份试卷映射出的人才标准
把整份试卷里的考点串联起来看,其实能还原出唯品会这类电商大厂对校招开发工程师的期待画像。
第一,基础要扎实到条件反射。选择题的高频考点就是Java基础、并发、JVM、数据库、网络、操作系统这六块,它们恰好是大学课程和日常开发中的地基。大厂招校招生的逻辑不是招进来立刻能上手业务,而是招进来之后能在半年内快速成长,基础扎实的人成长速度明显更快。
第二,要具备用代码解决业务问题的能力。编程题里出现场景模拟题,设计题是秒杀系统,全都在围绕电商业务展开。这说明笔试不满足于"LeetCode刷题机器",而是希望看到你能把抽象的算法题转化成实际业务场景下的可落地方案。
第三,表达要有条理。设计题的答案是最能看出一个人思考是否有结构的。一上来就堆技术名词的人,和先讲"核心矛盾是库存有限、先解决问题主链路、再考虑旁路细节"的人,高低立判。这种结构化的表达能力,笔试阶段就在考察了。
用一句话总结就是:他们找的不是最会刷题的人,而是把基础打牢、思考有章法、能在不确定性里做出合理取舍的人。这个标准,放在哪一年秋招都成立。
最后分享一个我实操中经常用的方法:拿到一份旧试卷,别急着做,先把试卷目录抄一遍,再对照当前的技术栈把考点更新一遍,然后用新考点去搜对应的最新题目来练。这种行为看起来多花了半小时,但比盲目刷十道题都管用。希望这篇拆解能帮你把这份四年前的试卷用出它真正的价值。