有些题五年之后回头看依然是面试官手里最好用的试金石。网易2018校园招聘Java开发工程师BJ笔试卷这套题在我硬盘里躺了很久。每年帮人改简历、做模拟面试的时候我都会把它翻出来重新过一遍。原因很简单这套题出得很有水平不堆砌偏题怪题每一道都在敲打Java工程师真正该有的地基——集合、并发、JVM、网络、数据库、设计模式全是日常开发里绕不开的东西。尤其适合两类人一类是准备校招、想进大厂的应届生另一类是工作两三年、想系统性查漏补缺的初级工程师。这篇文章我不会把卷子从头到尾念一遍而是把里面涉及的考点逐个拆开讲清楚每道题在考什么、为什么这么考、底层原理是什么以及我当时踩过的坑和总结出来的答题思路。你能拿到的不是一份答案而是一套可以迁移到任何Java面试里的知识框架。1. 整体设计与考点拆解这套笔试卷到底在考什么1.1 从试卷结构看网易的用人标准先说结论网易这套卷子考察的核心不是“你背了多少API”而是“你有没有在真实项目中踩过坑”。整张卷子覆盖了Java基础语法、集合框架、JVM内存模型、多线程并发、网络编程、数据库索引与事务、Spring核心机制、设计模式以及两道开放性的算法/逻辑题。乍一看覆盖面很广但仔细看会发现一个规律几乎所有题目都指向同一个能力模型——能不能用Java写出高性能、高可用、可维护的生产级代码。举个例子卷子里关于HashMap的题目表面上是问“HashMap和Hashtable的区别”但实际上是在考察你知不知道哈希冲突怎么解决、为什么JDK 1.8之后引入红黑树、扩容时为什么是2的幂次方、并发环境下HashMap会出什么问题。这些知识点单纯背面试题集是背不全的必须在写代码时真正遇到过、排查过才能答出深度。再比如JVM相关的题目网易没有直接问“JVM内存分为哪几块”而是给了一个具体的OOM场景让你分析可能的原因和排查思路。这种出题方式筛选的就是那些真正处理过线上问题的人。如果你只在IDE里写过CRUD没看过GC日志、没dump过堆内存这道题基本无从下手。1.2 “八股文”与真实能力的边界现在网上把Java面试题统称为“八股文”好像背一背就能进大厂。但我的看法是八股文本身没错错的是只背不理解。网易这套卷子恰恰是在用题目设计来区分“背过”和“理解”的人。举一个很典型的例子卷子里有一道关于String的题问“String str new String(abc)创建了几个对象”。这道题在很多面试题库里都有答案是“一个或两个”。但网易的考法会更进一步——它会给你一段代码让你判断字符串常量池里的对象和堆里的对象是否相等以及intern()方法的作用。这些内容如果只是背答案换个场景就懵了。但如果你真的去看过String源码、理解过常量池的设计逻辑不管题目怎么变你都能顺着思路推出来。我经常跟学弟学妹说一句话把“为什么”想清楚比把“是什么”背下来重要一百倍。这套卷子的价值就在这里——它是很好的“为什么”训练材料。1.3 时间分配与答题策略网易笔试题量不算小我记得当时的选择题加编程题总共给了大约90分钟。很多人在选择题上纠结太久导致后面的编程题没时间写。这里给一个实战策略先扫一遍全卷把会做的、有把握的题先做掉把需要思考的题标记出来最后集中攻克。选择题里Java基础、集合、JVM这三块通常占大头而且答案相对确定如果复习到位基本可以秒选。网络和数据库的题目需要稍微算一下比如TCP三次握手的状态变化、B树的高度计算这些要动手推一遍才稳妥。最忌讳的是在一道不确定的题上死磕五分钟把后面编程题的时间搭进去。分值是一样的但编程题往往是一题顶好几道选择题。我当时的经验是选择题平均每题控制在60到90秒碰到需要画图推演的题比如红黑树旋转、B树分裂先跳过等所有题过完一遍再回头慢慢算。这样能保证你的基础分拿满编程题也有充足的时间写完整、写对。2. 核心知识点逐项拆解从基础语法到JVM底层2.1 Java基础运算符、标识符、枚举与Lambda这套卷子的Java基础部分可以说是“看着简单实则全是坑”。先说运算符和表达式很多人觉得这个没什么好考的但实际上网易很喜欢在自增自减、短路运算、位运算这些地方做文章。比如int i 0; i i i;这种代码最终i的值是多少我第一次做的时候想当然地填了2但实际上这里涉及到Java运算符优先级、自增自减的副作用执行时机以及表达式的求值顺序。正确答案是2吗不是是2。但如果你换成int j 0; j j j;结果就完全不同了。这种题考的不是你能不能算对而是你知不知道Java在表达式求值时会先取左操作数的值、再取右操作数的值、最后才执行自增。看字节码的话会更清楚——每个自增操作被拆成了几步独立的指令顺序不同结果就不同。标识符命名规则也是必考的但这道题更多是送分题。需要注意的坑点包括$和_是合法标识符字符数字不能开头不能是关键字或保留字比如goto、const虽然是保留字但没被使用也不能做标识符true、false、null是字面量不是关键字。另外很多人不知道var在Java 10之后成了保留类型名但在某些版本里做标识符会报编译错误。这种细节写代码时一般遇不到但笔试里一分就是一道题的差距。枚举和Lambda是网易的偏爱。枚举的考点集中在枚举是否可以继承类答案enum隐式继承java.lang.Enum不能显式继承其他类但可以实现接口枚举的构造函数默认是private的枚举的values()和valueOf()是编译器自动生成的方法枚举用在单例模式下能否保证线程安全和反序列化安全。这些如果只是看过概念很容易在“枚举是否可以用比较”这种题目上犹豫——答案是可以用因为枚举实例在JVM中是单例的equals()底层也是调用了。Lambda这边重点在于理解函数式接口、变量捕获effectively final、以及方法引用和Lambda表达式的等价转换。网易喜欢出一道代码题比如ListString list new ArrayList(); list.removeIf(s - s.length() 3);让你判断输出。这种题如果没接触过removeIf这个默认方法很容易选错。Lambda不只是一个语法糖它在字节码层面是通过invokedynamic指令实现的和匿名内部类有本质区别——匿名内部类会生成一个独立的class文件而Lambda是在运行时动态生成实现类。这个区别也解释了为什么Lambda内部引用的外部变量必须是 effectively final 的。2.2 集合框架HashMap的底层逻辑与并发陷阱集合这块HashMap是绝对的主角也是网易反复考察的对象。问法可能有千百种但核心就几个底层数据结构是什么、put和get的流程、哈希冲突怎么解决、为什么负载因子是0.75、扩容机制是什么、JDK 1.7和1.8有什么区别、并发环境下会出什么问题。先看底层结构。JDK 1.8的HashMap是数组加链表加红黑树的结构数组的每个位置叫桶bucket当链表长度超过8并且数组长度大于等于64时链表会转成红黑树。为什么阈值是8官方注释里有解释理想情况下随机哈希码的桶中节点分布遵循泊松分布当负载因子为0.75时链表长度达到8的概率是千万分之六。也就是说正常情况下几乎不可能出现链表长度到8的情况如果真的到了8说明哈希函数分布极差这时候用红黑树来兜底把查找时间从O(n)降到O(log n)。put流程可以概括为五步计算哈希、定位桶、遍历节点、插入节点、判断是否需要扩容。这里有个细节经常被忽略JDK 1.8之后HashMap的哈希函数是把key的hashCode高16位和低16位做异或运算。这样做的目的是让高16位也参与路由运算减少碰撞概率。因为大多数场景下key的hashCode的低位相同概率更高如果不做扰动容易出现很多key被分到同一个桶的情况。扩容是HashMap里最复杂的部分也是网易的必考点。默认初始容量是16负载因子是0.75所以当size超过12时就会触发扩容。扩容时数组长度翻倍所有元素重新计算桶位置。JDK 1.7的扩容会头插法在并发环境下可能形成环导致get的时候死循环JDK 1.8改成尾插法解决了死循环问题但在多线程下仍然存在数据丢失和覆盖问题。所以答案很明确并发场景下不要用HashMap用ConcurrentHashMap。我在实际项目里踩过一次ConcurrentHashMap的坑可以一并说一下ConcurrentHashMap的putIfAbsent和computeIfAbsent是有区别的前者在key存在时不会替换value后者在线程安全的前提下可以实现更复杂的“不存在就计算、存在就返回”的逻辑。但要注意computeIfAbsent里的计算函数可能被并发调用多次官方文档说不能保证只执行一次所以不要在计算函数里写有副作用的代码。2.3 JVM内存模型、OOM分析与GC机制JVM是网易特别喜欢深挖的模块同时也是很多人最发怵的部分。卷子里最常见的考法是给出一个OOM场景比如 “Java heap space” 或 “PermGen space/Bootstrap method space” 或 “unable to create new native thread”让你分析原因和排查思路。先说 “java.lang.OutOfMemoryError: Java heap space”这是堆内存不够了。可能原因有几个一是内存泄漏对象被错误引用无法回收最常见的是静态集合持有对象、IO流未关闭、监听器未移除二是堆太小JVM参数-Xmx设置过小三是存在大对象或一次性加载大量数据比如在循环里用StringBuilder拼接超长字符串、把整张表全量查出来放进内存。排查步骤通常是这样先用jstat -gcutil pid 1000观察GC频率和GC后内存占用如果GC后内存降不下来基本可以断定是泄漏然后jmap -dump:formatb,fileheap.bin pid导出堆快照再用MAT或VisualVM分析看哪些对象的 retain size 最大从线程栈里找到引用链定位到具体代码行。我当时遇到的真实场景是一个定时任务每次执行都把一份大文件读入内存做解析解析完存库后没有释放引用而且任务里用了static List来缓存解析结果结果不到两天堆就满了。排查过程走了一遍上面的流程最后在MAT里看到byte[]占用接近90%堆内存顺着GC root一追发现全部挂在那个static列表上。还有一个容易被忽略的OOM是 “unable to create new native thread”。这不是堆内存的问题而是操作系统线程资源耗尽。每个线程在JVM外要占用系统内存线程栈默认线程栈大小可能是1MB-Xss。当系统总内存被占满或者进程的线程数达到操作系统上限ulimit -u就会报这个错。排查方式是用jstack看线程数、用ps -eLf | grep java | wc -l统计线程总数、检查是否有人在代码里无脑new Thread而没有用线程池。GC机制这部分网易更倾向于考察CMS和G1的区别以及如何通过GC日志判断是否需要进行调优。核心考点是分代收集理论、Minor GC和Full GC的区别、stop-the-world的含义、GC Roots有哪些。有一个高频题目是“哪些对象可以作为GC Roots”答案是虚拟机栈中引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象。这道题背下来不难但理解很重要——GC Roots是垃圾回收的起点从这些点出发做可达性分析能到达的对象就是存活的不能到达的就可以回收。2.4 并发与多线程从synchronized到AQS网易的并发题集中在三个地方synchronized和ReentrantLock的区别、volatile的可见性与有序性、线程池的参数与拒绝策略。先说volatile。这是Java并发里最基础也最容易被问懵的关键字。volatile有两个语义保证可见性、禁止指令重排序。但它不保证原子性。经典面试题是“volatile能不能保证i的线程安全”答案是不能因为i是读-改-写三步操作volatile只保证读和写是原子的但三步之间可能被其他线程打断。要解决这个问题可以用AtomicInteger或者synchronized。另外volatile的底层实现是通过内存屏障——写操作会在后面插入StoreStore屏障和StoreLoad屏障读操作会插入LoadLoad屏障和LoadStore屏障这些屏障限制了处理器和编译器的重排序。synchronized和ReentrantLock的区别是必背题但我想强调一个进阶点JDK 1.6之后synchronized做了大量优化偏向锁、轻量级锁、重量级锁、锁消除、锁粗化性能已经不输ReentrantLock。所以“用ReentrantLock一定比synchronized好”这种说法已经过时了。区别更多的在于功能ReentrantLock支持超时、可中断、公平锁、多个条件队列而synchronized只能做到非公平锁和单一条件队列。如果你要写一个需要“等待超时自动放弃”的锁synchronized做不到必须用ReentrantLock的tryLock(timeout, unit)。线程池是实际项目里使用频率最高的并发组件也是网易特别爱考的部分。核心参数一共七个核心线程数、最大线程数、空闲存活时间、存活时间单位、任务队列、线程工厂、拒绝策略。这里有一个很多新手搞混的点线程池的执行流程是先提交任务判断核心线程是否满不满直接开新线程执行满了加入队列队列满了才创建非核心线程最大线程也满了才走拒绝策略。也就是说线程池并不是“核心线程满了就立刻扩容到最大线程”任务队列的角色常常被忽略。拒绝策略有四种AbortPolicy抛异常、CallerRunsPolicy调用者自己执行、DiscardPolicy静默丢弃、DiscardOldestPolicy丢弃最老任务。生产环境我推荐用CallerRunsPolicy它能把多余的负载压力传回调用方起到天然的背压效果避免任务无声无息地丢失。网易还喜欢问一个ThreadLocal相关的题ThreadLocal的内存泄漏是怎么发生的如何避免。这个要把ThreadLocalMap的Entry继承WeakReferenceThreadLocal这个设计讲清楚。简单说ThreadLocal的key是弱引用value是强引用。如果ThreadLocal对象被回收key会变成null但value还挂在线程的Map里线程一直存活的话value永远无法被GC回收这就形成了内存泄漏。最好的规避方式是每次用完ThreadLocal记得调用remove()或者在业务逻辑里用try-finally包裹确保最后一定移除。很多线上问题都是线程池里用ThreadLocal没清理导致任务之间的变量串了查起来非常诡异。2.5 网络与数据库TCP、索引、事务和锁网络这块网易考察集中在TCP和HTTP。TCP的必考点是三次握手和四次挥手以及各状态的含义。笔试里最常见的就是给你几个状态让你排序或者问“TIME_WAIT出现在哪一端为什么要等2MSL”。回答要点是主动关闭连接的一端在发送最后一个ACK后会进入TIME_WAIT停留2MSL最大报文段生存时间后关闭目的是确保最后一个ACK能到达对方如果丢失对方会重发FIN同时让本连接产生的所有旧报文段在网络中自然消失避免影响后续使用相同端口的新连接。HTTP方面网易在2018年那会儿已经开始考HTTP/2了。需要掌握SPDY、多路复用、头部压缩HPACK、服务端推送这几个核心特性。另外还有一个高频考点HTTP状态码的语义。301是永久重定向302是临时重定向304是未修改命中协商缓存401是未认证403是禁止访问404是资源不存在500是服务器内部错误502是网关错误上游没响应503是服务不可用过载或维护504是网关超时。如果问到你线上报警里常见的500和504分别如何处理前者通常是应用代码抛出异常没捕获后者通常是上游接口或数据库响应超时。数据库是另一大块。网易的题集中在索引、事务和锁。索引部分B树的优势需要答到点上树的高度低一般三到四层每次磁盘IO能读取更多数据叶子节点用链表连接适合范围查询所有数据都存在叶子节点查询次数固定性能稳定。另外要会计算B树的高度——假设InnoDB默认页大小是16KB主键是bigint占8字节加上指针占6字节一个页能存大约16KB/14B≈1170个索引项第二层同理第三层能存1170×1170×16KB/行的大小所以三层的B树能存储上千万甚至上亿条记录。这个计算过程在手撕题里很容易出现。聚簇索引和非聚簇索引的区别也是必考。InnoDB的主键索引就是聚簇索引叶子节点直接存整行数据二级索引非聚簇索引的叶子节点存的是主键值所以查询时会产生“回表”操作先查二级索引找到主键再回主键索引里查整行。如果要查的字段已经被二级索引覆盖就无需回表这就是“覆盖索引优化”。事务和锁那边核心是ACID和隔离级别。四种隔离级别读未提交、读已提交、可重复读、串行化的差别要讲清楚特别是InnoDB默认的可重复读以及它如何通过MVCC解决快照读的幻读。但要注意可重复读下如果使用当前读SELECT ... FOR UPDATE还是可能产生幻读的InnoDB用间隙锁gap lock和临键锁next-key lock来避免这个问题。需要记一个点间隙锁锁住的不是具体记录而是索引记录之间的间隙使用前提是隔离级别至少要可重复读并且索引要存在。2.6 Spring核心机制与设计模式Spring部分网易比较在意你对IoC容器和AOP的理解深度而不只是会背“控制反转就是对象创建交给容器管理”这种一句话定义。IoC部分建议从Bean的生命周期讲起这是最能体现功底的地方。完整生命周期我大概能列出十几步扫描BeanDefinition、推断构造方法、实例化、属性填充依赖注入、初始化前BeanPostProcessor的postProcessBeforeInitialization、初始化InitializingBean或PostConstruct、初始化后postProcessAfterInitialization——到这里一个Bean就能用了之后是使用阶段最后容器关闭时执行销毁DisposableBean或PreDestroy。网易如果深入考会让你回答“构造函数里面能不能调用被代理的Bean的方法”答案是不能因为代理对象是在初始化后阶段生成的构造和属性填充时还在原始对象。AOP的核心是动态代理——Spring默认对接口使用JDK动态代理对类使用CGLIB代理。自Spring Boot 2.x开始spring.aop.proxy-target-class默认是true也就是默认全部用CGLIB。这里有个潜在的坑JDK动态代理要求目标类必须实现接口生成的代理类和目标类是兄弟关系不是子类关系CGLIB是通过继承目标类生成子类来实现代理所以目标类不能是final的方法也不能是final的。如果你在写拦截器或切面时发现目标方法没被拦截先检查一下类是不是final、方法是不是private或final、有没有加上public修饰。设计模式这块网易通常不直接问“请列举23种设计模式”而是给一个业务场景让你选择合适的设计模式并说明理由。比如“系统中需要根据不同类型创建不同算法对象后续新增类型不需要修改已有代码”这个描述在引导你答策略模式加简单工厂或者策略模式加Spring的ApplicationContext。另一个常见的是“订单状态流转不同状态下行为不同”——这是状态模式“用统一接口屏蔽多种日志实现”——这是门面模式或适配器模式。我建议重点掌握这几个单例、工厂、策略、模板方法、观察者、代理、责任链、建造者。理由是这些在Java生态里出现频率最高而且容易结合Spring源码来答。3. 真题实战推演从审题到作答的完整过程3.1 选择题中的易错点与排除法选择题是笔试的大头也是很多人的失分重灾区。我发现网易选择题的干扰项设计特别有讲究它不会放明显错误的选项而是把“看着对但细节不对”的选项混进来。所以做题时一定要养成一个习惯看到一个选项先判断它哪句话说绝对了、哪句话偷换了概念、哪句话场景不成立。举例来说“下列关于HashMap的说法正确的是”这道题有四个选项A. HashMap允许key为nullHashtable也允许key为nullB. HashMap默认初始容量是16负载因子是0.75C. JDK 1.8中HashMap的所有操作都是线程安全的D. HashMap扩容时所有元素都会重新计算hashCode正确选项是B。A的错误在Hashtable不允许null keyC错在HashMap任何版本都不是线程安全的D错在扩容时元素重新计算的是桶位置(n - 1) hash而不是重新计算hashCode。这类题的关键是对每个选项做到精准定位知道它错在哪一行代码、哪一个单词。模糊记忆在选择题里非常吃亏。另一个高频点是“下列哪些做法可以避免产生内存泄漏”常见选项包括不再使用的集合及时置null、IO流使用后关闭、监听器在不需要时移除、ThreadLocal使用后调用remove。如果你漏选了ThreadLocal那项说明对ThreadLocal的内存泄漏理解还停留在概念层面没有落到实践。3.2 编程题从解题思路到代码实现网易的编程题通常是一道算法题和一道场景设计题。算法题的难度不会太高一般落在链表、二叉树、动态规划、字符串处理这个范围。但它的判分标准比较严格不仅要跑通测试用例还会看时间复杂度和空间复杂度。所以即使你能用暴力法解出来也要尽量优化。我拿一道当年比较典型的题目举例给定一个整数数组找出其中两个数使它们的和等于给定目标值返回这两个数的下标。这道题最直接的解法是双重循环时间复杂度O(n²)——笔试时大概率过不了大用例。优化的思路是用HashMap存储“当前值对应的下标”遍历一次每次检查target - current是否已存在于Map中如果存在就直接返回否则把当前值放进Map。时间复杂度O(n)空间复杂度O(n)。这种“用空间换时间”的思路是算法题里最基础也最重要的套路。场景设计题则更贴近工程实践。我记得有一题大概是设计一个任务调度器支持定时任务和执行结果的记录要求考虑并发和异常恢复。这种题没有标准答案但阅卷老师会看你有没有考虑到线程池参数怎么设置、任务超时怎么处理、任务失败怎么重试、是否持久化任务状态、重启后未完成的任务怎么恢复。当时我的作答思路是用ScheduledExecutorService实现定时调度任务状态用数据库记录PENDING/RUNNING/SUCCESS/FAILED每次任务开始前将状态置为RUNNING结束之后更新状态如果进程重启启动时扫描所有RUNNING状态的任务全部重置为PENDING并重新调度。这套方案不复杂但能覆盖大多数边界情况。3.3 手撕代码的规范与细节笔试的手撕代码环节很多人算法想明白了却因为细节丢分。这里分享几个血泪教训第一变量命名要清晰。我之前见过有人写算法题变量名全是a、b、c逻辑完全看不懂这种代码即使AC了面试官印象也会打折扣。最好用有语义的变量名比如left、right、current、pre让代码像散文一样易读。第二边界条件要提前处理。比如链表题里的空链表、单节点链表数组题里的空数组、长度为1的数组二分查找里的left right退出条件。大多数算法题的隐藏测试用例就是这些边界。第三如果不确定某种写法是否越界宁可用更保守的方式。比如数组下标计算先判断下标是否在[0, len-1]范围内再访问或者用Math.max/min做钳制。多写两行不丢分越界一次可能就是直接0分。4. 真题之外这套卷子延伸出的知识体系与避坑指南4.1 从一道错题延伸出的知识漏洞清单做完这套卷子后我非常推荐你做一个动作把每道错题背后对应的知识点列成一张表逐项打勾确认自己是否真正掌握。这是我当时自己整理过的一个最小知识清单放在这里供参考模块必会知识点自查标准Java基础字符串常量池与intern()能画出执行new String(a) new String(b)时堆和常量池的对象分布Java基础枚举与单例能解释为什么枚举单例天然线程安全且能防止反射破坏集合HashMap的put流程能说出JDK 1.8中从计算hash到插入节点的每一步以及何时触发树化集合ConcurrentHashMap的分段/散列机制能解释JDK 1.7的分段锁和JDK 1.8的CASsynchronized区别JVM类加载过程能按顺序说出加载、验证、准备、解析、初始化五个阶段并举出每个阶段的例子JVMGC Roots能说出四类GC Roots并能结合MAT分析一个实际堆转储并发synchronized的锁升级过程能说明偏向锁到轻量级锁到重量级锁的触发条件和Mark Word变化并发线程池任务执行流程图能手绘出核心线程、队列、最大线程、拒绝策略的执行顺序网络TCP挥手状态机能画出主动关闭方和被关闭方的状态流转并解释TIME_WAIT的作用数据库InnoDB索引结构能通过行大小和页大小计算三层B树能存多少行数据SpringBean生命周期能说出Bean从定义到销毁的完整流程并标出AOP代理是在哪个阶段产生的设计模式策略工厂组合能给出一个去掉if-else的Spring实现方案如果你发现自己某一项卡住了建议回到源码层面去看不要只看二手博客。比如HashMap直接打开JDK源码的putVal方法逐行读一遍比如线程池直接看ThreadPoolExecutor.execute的源码把workerCountOf(c)、workQueue.offer、addWorker这几个关键分支理清楚。源码是唯一的“标准答案”其他任何文章都可能有过时或错误的信息。4.2 我复盘这套卷子时的三个重要发现第一网易的题目设置非常功利什么常用考什么。它不是随机抽知识点而是从真实业务中提取高频场景。所以如果你时间有限优先复习HashMap、线程池、Spring Bean生命周期、MySQL索引、TCP连接管理。这五个模块覆盖了这套卷子里几乎所有中等以上难度的题目。第二历年真题的重复率极高但考察角度会变。同一道“HashMap和Hashtable区别”2016年可能只是背诵题到2018年就变成了“并发场景下HashMap出了什么问题你如何解决”。所以你看旧题不要只看答案要看它在那一年的问法和考察深度往前推几年你基本能猜到今年会怎么问。第三Java版本迭代对考点影响巨大。2018年那会儿JDK 8还是绝对的主流Lambda和Stream是加分项。放到现在JDK 11、17、21都普及了面试官还会追问record、sealed class、虚拟线程。所以做这套旧题时建议顺手把JDK 8之后的新特性也过一遍别把知识停留在2018年。4.3 避坑指南最常见的五个实战错误这套卷子做下来我总结出五个特别容易踩的坑每一个都是我亲眼见过别人踩的也可能你也正在踩。第一个坑是用Hashtable和Vector当并发集合。很多教材还在拿“Hashtable是线程安全的”当考点导致新手误以为并发场景用Hashtable是对的。实际上Hashtable的做法是对每个方法加synchronized锁锁粒度太粗并发性能极差。正确做法是用ConcurrentHashMap。同理Vector也应该用CopyOnWriteArrayList或Collections.synchronizedList替代。笔试选“最佳做法”的时候“线程安全”并不等于“最佳”。第二个坑是混淆和equals的适用场景。笔试题里经常给一串字符串比较的代码让你判断输出。核心规则很简单比较的是引用地址equals比较的是内容。但结合字符串常量池后事情就变得复杂了——String s1 a b和String s2 new String(ab)的地址不一样s1.intern()和s2.intern()才是同一个引用。这类题死记硬背很容易错最优解是画图把堆里的对象、常量池里的对象、Stack上的引用都画出来一步都不会错。第三个坑是动态SQL或MyBatis里错误使用foreach导致SQL性能问题。这虽然不是笔试题但面试官会在项目经验环节连环追问如果用户在前端勾选了1000个id你直接WHERE id IN (...)会怎么样答案是大批量IN查询可能导致索引失效、SQL解析时间变长、数据库CPU飙升。正确做法是限制单批数量比如500个一批分多次查询后合并结果或用临时表处理。这个经验能帮你应对项目深挖环节。第四个坑是线程池的队列选型错误。很多人以为newFixedThreadPool就是万能的但LinkedBlockingQueue默认是无界队列任务堆积可能导致内存OOM。笔试如果问“线程池如何设置队列”你要能区分有界队列ArrayBlockingQueue适合需要背压的场景无界队列LinkedBlockingQueue适合任务量可控的场景SynchronousQueue不缓存任务直接传给线程处理适合“来一个处理一个”的场景。选型没有标准答案但你要说出理由和代价。第五个坑是忽视JVM参数对程序行为的影响。有些网易笔试题会给一段代码问“程序能不能正常运行、会不会OOM”。这时候你必须考虑-Xmx、-Xss这些参数的默认值。比如递归调用深度超过默认栈深度就会抛StackOverflowError——StackOverflowError是Error不是Exception所以用try-catch Exception是接不住的。类似的OOM也是Error不是Exception。如果不能区分Error和Exception的区别这种题很容易答错。5. 资源与准备工作如何高效备考Java校招笔试5.1 我建议的复习顺序与资料组合如果你距离笔试还有三到六个月我的建议是分四个阶段来准备不要一上来就刷题那样效率很低而且很容易陷入“背了忘、忘了背”的循环。第一阶段第1-4周系统性过一遍Java基础。以《Java核心技术卷I》为主配合JDK源码阅读。重点是集合、泛型、异常、IO、并发这几个大模块。每天至少写20行代码验证书上的结论不要只看不练。第二阶段第5-8周深入JVM和并发。看《深入理解Java虚拟机》重点看内存模型、垃圾收集器、类加载机制这三章。并发部分看《Java并发编程的艺术》或者更薄一点的《实战Java高并发程序设计》。这个阶段的输出是笔记每学完一个主题用自己的话写一篇能讲给别人的总结。第三阶段第9-12周刷题和项目复盘。开始做历年的校招真题每周两套。每套卷子做完后用我上面提到的“知识漏洞清单”方式整理错题找出反复出错的知识点回炉重造。同时把你自己做过的项目拿出来按照“技术难点-解决方案-最终效果”的结构写清楚这是面试环节的核心素材。第四阶段冲刺两周回顾错题、手写代码、模拟面试。找朋友或前辈帮你模拟至少三轮面试每次一小时。模拟面试的收获比刷十套题都大因为你会在高压下发现自己真正薄弱的地方。5.2 编程环境与调试工具的准备笔试一般是在牛客网或赛码网这类在线平台上进行有的支持本地IDE编译。这里有几个细节要提前确认平台支不支持Java 11或17的语法特性默认的内存限制和超时时间是多少输出格式是否严格要求比如最后一个字符不能有多余空格。这些细节看着小但非常影响发挥。我印象里有一次线上笔试因为没搞清平台是“每行输出一个结果”还是“等间隔空格分隔”白白丢了一道简单题的分。所以在笔试前一天我强烈建议你找目标平台的模拟题完整跑一遍输入输出流程适应它的编码环境和提交方式。调试方面自己刷题时可以用本地IDE加JUnit写单元测试把边界用例都测一遍比如空输入、极小值、极大值、重复元素、顺序颠倒。这些测试用例在线上笔试时往往就是隐藏用例测过一遍心里就有底。5.3 从校招笔试到技术面试的延伸准备笔试只是第一关后面还有两到三轮技术面试。网易的技术面试风格和笔试是一脉相承的少问理论多问实践喜欢拿你写的代码和项目经历追问到底。所以笔试结束后不要松懈趁热打铁把卷面中暴露的薄弱点补上很可能面试官就直接从你笔试的某道题切入问你能不能现场优化。举个例子如果你笔试时写了一道“两个大数相加”的题面试官可能会接着问大数用什么类型存储如果超出long的范围怎么办用字符串模拟加法的复杂度是多少能不能用并行计算优化每道递进问题都在考察你的知识广度和思维深度答得好就是完美的“加分链”。我个人还有一个习惯每次笔试完不管结果如何都会把卷子里所有题目的知识点整理成一篇复盘笔记写清楚“这题考什么、我为什么错、正确思路是什么、能延伸到哪些项目场景”。三年下来我积累了上百篇这样的笔记后来跳槽时整理成了一份自己的知识图谱面试前翻一遍几乎覆盖了90%的考点。这个方法也分享给你坚持下来你的成长速度会快得肉眼可见。最后再分享一个小技巧做这套2018年的卷子时可以主动去搜一搜当年网易Java工程师岗位的JD描述。你会发现笔试的考点和JD里写的“熟悉集合、多线程、JVM、Spring、MySQL”高度重合。也就是说笔试题不是凭空出的而是从岗位技能树里抽的。反过来你也可以用这个逻辑去推测任何一家公司的笔试重点——找到它们的JD画出技能树再有针对性地复习远比漫无目的地刷题高效。这套卷子到今天依然有它的价值哪怕你已经不是应届生用它来做自测和复盘也能清楚地看到自己的技术短板在哪。Java这个领域知识更新快、框架迭代快但底层的东西其实没怎么变过。把地基打牢后面的路会好走很多。