
网易2018校招Java开发工程师笔试卷到现在依然值得拿出来细看。原因很简单它是我印象里当年最典型的“大而全”校招卷一套题几乎把所有Java后端笔试的常规考点都覆盖了。我当年备考时把能找到的回忆版来回刷了好几遍后来带新人时也经常拿它当复习提纲。现在回头重新拆这套题其实可以很清楚地看到命题组到底在筛什么样的人——不是背书机器而是真正理解Java运行时行为、有算法基本功、还能把计算机基础串起来的人。1. 整卷画像网易这套笔试题到底在筛选什么人1.1 题量和时间设计背后的意图先看试卷结构。这套题给我最直观的感受是题量不小但每道题的单题分值并不均匀明显有“基础送分题—进阶拉开题—算法压轴题”三个梯度。这种设计在校招笔试里非常常见目的是在两个小时左右的考试时间里快速把候选人分成几个层次第一层Java基础语法和集合框架都还没掌握扎实的人这类题做不对基本没机会进面试。第二层基础还行但多线程、JVM、网络细节一问就模糊的人这类题是主要的筛人区域。第三层技术知识面完整同时现场写算法够快够准的人这类人往往是面试官愿意继续聊下去的。很多同学拿到卷子习惯从头做到尾这是校招笔试的大忌。我更建议先花一分钟扫一遍全卷把题目按“秒杀题—思考题—放弃题”分个类。秒杀题就是看到答案立刻能落笔的思考题是需要草稿纸推导的放弃题是看了三遍还没思路的。先把秒杀题全部拿下再集中火力做思考题放弃题最后用排除法或常识推断蒙一个不要空着。这套卷里的题目分布恰恰适合这种策略。1.2 考点模块的权重分配综合多个版本的考生回忆这套网易2018校招Java卷的考点模块大体呈现这样的权重考点模块大概占比典型考题方向Java语法与面向对象20%-25%String、包装类、异常、继承多态集合框架与泛型10%-15%HashMap原理、ArrayList与LinkedList多线程与并发10%-15%synchronized、线程池、并发容器JVM与内存模型10%左右内存区域、GC、类加载数据结构与算法25%-30%链表操作、二叉树、排序、动态规划计算机网络5%-10%TCP握手、HTTP状态码操作系统与数据库10%左右进程线程、事务、索引注意算法和Java基础加起来占了半壁江山。这其实反映出一个很重要的行业信号对于校招Java开发工程师公司默认你的Java知识可以通过入职后培训补上但数据结构和算法的基本功短期很难速成所以笔试必须重点卡。这个观点放到今天依然成立。1.3 和当下Java面试八股文的关系现在大家常说“Java八股文”其实网易这套2018年卷就是早期八股文考法的典型代表。String、HashMap、线程池、JVM这些考点至今仍是Java面试的高频题只是问法更刁钻了。比如当年可能只考“HashMap底层结构是什么”现在会追问“为什么红黑树阈值是8而不是10”“链表转树之前为什么先比较容量”。所以我一直认为把一套经典旧卷做透比盲目刷几十套新卷都管用因为考点内核没变变的只是包装方式。2. Java语言基础题的常见陷阱从源码层面看命题人想考什么2.1 String、包装类与和equals的经典纠缠这套卷的基础题部分几乎绕不开String和包装类的比较问题。别看题目简单正确率其实不高。核心要搞清楚三件事。第一比较的是引用地址equals比较的是内容前提是类重写了equals。String类重写了equals所以new String(abc).equals(abc)为true但用比较为false因为一边是堆上新对象一边是常量池里的对象。第二String在JVM里有字符串常量池机制。直接赋值String s1 abc会先去常量池找有没有“abc”有就复用没有就创建。而String s2 new String(abc)除了常量池里的“abc”还会在堆上创建一个新的String对象。所以abc s2是false。第三包装类的缓存池。比如Integer a 127; Integer b 127; System.out.println(a b); // true Integer c 128; Integer d 128; System.out.println(c d); // false为什么因为Integer默认缓存了-128到127之间的对象在这个范围内自动装箱时直接复用缓存对象超过范围就new新对象。Boolean、Byte、Short、Long的缓存范围不同Character是0到127面试时经常混着考。真题里常见的变化是把它套进一个方法里比如public static void change(String s) { s world; }问调用后原字符串变不变。答案是不变。因为Java参数传递是值传递形参s只是实参引用的一个拷贝在方法里让s指向新的字符串并不会影响外层的引用。很多人把String当成特殊“引用传递”其实String和普通对象在参数传递上没区别只是String不可变才会产生这种误解。2.2 集合框架中最容易被追问的HashMap细节HashMap几乎是每套Java卷的必考题。2018年这套卷里HashMap相关题目的难度已经不低会直接考到JDK 8的改进点和并发场景下的行为。复习时至少要能回答这几个问题HashMap的底层结构是什么JDK 8之后是数组加链表链表长度超过阈值8且数组长度达到64时链表转红黑树。为什么负载因子是0.75这是时间复杂度和空间占用之间的折中。负载因子越大空间利用率越高但哈希冲突概率增大越小则相反。0.75是官方在大量测试基础上选出来的平衡值。扩容机制是怎样的当元素个数超过容量乘负载因子时容量扩大为原来的两倍并重新计算每个元素的桶位置。这也是为什么容量设计成2的幂方便用位运算(n - 1) hash来替代取模运算。HashMap为什么线程不安全多线程扩容时可能出现循环链表导致死循环JDK 7JDK 8虽然通过头插改尾插解决了死循环问题但依然存在数据覆盖等并发问题。这类题目真正的难点不在背结论而是能否解释“为什么”。比如问你“为什么转红黑树的阈值是8”这就涉及到泊松分布。在负载因子0.75、随机哈希函数下一个桶里链表节点个数达到8的概率极低约为千万分之六。选8既保证红黑树不会频繁退化又保证哈希函数足够均匀时链表性能也可接受。2.3 异常与finally的执行顺序异常处理也是这套卷的高频考点而且非常考验细节。最常见的一道题是public static int test() { try { return 1; } finally { return 2; } }问返回值是什么。答案是2。因为return 1先计算好结果准备返回但在真正返回之前要执行finally块而finally里直接来了个return这会覆盖try里的return直接导致方法返回2。另一个更隐蔽的版本public static int test() { int x 1; try { return x; } finally { x 2; } }问返回值是什么。答案是1。注意这里finally里只是修改变量x的值并没有return。JVM在执行try里的return x时已经把x的当前值1保存到局部变量表的临时槽里了finally对x的修改不影响这个已保存的值。所以返回的还是1。但如果换成StringBuilder这样的引用类型finally里修改的是对象内容返回值就会被影响因为返回的是同一个引用。这种细微区别就是笔试拉开分差的点。2.4 泛型和反射的边界条件泛型题在2018年的卷子里出现频率不低核心考点是类型擦除。比如问ListString和ListInteger在运行时是不是同一个类型答案是同一个因为泛型信息在编译后被擦除了运行时都是裸List。由此衍生出来的经典面试题是通过反射往ListString里塞一个Integer能不能成功可以。因为运行时泛型信息不存在反射只看到ArrayList往里面add任何Object都不受限制。编译时泛型检查只是为了约束程序员不犯错JVM层面并不认识泛型。这类题需要动手验证我建议备考时自己写几行反射代码跑一遍印象比背书深得多。3. 多线程与JVM那份卷里“拉开差距”的部分3.1 并发安全的底层逻辑从synchronized到volatile多线程部分网易2018年这套卷至少有一道题是围绕synchronized展开的。常见问法包括synchronized修饰实例方法和修饰静态方法有什么区别synchronized底层是怎么实现的volatile和synchronized有什么区别先理清锁对象这个概念。synchronized修饰实例方法锁的是当前实例this修饰静态方法锁的是当前类的Class对象修饰代码块锁的是括号里指定的对象。三种锁对象不同竞争的范围就不同这是选择题最爱挖的坑。volatile则是另一个高频考点。它是JVM提供的轻量级同步机制核心能力有两个可见性对volatile变量的写操作会强制刷新到主内存并使其他线程的本地缓存失效。有序性通过内存屏障禁止指令重排序。但volatile不保证原子性。经典例子就是多线程执行count即便count声明为volatile最终结果依然可能小于预期因为count在字节码层面是三步操作读、加一、写三步之间可能被其他线程打断。所以volatile适合做状态标记、单例模式中的double-check不适合做计数器。JUC包里的并发工具也是常客。ConcurrentHashMap在JDK 8后放弃了分段锁改用CAS加synchronized只锁链表或红黑树的头节点细粒度更高。ThreadLocal则是每个线程一份变量副本但线上必须注意remove否则ThreadLocalMap里的Entry的key是弱引用value是强引用线程池复用线程时容易造成内存泄漏。3.2 线程池参数一道题考完所有核心概念线程池在笔试里的地位这几年越来越高因为一道线程池题可以同时考察队列、拒绝策略、执行流程、参数含义性价比极高。创建一个ThreadPoolExecutor需要七个参数corePoolSize核心线程数。即使线程空闲只要没超过这个数线程也不会被回收。maximumPoolSize最大线程数。线程不够用时最多能扩到这么大。keepAliveTime非核心线程空闲存活时间。workQueue任务等待队列。threadFactory创建线程的工厂一般用来给线程起有意义的名字。handler拒绝策略。队列满且线程数达到最大时触发。执行流程是这样的提交任务时如果当前线程数小于corePoolSize直接创建核心线程执行任务。如果核心线程已满新任务进入workQueue排队。如果队列也满了且线程数小于maximumPoolSize创建非核心线程执行任务。如果线程数已达最大值且队列满触发拒绝策略。四种拒绝策略分别是AbortPolicy抛异常、CallerRunsPolicy调用者线程执行、DiscardPolicy丢弃、DiscardOldestPolicy丢弃最老的未处理任务。选择题经常把策略和效果混在一起让人选记的时候抓关键词就好。这里有一个坑要提醒队列类型不同任务的执行顺序完全不同。如果使用LinkedBlockingQueue默认无界队列maximumPoolSize和拒绝策略基本失效因为队列永远不会满。如果使用SynchronousQueue它不存任务任务直接交给线程线程不够就立刻扩到maximumPoolSize。这些差异笔试考的是理解面试考的是场景选型。3.3 JVM内存区域与GC判定JVM相关题目在这套卷里主要考运行时数据区和垃圾回收。运行时数据区要能画出来并区分线程共享与线程私有线程私有程序计数器、虚拟机栈、本地方法栈。线程共享堆、方法区JDK 8后由元空间实现不再使用堆内永久代。一道经典送分题是哪个区域不可能抛出OutOfMemoryError答案是程序计数器因为它是唯一一个没有OOM的区域。反过来可能会出现java.lang.OutOfMemoryError: insufficient memory的通常是堆或元空间这类线上问题在排障时一定要先分清是堆内存不够还是通过JNI调用本地内存被耗尽。GC部分需要掌握两个怎么判断怎么判断对象可回收主流JVM用可达性分析从GC Roots出发不可达的对象判定为可回收。GC Roots包括虚拟机栈引用的对象、静态属性引用的对象、常量引用的对象、本地方法栈引用的对象等。怎么判断需要Full GC不同收集器判断方式不同但核心都是老年代空间不足。常见的System.gc()调用会触发Full GC但只是建议不保证立即执行。Java四大引用类型也值得花时间理一遍强引用对象可达就不回收、软引用内存不足时回收适合缓存、弱引用下次GC就回收、虚引用主要用来跟踪对象被回收管理直接内存。这四者优先级从强到弱GC回收力度从弱到强。4. 数据结构与算法题笔试真正的分水岭4.1 必考的数据结构操作链表、二叉树、栈与队列校招笔试的算法题大厂普遍不考太偏的竞赛题更多是经典数据结构操作题。网易这套卷里出现的算法题几乎都是这类。链表题第一个必练的是反转链表。迭代版思路是三个指针pre、cur、next每次把cur.next指向前一个节点然后整体后移。递归版则是先反转后面的链表再把当前节点的next的next指向当前节点。我建议两种都练熟因为面试官喜欢让人现场写递归版。第二个是判断链表是否有环。快慢指针法快指针每次走两步慢指针每次走一步如果有环两者必相遇。进一步的问题是找环的入口相遇后一个指针从头开始另一个留在相遇点都每次走一步再次相遇点就是环入口。这个结论背后是数学推导笔试里能直接记住结论用就好。二叉树必考的是遍历。层序遍历要用队列前中后序遍历用递归非常简单但非递归版本需要自己用栈模拟尤其是后序遍历的非递归实现最容易被问。这里有个我自己的经验把非递归遍历的代码模板抄下来考前一周每天默写一遍考场上基本不会慌。栈和队列的结合题也很常见比如用两个栈实现队列、用队列实现栈。前者思路是入队直接入push栈出队时如果pop栈为空把push栈全部倒过来再弹出。后者则需要通过队尾元素绕到队首来实现栈顶。4.2 排序与查找不只是背代码排序算法在笔试里通常不会直接让你手写快排但会考复杂度、稳定性、适用场景。有一个点很多同学会混淆快速排序的平均时间复杂度是O(nlogn)最坏是O(n²)归并排序时间复杂度稳定O(nlogn)但空间复杂度是O(n)堆排序时间复杂度O(nlogn)且是原地排序但跳跃访问导致实际运行不如快排快。稳定性也要记清楚稳定的排序有插入、冒泡、归并不稳定的有选择、快排、堆排。冒泡排序在Java的Arrays.sort里对于小数组的优化TimSort等混合排序在JDK源码中的使用这些也会作为拓展考点出现。查找类算法考得最多的是二分查找常见变形包括查找第一个等于目标值的位置、最后一个等于目标值的位置、找旋转数组的最小值。核心是理解left和right的更新条件不要死记模板。我当年练二分的时候反复在纸上画区间把每个边界情况跑一遍后来遇到变种题也能临场推出正确的边界。4.3 动态规划题的破题思路如果这套卷的算法部分有压轴题很大概率是动态规划。常见的背包、爬楼梯、最大连续子序列和、编辑距离都值得刷。以一个普通的跳台阶问题为例一只青蛙一次可以跳1级或2级台阶问跳上n级台阶有多少种跳法。这个问题的本质是斐波那契数列的变体。设f(n)表示跳上n级台阶的跳法数那么最后一跳只可能是从第n-1级跳1级上来或者从第n-2级跳2级上来所以状态转移方程为f(n) f(n-1) f(n-2)初始条件f(1)1f(2)2。这个题看似简单但能引出很多延伸如果一次可以跳任意级呢如果某级台阶是坏的不能踩呢笔试里大概率会考变体所以一定要掌握从“最后一步”出发思考的套路而不是背公式。再比如最大连续子序列和问题。核心状态是dp[i]表示以第i个元素结尾的子数组的最大和。转移方程就两句话如果dp[i-1]大于0就把当前元素加进来否则从当前元素重新开始。用滚动变量代替dp数组可以做到O(1)空间复杂度。这类题目的通用解题思路是三步定义状态想清楚dp[i]代表什么。找状态转移方程思考“当前状态可以由哪些更小的状态推导出来”。确定初始值最后从所有dp值里取最优解。熟练之后大部分笔试DP题都能在十分钟内写出框架。4.4 手写算法题的练习路线备考算法部分我的建议是按专题刷题而不是按题号刷题。先花两周把链表、二叉树、二分、排序、DP这五个专题的核心题各刷十道左右再开始刷整套试卷。刷的时候不要直接看题解先自己想五分钟能写多少写多少然后再对着题解找差距。这样留在脑子里的不是答案而是解题路径。另一个实用技巧是笔试环境往往只有简单的编辑器没有IDE提示更没有代码补全。平时练习就尽量用记事本或LeetCode网页版白板写代码强迫自己把常用API都背下来。很多人在考场上因为不懂Collections.sort的Comparator写法浪费了大量时间这类基础代码必须形成肌肉记忆。5. 网络、操作系统与数据库非Java但必考的底盘知识5.1 TCP和HTTP的高频设问方式计算机网络在Java后端笔试里是稳定的送分模块也是很多人复习时容易轻视的模块因为题目看起来“会”但一做就错。TCP的三次握手和四次挥手几乎是必考。三次握手解决的核心问题是确认双方的收发能力都正常。第一次客户端发SYN服务端知道客户端发信能力正常第二次服务端回SYNACK客户端确认自己发信正常且服务端收发正常第三次客户端回ACK服务端确认客户端收信正常。这个逻辑要能用自己的话讲清楚而不是背口号。四次挥手比三次握手更难理解核心点是TIME_WAIT状态。主动关闭方在收到对方的FIN并回复ACK后要等2MSL才进入CLOSED状态主要目的是防止最后一次ACK丢失导致对方重发FIN同时让旧连接的报文在网络中自然消亡不至于干扰新连接。HTTP部分常见的是状态码题。301是永久重定向302是临时重定向304是未修改可用缓存400是客户端请求错误401是未认证403是禁止访问404是找不到资源500是服务器内部错误502是网关错误503是服务不可用。网易这套卷里通常会给一个实际场景让你选择正确的状态码。5.2 进程线程与内存管理的易混点操作系统模块进程与线程的区别是基础中的基础。进程是资源分配的基本单位线程是CPU调度的基本单位。进程拥有独立的地址空间一个进程崩溃一般不影响其他进程同一个进程里的线程共享地址空间一个线程崩溃往往导致整个进程退出。死锁四个必要条件要记牢互斥、持有并等待、不可剥夺、循环等待。由此引出的死锁避免策略包括破坏这四个条件其中之一。常见的银行家算法属于避免死锁而不是预防死锁这个名词辨析在选择题里经常设坑。虚拟内存的核心思想是让每个进程觉得自己拥有连续完整的内存空间通过页表映射到物理内存上。缺页中断、页面置换算法LRU、FIFO、Clock也是可能出现的考点。我备考时有一个心得把操作系统当成“计算机资源调度故事”来理解进程是故事主角内存是舞台虚拟内存就是给主角画的一个大饼这样类比着记就不容易忘。5.3 SQL与索引优化的常见考查角度数据库方面题型相对固定主要看SQL书写能力、事务隔离级别、索引原理三块。SQL题一般会给两张表要求查出某个条件下的数据重点考察GROUP BY配合HAVING的用法以及各种JOIN的区别。INNER JOIN只返回两表匹配的行LEFT JOIN返回左表全部加右表匹配部分RIGHT JOIN相反FULL OUTER JOIN返回两表全部没有匹配的地方补NULL。笔试里最容易错的是漏掉LEFT JOIN后对右表字段的NULL判断一旦在WHERE里写了右表字段等于某值的条件左连接就退化成了内连接。事务隔离级别有四档读未提交Read Uncommitted可能脏读、不可重复读、幻读。读已提交Read Committed解决脏读但可能不可重复读和幻读。可重复读Repeatable Read解决脏读和不可重复读但可能幻读。MySQL默认级别。串行化Serializable全部解决但并发性能极差。MySQL InnoDB在可重复读级别下通过MVCC解决快照读的幻读问题通过间隙锁解决当前读的幻读问题这是面试常追的深度。索引部分核心是理解为什么InnoDB用B树而不是B树或红黑树。B树只有叶子节点存数据非叶子节点可以存更多索引项树更矮IO次数更少同时叶子节点之间通过链表相连对范围查询非常友好。联合索引遵循最左前缀原则比如建立索引(a, b, c)查询条件包含a或者a、b或者a、b、c时才能用到索引直接查b或c都走不了。6. 从2018到校招面试我的复盘方法与避坑经验6.1 如何用这套卷反推复习优先级很多人刷笔试真题只是对答案改完就扔这是最大的浪费。我的复盘方法是每做错一道题不只看正确答案而是反问自己三个问题——这道题考的是哪个知识点我为什么做错是知识盲区还是审题失误这个知识点在知识体系里还能延伸到哪些相关考点比如你做错了HashMap并发问题那要复习的不只是HashMap源码还应该顺手把ConcurrentHashMap、Hashtable、CopyOnWriteArrayList都过一遍。因为面试时面试官不会只问你错的那道题他会顺着这道题一直往外问。建议做一张错题表字段包括考点模块、题目简述、错误原因、关联知识点、同类题是否还错。每周翻一遍这张表直到所有错误原因都能不看答案说清楚。这个习惯我从校招一直保持到现在带团队面试别人时也会用类似方法给候选人复盘。6.2 笔试实战中的时间分配建议笔试的时间分配直接影响成绩。以这套2018年卷为例假设总时间120分钟我会这样分配前5分钟快速浏览全卷给题目打标记分清秒杀题、思考题、放弃题。中间80分钟先做秒杀题再做Java基础和多线程相关题这些题分值高、耗时短。后续30分钟集中攻克算法题。先写暴力解拿部分分再优化。最后5分钟检查填涂有没有错位确认代码题没有明显编译错误随机补空题。这里特别强调算法题不要一上来就追求最优解。如果你在考场上五分钟内没想出O(n)解法就先写一个暴力解至少保证有分。很多笔试系统是按用例跑分的部分正确也能拿部分分数空着才是彻底的零分。6.3 我踩过的几个典型坑和纠正方式第一个坑是审题不仔细。有一次模拟题问“下列哪个选项不是线程安全的”我全程在找“是线程安全”的特征最后选错。从那以后我养成习惯动笔前先用笔把“不是”“错误的是”“最不适合”这类否定词圈出来一套卷做完再回查一遍。第二个坑是答案犹豫不决反复改。我对答案时有个心理规律第一直觉往往是记忆最清晰的犹豫之后改的答案大概率会改错。所以现在做选择题的原则是除非确认自己一开始理解错了题目否则不轻易改第一遍的答案。第三个坑是代码题不写注释就被打断思路。笔试环境没有IDE提示纯手写时思路容易断。后来我习惯先写一行注释把整体思路列出来比如第一步写“处理边界条件”第二步写“双指针遍历”这样写代码时不会迷失。6.4 针对这份卷的复习清单总结如果你现在有大约三周准备时间可以按这个节奏来第一周Java基础加集合框架。把String、包装类、异常、反射、HashMap全部过一遍配合LeetCode热题里的链表和二叉树各刷十道。第二周多线程加JVM。重点掌握synchronized、volatile、线程池和JVM内存模型算法转战二分和排序。第三周网络加数据库加操作系统。用思维导图把TCP、HTTP、MySQL事务、索引、进程线程串起来每天做一套完整笔试试卷练手感。这套复习节奏不一定适合所有人但对大多数基础中等偏上的同学来说能最大化覆盖高频考点。网易2018这套卷的价值不在于题目本身有多难而在于它把Java工程师应该掌握的知识广度和深度都展示出来了。把一份经典卷嚼碎了比泛泛刷十份新卷更有效。