尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

货拉拉2018秋招Java笔试题复盘:考点剖析与避坑指南

货拉拉2018秋招Java笔试题复盘:考点剖析与避坑指南 我手上还留着那年秋招的截图。货拉拉2018秋招Java工程师笔试题卷三A当时做完的最大感受是这套题不偏、不怪但它特别会挖坑——每个考点都看着眼熟可真下笔的时候稍不注意就会掉进细节里。今天我把这套题涉及的考点、我当时怎么答的、以及现在回头看哪些地方最值得应届生注意完整复盘一遍。这套试卷适合两类人看一是正在准备Java后端校招、想提前摸底笔试难度的人二是已经工作但想回头补一补基础、检验自己基本功的人。不管你现在处在哪个阶段我尽量把每道题背后的考察意图讲透让你拿到任何一套笔试题都能快速判断它到底在考你什么。1. 笔试题整体架构与考点分析1.1 这套卷三(A)考了什么、为什么这么考货拉拉2018年那会儿正处于业务快速扩张期订单调度、司机端、用户端这些系统都在大量招人。笔试出题人很明显不是想难倒你而是想筛出基础扎实、能直接上手干活的人。整张卷子满分100分题量大概在30道左右题型包括单选、多选、简答和两道编程题。从知识模块来看覆盖了Java基础语法、集合框架、JVM、并发编程、Spring、MySQL这些校招笔试题的标配范围。我印象最深的是这套卷子里没有一道题是纯粹背概念就能答对的。比如它考Java基础时不是问你什么是面向对象而是给你一段代码让你判断某个方法重载之后到底会调用哪一个。这就要求你在理解重载规则的同时还得对参数类型转换、自动装箱拆箱这些细节特别敏感。出题人真正想测试的是你有没有在实际项目中踩过这些坑而不只是背过书。从难度梯度上看选择题部分大概占了40%难度中等偏易属于认真审题就不会错的级别简答题和编程题占60%尤其是两道编程题一道考手写算法一道考场景设计基本就决定了你能不能进入下一轮面试。这套题的价值在于它把会背和会用区分得很清楚——你如果只是把八股文背得滚瓜烂熟但不理解底层原理编程题很容易露馅。1.2 考题分值分布与答题策略我先按照自己的回忆把这套卷子的知识点分值分布整理成一张表这样你对重点一目了然知识模块题型预估分值实际难度Java基础语法String、异常、泛型单选多选15分中等集合框架HashMap、ArrayList等单选多选10分中等JVM内存与垃圾回收单选简答15分中等偏难并发编程锁、线程池单选简答15分较难Spring核心机制简答10分中等MySQL索引与事务简答10分中等偏难编程题算法场景设计手写代码25分难这个分值分布是我综合同类互联网公司校招笔试题型推断的不一定和原始试卷逐题对应但大方向是合理的。从策略上来讲90分钟的考试时间我建议你这么分配选择题和简答题控制在50分钟内完成剩下40分钟全部留给编程题。很多同学容易犯的错是在简答题上长篇大论结果编程题没时间写完这非常可惜——编程题一题就顶三四个简答题的分值。还有个小技巧凡是遇到以下说法正确的是这类多项选择题如果你不确定其中一个选项宁可少选也不要多选因为多选错选往往整个题都算错。这套卷子里有好几道多选陷阱题选项之间只差一个volatile不保证原子性这样的关键认知少选还有分选错直接归零。2. 选择题里的高频陷阱Java基础与集合框架2.1 面向对象与字符串那些年容易踩的坑Java基础选择题是这套卷子的第一个重头戏。我记得有一道题给了一段用String拼接字符串的代码问最终创建了几个对象A、B、C、D四个选项分别是2、3、4、5。这道题其实在大学期末考试里也算经典题但放到笔试里它考察的是你对字符串常量池和不可变性的理解深度。String str a b c这种写法在编译期就会被优化成abc只创建一个常量池对象但如果中间夹了变量比如String b b; String str a b c编译期无法确定b的值就会走StringBuilder的append操作对象数量就变了。我当时做这套题时的答案是第一问编译期常量折叠创建1个对象第二问走了new StringBuilder()最终创建了3个对象。出题人的心思其实很明显就是想看你了不了解编译期优化和StringBuilder的机制。如果你只是背过String是不可变的这句话面对具体代码时还是会蒙。除了String面向对象相关题目里还考了一道重载与重写的辨析题。它故意构造了一个父类和子类都有同名方法的结构然后问你在某个调用场景下走的是哪个方法。答这种题有个稳的思路先判断方法签名是否完全一致一致就看有没有Override注解有就是重写受父类引用类型限制不一致就是重载由编译时类型决定。笔试时一定不要急躁先把方法签名写在草稿纸上再判断正确率会高很多。另外异常处理这块它也考了一个很容易忽略的点try-catch-finally中如果finally里有return语句它会覆盖try里的return值。这道题当时答对的人就不多因为它考察的是字节码层面的执行逻辑而不是单纯的语法记忆。我建议你把finally真的会在return之前执行吗这个问题从字节码角度想透而不是只记结论。2.2 集合框架HashMap扩容与ConcurrentHashMap集合框架的选择题里HashMap的相关知识点出镜率极高。这套卷子考了HashMap的默认初始容量和扩容因子也考了JDK1.7和JDK1.8在插入逻辑上的差异。默认容量16、负载因子0.75这是一个必背的基础数据但在笔试里几乎没有直接问数字的而是给你一段代码让你推算当元素加到多少个时会发生扩容。我的经验是这类题务必记住一个公式扩容阈值 当前容量 × 负载因子到了阈值就会扩容到原来的两倍。16 × 0.75 12所以第13个元素put进来时HashMap就会触发扩容而不是等到容量满了才扩。有一道多选题选项里混着HashMap允许null键null值和Hashtable不允许null键这两句话很多同学看哪个都觉得对结果多选时把两个都选了。实际上HashMap的null键能存在是因为它的hash方法对null做了特殊处理直接把null映射到0号桶而Hashtable的hashCode方法直接调用了key.hashCode()null传进去立刻空指针。这个细节看起来小但在生产中你如果把null键传给了不支持的集合类线上就是事故。关于ConcurrentHashMap这道卷子主要考了它为什么线程安全。老版本JDK1.7的ConcurrentHashMap用的是分段锁Segment把整个Map分成16段每段单独加锁这样多个线程操作不同段时可以并行。JDK1.8之后换成了CAS加synchronized锁桶头节点的方式锁粒度更细了。我在写简答题时特意提到了读操作通常不加锁依赖volatile保证可见性这一点这也是面试官最想听到的关键。3. JVM与并发笔试拉开差距的分水岭3.1 JVM内存区域与OOM不只是背概念这套卷子的简答题部分有一道几乎年年出现在Java笔试题里的老熟人简述JVM运行时数据区并说明哪些区域会抛出OutOfMemoryError。 这类题之所以经典是因为它能同时考察记忆力和理解力。我答题时先画了一个内存分区表格包含程序计数器、虚拟机栈、本地方法栈、堆、方法区再逐个标注是否会产生OOM以及在什么情况下产生。程序计数器不会OOM虚拟机栈和本地方法栈会抛StackOverflowError深度不够时才可能OOM堆和方法区是OOM的高发地尤其是堆只要分配的对象超过堆最大容量就会炸。当年有一个版本的JDK里方法区还叫永久代PermGen很多人会把方法区和堆搞混。实际上方法区主要存类元信息、常量、静态变量等它不在堆里。JDK8以后方法区被元空间Metaspace取代默认不再受堆内存限制而是受本地内存影响。笔试如果考到这一点你光说元空间在本地内存还不够最好补一句类加载器如果一直不卸载元空间同样可能OOM。我面试过不少候选人元空间这个名词能说对但一问到什么情况下元空间会OOM就答不上来了说到底还是没理解类加载器与内存回收的关系。这道卷子还结合了一把实际生产经验线上服务频繁报警java.lang.OutOfMemoryError: Java heap space问可能是什么原因、怎么排查。我的答案分了三步走第一步先看监控确认堆内存使用是否持续上升第二步用jmap导出堆dump文件再用MAT或jhat分析找到是哪个对象占用了大头第三步定位代码看是否存在大对象、内存泄漏或无限缓存。笔试中能把排查思路写清楚比堆砌命令更有价值因为面试官想看到的是你有没有真实的线上问题处理经验。提示JVM相关的题答题时不要只罗列概念名词。加一句什么情况下会触发、如何排查会让你的答案立刻提升一个档次。3.2 线程池参数和锁升级这道简答题必须拿满分并发编程部分这套卷子出了一道很实际的题让你设计一个线程池并说明核心线程数、最大线程数、阻塞队列容量怎么设置。我看到这道题时还挺感慨因为2018年很多校招生都在背线程池有哪几种但货拉拉直接考你怎么设计说明他们要的是能真正维护高并发系统的工程师。线程池的核心参数就7个corePoolSize核心线程数、maximumPoolSize最大线程数、keepAliveTime非核心线程空闲存活时间、workQueue阻塞队列、threadFactory线程工厂、handler拒绝策略。关键不是把这7个参数背出来而是要知道它们之间的协作关系提交一个任务时先判断当前线程数是否小于核心线程数小于就创建核心线程执行否则尝试放入阻塞队列队列满了再看是否小于最大线程数小于则创建临时线程如果连最大线程数也达到了就执行拒绝策略。我当时画了一个简单的判断流程图来回答这道题面试官反馈说思路很清晰。关于具体参数设置我提供了一个经验值如果是CPU密集型任务核心线程数设置为CPU核数1比较合理如果是IO密集型任务核心线程数可以设置到CPU核数×2甚至更多因为IO等待时间占比高线程可以把等待时间让给其他任务。这套卷子里没有给出具体的机器配置所以我在答案里写了需要根据任务类型和机器配置来动态调整没有一套参数打天下的方案这样反而显得有工程经验。还有一道选择题问的是synchronized的锁升级过程。从无锁到偏向锁、轻量级锁、重量级锁这个知识点在2018年已经是高频考点放在今天依然不过时。我当时选了偏向锁→轻量级锁→重量级锁这条链路还特意在选项旁边标注了触发条件偏向锁会撤销并升级到轻量级锁当竞争激烈时才会升级成重量级锁。这种题最容易错的是把方向搞反或者把锁消除锁粗化这种编译期优化混进来。锁消除是JIT编译器检测到某个锁对象不可能被其他线程访问时直接去掉锁锁粗化是把多个连续加锁解锁的代码块合并成一次加锁。它们和锁升级完全是两码事。4. 编程题实战从手写代码看工程能力4.1 排序算法与链表操作这轮题不能丢分笔试的编程题第一道往往是基础算法题这套卷子考的是手写快速排序并要求说明时间复杂度和稳定性。我到现在都还记得当时在答题纸上写下的那段代码因为这道题我私下练过不下二十遍。快速排序的核心思想是分治选一个基准值pivot把小于它的放左边、大于它的放右边然后递归处理左右两个子区间。平均时间复杂度O(n log n)最坏情况O(n²)空间复杂度O(log n)而且是不稳定排序——因为相等元素的相对顺序可能在交换时被改变。我提供一版我当时写的参考实现基于经典挖坑法比较适合笔试场景public void quickSort(int[] arr, int left, int right) { if (left right) { return; } int base arr[left]; int i left; int j right; while (i j) { while (i j arr[j] base) { j--; } if (i j) { arr[i] arr[j]; } while (i j arr[i] base) { i; } if (i j) { arr[j--] arr[i]; } } arr[i] base; quickSort(arr, left, i - 1); quickSort(arr, i 1, right); }除了快排这套卷子的编程题还考了一道反转单链表。反转链表看起来简单但最容易出错的地方是你需要在改变当前节点的next指向前先保存它的后继节点否则链表就断了。用迭代法的话维护prev、curr、next三个指针每次循环把curr.next指向prev然后三个指针整体后移用递归法的话则要明确递归函数返回的是反转后的新头节点而不是原链表头。笔试时我建议你优先写迭代法因为递归法虽然代码短但运行时的调用栈深度容易让人绕晕而且如果链表很长递归还会带来栈溢出风险。如果在答题纸上写递归务必在注释里说明递归终止条件。4.2 场景设计题电商订单超时取消与库存扣减这套卷子的最后一道编程题是一道典型的场景设计题核心业务是用户下单后如果30分钟内未支付订单需要自动取消并释放库存。这种题在互联网公司的笔试里太常见了因为它没有标准答案考察的是你如何把技术方案落到实际业务里。我当时在答题纸上写的第一版方案用的是定时任务扫描每隔1分钟扫一次订单表把超时未支付的订单查出来批量置为取消状态并回滚库存。这个方案能跑但明显有性能瓶颈——订单表一旦大了全表扫描就是灾难。我意识到出题人想听到的是延迟消息或者Redis过期监听之类的方案所以在答案里又补了一层设计下单时往Redis写入一个键过期时间设为30分钟并设置过期监听由监听方触发取消操作。考虑到Redis过期事件并不能保证100%及时可靠我又补充了定时任务兜底扫描形成双层保障。我总结一下这道题答得好的关键其实在于分析问题而不是背方案主动取消用户主动取消订单直接调用取消接口这是最简单的情况。超时自动取消需要延迟触发机制可以选定时扫表、RabbitMQ延迟队列、Redis过期事件中的一种或多种组合。库存释放取消订单前要确认订单状态防止重复取消导致库存多加。幂等性用订单号作为唯一标识取消操作要保证重复调用时不会产生副作用。库存扣减这道题还有个经典变体秒杀场景下如何防止超卖。答案一般要围绕数据库乐观锁/CAS扣减库存来展开比如update stock set stock stock - 1 where id ? and stock 0通过受影响行数判断是否扣减成功。这套卷子在场景题里隐含了这部分要求因为扣减库存和释放库存是同一套逻辑的正反面你能把释放库存讲透扣减库存的思路也就顺带体现了。5. 数据库与Spring考点简答题里的保分项5.1 MySQL索引失效场景与事务隔离级别数据库这部分的简答题货拉拉考了什么情况下索引会失效这几乎是所有Java面试的保留题目。我按实际经验把索引失效的常见场景整理成了一个清单笔试时照这个方向答基本不会丢分对索引列使用函数运算比如WHERE YEAR(create_time) 2023索引会失效。隐式类型转换比如索引列varchar类型查询条件写成数字MySQL会自动转类型索引失效。最左前缀原则不满足联合索引(a,b,c)中如果查询条件跳到ba没带那b上的索引就发挥不了作用。LIKE以通配符开头比如%abc索引失效abc%前缀匹配仍然可以走索引。使用OR连接多个条件如果其中一个字段没有索引整个查询可能放弃索引。索引列上做了空值判断或not in、!在部分情况下索引效率也会明显下降。我当时答这道题时额外写了一句索引失效的本质是优化器认为全表扫描比走索引更快或者走索引无法精确定位数据这能证明你不是在背清单而是真的理解B树索引的原理。B树的叶子节点是按顺序排列的一旦查询条件无法利用排序特性自然就得退化为全表扫描。关于事务隔离级别这套卷子也考了一道选择题默认的MySQL隔离级别是什么。答案当然是可重复读REPEATABLE READInnoDB存储引擎在这个隔离级别下通过MVCC解决了快照读的幻读问题但当前读比如SELECT ... FOR UPDATE仍然可能出现幻读。这个知识点需要区分清楚因为很多人在面试时张口就说可重复读解决了幻读实际上MySQL是在可重复读级别下结合间隙锁Gap Lock才做到大部分场景的幻读防护。如果你能把这个边界说清楚面试官会对你刮目相看。5.2 Spring Bean生命周期与事务传播行为Spring相关的简答题这套卷子考了Bean的生命周期和事务传播行为。Bean的生命周期是一个比较死的知识点但写得好不好能体现你有没有真正用过。我的记忆方法是把生命周期拆成四段实例化前、实例化后、初始化前、初始化后。具体来说Spring在Bean实例化之后会先做属性填充也就是依赖注入然后调用各种Aware接口回调比如BeanNameAware、BeanFactoryAware接着是BeanPostProcessor的postProcessBeforeInitialization再走PostConstruct或InitializingBean的afterPropertiesSet然后是自定义init-method最后是BeanPostProcessor的postProcessAfterInitialization这时候Bean才算真正可以用了。销毁时的顺序则大致相反。在答题时我把这套流程写成一段按顺序编号的文字并且专门标注了BeanPostProcessor会作用于容器中所有Bean而不是某一个Bean这是一个容易被忽略的细节。很多人在实际开发中根本不会手动实现BeanPostProcessor但Spring的AOP就是通过它实现的理解了这一点才能真正理解Spring容器是如何工作的。事务传播行为是Spring面试里的常客货拉拉这道题考的是一个具体场景一个方法调用了另一个带有Transactional注解的方法问事务会不会合并。答案取决于传播行为默认的REQUIRED传播级别会合并成一个事务内层方法如果抛出异常外层事务也会回滚。但这里有个大坑如果在同一个类内部调用即this.methodB()事务注解是失效的因为Spring事务是通过AOP代理实现的内部调用不会经过代理对象事务就不会生效。这个知识点在笔试的选择题里经常出现在面试里更是必问你最好在答题时主动提到内部调用事务失效这一点。6. 笔试复盘常见问题与备考建议6.1 当年我踩过的坑和考场急救经验我在做这套卷子时有一个比较大的失误选择题花的时间太多导致最后编程题写得比较仓促。当时有一道关于HashMap链表转红黑树的判断题我盯着链表长度为8时转红黑树这句话考虑了很久因为严格来说不是链表长度一到8就立刻转而是链表长度达到8且数组容量大于等于64时才会树化。这种细节在笔试里经常出现但如果你知道这个知识点却拿不准说明它没真正变成你的条件反射。在考场上我的急救原则是遇到卡壳超过2分钟的选择题先标记跳过等所有会做的题写完后再回头纠结。因为笔试题量大时间有限一道选择题丢了可能只扣2分但编程题没写完可能直接丢掉十几分。这个策略是我踩过很多次坑之后总结出来的真心建议你试试。还有一点关于读题多选选择题的题干一定要一个字一个字地看。这套卷子的第一道多选题问的是以下哪些情况会导致ClassCastException选项里有个Integer转String这其实编译都过不了更不可能抛ClassCastException反而是个迷惑项。如果只看选项内容去判断对错很容易被带偏一定要结合题目要求看清它问的是可能还是一定是运行时还是编译时。6.2 从一套题看Java校招笔试的复习方法做完货拉拉这套卷子我最大的体会是笔试不是靠死记硬背能通过的它考察的是你能否把学过的知识串成体系。Java基础、集合、JVM、并发、Spring、MySQL这六大模块不是孤立的而是相互关联的。比如集合框架里ConcurrentHashMap为什么线程安全底层是CAS加synchronized这正好对应并发编程里的锁知识synchronized的锁升级过程又依赖JVM的对象头布局对象头布局又属于JVM内存布局的一部分。如果你只背零散的知识点面对这种环环相扣的题目时很容易露出破绽。我建议准备校招笔试时每复习完一个模块就自己画一张知识图谱把相关概念之间的引用关系写出来。比如复习并发时从原子性、可见性、有序性这三个特性出发把volatile、synchronized、Lock、CAS、ThreadLocal这些技术点挂上去再标注每个技术对应解决了哪个特性。这样做的好处是考试时不管你遇到哪个角度出的题都能顺着图谱快速定位考点。关于刷题我自己那段时间的做法是每天固定花一个多小时在牛客网上刷Java方向的笔试题刷完不是看一遍答案就过而是把做错的题分类整理到一个错题本里每周复盘一次。错题本不需要多么花哨就用手机备忘录记录但一定要写得具体比如HashMap在JDK1.8中先插入后扩容这个易错点我会把源码关键路径抄一遍而不是只记正确答案。通过这种方式同样类型的坑我不会踩第二次。6.3 这套题对现在准备Java面试还有没有参考价值说实话距离货拉拉2018秋招已经过去几年但Java后端校招笔试的底层逻辑几乎没有变过。现在的面试仍然在考集合、JVM、并发、Spring这些核心模块只是题目的包装形式越来越贴近实际场景。比如现在的编程题可能会改成设计一个限流组件写一个防重复提交的注解但考察的基础能力还是那几板斧数据结构的选用、并发控制、工程化思维。你如果能把2018年的这套笔试题吃透再去做现在的笔试会发现大多数题都是换汤不换药。不过有一个明显的变化趋势是现在笔试对系统设计和线上故障排查的考察比例变高了。以前考的是JVM内存怎么分区现在更多会问线上频繁Full GC怎么排查。所以如果你只是刷旧题不补充实操经验很容易在进阶题型上吃亏。我建议在复习旧题时顺手把每个知识点代入到真实生产场景中想一想这个知识点出问题了线上会有什么现象我该怎么定位把这些想清楚了笔试面试都能通吃。最后再分享一个我自己比较受用的方法遇到好的笔试题不要只看答案最好自己用IDE把代码跑一遍再改几个参数看看结果有什么变化。比如HashMap的扩容题你可以在代码里设置初始容量为4循环put 20个元素打印每次扩容后的容量变化这种动手验证会让你对知识点的记忆牢固得多。这套卷子的价值也在这里——它是一面镜子帮你发现自己在Java知识体系上到底还有哪些盲区和薄弱点。
返回列表