尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

欢聚时代校招Java基础笔试题B卷详解:考点拆解与避坑指南

欢聚时代校招Java基础笔试题B卷详解:考点拆解与避坑指南 这份欢聚时代2017校招笔试题目JAVA基础类B卷放在今天看依然很有嚼头。虽然时间过去了几年但Java基础知识的考察核心并没有变面向对象思想、集合框架、异常处理、JVM基础、多线程这些依然是校招笔试和面试的高频考点也是日常开发中真正决定代码质量的东西。我身边不少准备跳槽的同事还会专门翻这类老题来复盘基础就是因为它们出题规范、考点集中比网上零散的“八股文”更有参考价值。这套B卷尤其适合正在准备Java校招、或者工作一两年想要系统梳理基础的人拿来练手和查漏补缺。1. 试卷定位互联网公司校招笔试的出题逻辑1.1 为什么笔试总爱考Java基础不少同学看到笔试题目全是基础概念题会觉得“这有什么难的”结果一做就错。原因在于校招笔试的考察目标从来不是让你背出某个API的拼写而是通过基础题筛选出真正具备编程思维的人。欢聚时代这类做直播、社交产品起家的互联网公司业务迭代速度极快新入职的工程师需要快速融入团队并产出可维护的代码这就要求候选人必须对语言核心机制有足够深入的理解。一套Java基础笔试题看起来是在考语法实际上是在考三件事第一你有没有完整的学习体系而不是只会调包第二你能不能看懂别人写的代码理解运行期和编译期的区别第三你写的代码是否具备健壮性能不能处理边界情况和异常。笔试中的每一道题几乎都能映射到实际开发中的具体场景。比如集合类的题目对应的是日常CRUD中的数据结构选型多线程题目对应的是高并发场景下的资源竞争问题。1.2 B卷的侧重点与备考定位从这套B卷的考点分布来看它没有刻意追求偏题怪题而是把重点放在Java核心基础类的常见易错点上。这意味着两点一是题目覆盖面广从语法糖到底层内存模型都有涉及二是题目的区分度主要来自“细节是否真的懂”而不是“是否见过这道题”。备考这套试卷以及同类笔试最忌讳的就是死记硬背答案。比如问到“ArrayList和LinkedList的区别”如果你只回答“一个基于数组、一个基于链表”这道题你只能拿到一半的分数。阅卷者期望看到的答案是ArrayList的随机访问时间复杂度是O(1)但插入删除可能导致数组拷贝LinkedList虽然插入删除看似高效但实际需要遍历查找节点且每个节点有额外的内存开销。只有把这些底层实现讲清楚才能体现出你是真正理解而不是背了结论。2. Java基础考点拆解从语法糖到底层原理2.1 面向对象三大特性的考察方式欢聚时代的笔试题里面向对象部分几乎没有出过“什么是封装、继承、多态”这种直接背诵题而是把考察点藏在代码阅读和改错中。这是互联网公司出题的典型风格基础概念谁都能说两句但能不能在具体代码中识别出多态的应用就是另一回事了。多态是Java面试中出镜率最高也最容易出错的考点。笔试中常见的考察方式是这样的父类引用指向子类对象调用一个被子类重写的方法输出是什么。很多人在这种题上丢分是因为混淆了“编译看左边运行看右边”这句口诀的真正含义。编译期Java编译器看到的是父类引用类型所以只允许调用父类中声明过的方法运行期JVM实际调用的是子类重写后的方法版本。这个过程叫做动态绑定它依赖于方法表vtable的查找机制。而静态方法、私有方法和final方法的调用则走静态绑定这也是为什么子类中定义同名静态方法不算重写的原因。封装这个考点笔试题更喜欢从访问修饰符入手比如“protected成员在什么范围内可见”。很多人的答案是“同一包内和子类中”但这个表述不够严谨。准确的说法是protected成员的可见范围是同一包内的所有类以及不同包中该类的子类。这里有个细节很容易被忽略子类通过继承得到的protected成员在子类自己的代码中可以访问但如果通过父类引用去new一个子类对象在子类外部依然不能访问。市面上不少教程把这一点讲得模棱两可笔试遇到就是送分题变成送命题。2.2 集合框架校招笔试题的“必争之地”整套B卷里集合框架相关题目占比相当可观这与实际开发中的使用频率完全一致。日常写业务代码集合类几乎占据了半壁江山。笔试中对集合的考察集中在三点数据结构原理、线程安全性、遍历时的行为。HashMap是面试中的绝对核心。笔试喜欢考的点是HashMap的底层数据结构是什么put一个键值对时经历了哪些步骤。从JDK 1.8开始HashMap底层是数组加链表加红黑树的结构。put操作的完整流程是首先通过key的hashCode经过扰动函数计算出hash值然后通过(n-1) hash找到对应的数组下标如果这个位置为空直接放入新节点如果不为空遍历链表或红黑树判断key是否存在存在则覆盖旧值不存在则插入新节点当链表长度达到8且数组长度大于等于64时链表会转换为红黑树。很多人会忽略一个细节链表转红黑树有两个条件链表长度大于等于8同时数组长度大于等于64而不是只有一个条件。如果数组长度不足64即使链表已经很长也只会进行扩容不会转树。这个细节笔试中经常以判断题或改错题的形式出现目的就是考察你到底有没有完整读过源码。另外扩容时HashMap会重新计算每个元素的hash槽位这也是为什么并发情况下扩容容易出现死循环JDK 1.7及以前的原因。ArrayList与Vector的区别、Iterator与ListIterator的区别这类送分题也要答出深度。特别是“快速失败fail-fast”机制Iterator遍历时如果检测到集合结构被修改会立即抛出ConcurrentModificationException。这个机制实现的关键是modCount字段每次结构性修改都会增加这个计数迭代器在遍历前会缓存expectedModCount两者不一致就抛异常。2.3 异常体系与代码健壮性异常处理在笔试中往往以“阅读代码判断输出”的形式出现考点集中在try-catch-finally的执行顺序、受检异常与非受检异常的区别、以及异常覆盖的问题上。finally块中的return语句是一个经典陷阱。如果try块和finally块中都有return语句JVM会执行finally块中的return且会覆盖try块中的返回值如果try块中有returnfinally块中只是修改了基础类型的返回值变量那么返回值不会被修改因为返回值的传递发生在finally执行之前。但如果返回的是引用类型finally中对该对象内部的修改会生效。这类细节没有实际写过代码光靠看是记不住的备考阶段建议把这些场景挨个手写一遍。受检异常checked exception与非受检异常unchecked exception的区别也是必考题。受检异常必须在编译期处理要么throws声明抛出要么try-catch捕获非受检异常RuntimeException及其子类则不需要强制处理。一个值得深入理解的点是为什么Spring的事务默认只对RuntimeException回滚这是因为受检异常通常代表可恢复的业务异常比如余额不足而非受检异常代表不可预料的系统错误比如空指针、数组越界后者需要立即打断流程并进行回滚。3. 笔试核心环节典型题型的解题思路还原3.1 程序阅读题手写输出结果类的破题方法这套B卷中程序阅读题的占比不小。这类题目考察的核心是你是否能准确模拟JVM执行代码的过程。做题的时候建议在草稿纸上画一个简单的内存示意图分栏列出栈帧中的局部变量、堆中的对象、方法区的类信息一边执行一边更新。举一个典型的例子String比较相关的输出题。String s1 hello; String s2 hello; String s3 new String(hello); 问s1 s2、s1 s3、s1.equals(s3)的结果。这里的考点是字符串常量池与堆内存的区别。s1和s2在编译期就已经确定直接指向常量池中的同一个字符串对象所以s1 s2为trues3通过new创建在堆中开辟新空间所以s1 s3为false但equals比较的是内容所以为true。再比如Integer缓存的题Integer a 127; Integer b 127; Integer c 128; Integer d 128; 问a b和c d的结果。答案是前者为true后者为false。原因是Integer内部有缓存机制默认缓存范围是-128到127在这个范围内的整数直接返回缓存对象超出范围才会new新对象。很多人在这一题上栽跟头是因为不清楚自动装箱底层调用了Integer.valueOf()方法而valueOf方法内部有缓存判断逻辑。做这种题最忌讳的是只看答案不动手验证。我在备考时养成一个习惯每做完一道程序阅读题就把代码原样放进IDE里运行一遍对比自己的推理和实际输出的差异。差异就是知识盲区比记十道错题都管用。3.2 代码设计题单例模式与排序算法的标准答法B卷中的手写代码题常见的命题方向有两个一是设计模式二是基础算法。设计模式中出镜率最高的就是单例模式尤其是双重检查锁Double-Checked Locking写法因为它同时考察了多线程、同步、volatile关键字等多个知识点。标准的双重检查锁单例实现是私有构造方法静态成员变量用volatile修饰getInstance方法中第一次判断实例是否为空不加锁为空则进入同步代码块在同步代码块内再次判断实例是否为空然后创建实例。这里的volatile是必须的因为它防止了指令重排序。实例创建过程在字节码层面分为三步分配内存、初始化对象、将引用指向内存地址。如果不加volatileJVM可能重排序为“先让引用指向内存地址再初始化对象”这样另一个线程就会拿到一个尚未初始化完成的对象。排序算法方面笔试常考快速排序和冒泡排序。快排的核心思想选一个基准值将数组分成左右两部分左边都比基准小右边都比基准大然后递归处理左右子数组。实现时有两个细节要注意一是基准值的选取固定取第一个元素在数据接近有序时会导致递归深度退化为O(n)可以考虑随机选取二是递归终止条件left right时返回。笔试阅卷时考官看重的是代码逻辑是否清晰、边界条件是否完备所以建议写快排时先写清楚注释再写实现。3.3 简答题JVM与多线程的答题框架JVM内存模型是简答题的常客。答题的时候不要只是罗列“堆、栈、方法区”这几个名词而是要把每一块区域的作用、存储内容、是否线程共享、可能抛出的异常都写清楚。比如堆是所有线程共享的存放对象实例主要垃圾回收区域当堆内存不足时抛出OutOfMemoryError虚拟机栈是线程私有的存放局部变量表、操作数栈、动态链接、方法出口等栈深度不足时抛出StackOverflowError方法区Java 8以后为元空间存放类元信息、常量池、静态变量等。多线程的简答重点围绕线程生命周期和线程安全。线程的六个状态NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED之间的转换关系要画清楚尤其是WAITING和TIMED_WAITING的区别前者是无限期等待需要其他线程显式唤醒后者是超时自动唤醒。线程安全的考察点集中在 synchronized 和 volatile 上。要讲清楚 synchronized 的三种用法修饰实例方法锁的是this、修饰静态方法锁的是类对象、修饰代码块锁的是指定对象和底层实现Monitor机制JDK 1.6以后引入偏向锁、轻量级锁、重量级锁的升级过程。volatile 的考察点是可见性和有序性但不保证原子性。如果题目里出现“volatile能保证线程安全吗”这种问题标准的回答是不能因为它无法解决复合操作的原子性问题例如count这种读取-修改-写入的操作volatile无能为力。4. 高频失分点与避坑技巧实录4.1 集合遍历时删除元素的正确姿势笔试和实际开发中最常见的坑之一就是在遍历集合时删除元素。很多人的第一反应是for循环加list.remove()运行起来就报ConcurrentModificationException。原因很简单普通for循环中每执行一次remove集合的modCount就会加1而迭代器内部的expectedModCount没有被更新下一次循环调用iterator.next()时发现两个值不相等立即抛出异常。正确的做法有三种一是使用Iterator的remove方法它会同步修改expectedModCount二是使用for循环倒序遍历索引删除后索引减一三是使用JDK 1.8的removeIf方法底层也是迭代器实现。笔试中如果让你“说出遍历删除的几种方式及区别”回答到removeIf这一层会是个不错的加分项。4.2 与equals之间的爱恨情仇“比较的是引用地址equals比较的是内容”这是最常见的面试答案但它实际上是半对半错。准确地说Object类的equals方法内部使用的就是比较的仍然是引用地址。我们平时说的“equals比较内容”是因为String、Integer这些类重写了equals方法改成了逐字符或逐值比较。更深一层的问题是为什么重写equals必须同时重写hashCode这是因为基于哈希的集合HashMap、HashSet在查找元素时先通过hashCode定位到桶再通过equals比较桶内的元素。如果只重写equals不重写hashCode两个内容相同的对象hashCode不同会被哈希到不同的桶里那equals的比较就完全没有意义了。笔试很喜欢出一道题往HashSet里放两个内容相同的对象问集合里有几个元素。不重写hashCode时结果是两个重写了才是1个。4.3 try-catch-finally中的返回值陷阱这个考点出错率极高原因是它违反大多数人的直观认知。看下面这个代码片段public static int test() { int i 1; try { return i; } finally { i; } }这段代码的返回值是1不是2。原因是当try块中执行到return语句时JVM会先把返回值i的当前值1保存到局部变量表中然后才执行finally块。finally块中i修改的是局部变量表中的i但已经被保存的返回值不会跟着变。返回的始终是1。但如果把返回值换成引用类型public static StringBuilder test() { StringBuilder sb new StringBuilder(a); try { return sb; } finally { sb.append(b); } }这种情况下finally中的操作对引用对象内部状态的修改是生效的返回值是“ab”。因为这里的return返回的是引用而非引用的副本finally中修改的是同一个对象的内容。这两种场景建议备考时一定亲手在IDE里跑一遍印象会比看书深刻得多。5. 从笔试到面试备考路径与底层能力构建5.1 建立自己的Java知识体系而不是背八股文很多人在准备校招的时候有个误区疯狂收集“Java面试八股文”把上百个问题的答案背下来。这种做法在笔试阶段或许能混过一些选择题但一旦遇到需要你现场推导的程序阅读题或设计题背答案就完全失效了。笔试和面试真正考察的是你脑子里有没有一张完整的知识网络。我建议按照这样的路径来整理知识体系先梳理Java语法基础数据类型、运算符、控制流、面向对象再深入集合框架与泛型接着是异常、IO、反射、注解然后是多线程与并发工具类最后是JVM内存模型与垃圾回收。每一块都要问自己三个问题它解决了什么问题它的底层实现是什么它有什么缺点和替代方案比如学到HashMap不仅要会put和get还要能说清楚为什么负载因子默认是0.75以及链表转红黑树的阈值为什么是8。网上关于Java学习路线的资料非常多但真正有价值的不是那一张张知识清单而是你自己动手实践的过程。这里推荐几类经典书籍作为体系支撑结合B卷的考点有针对性地阅读。并发这块看《Java并发编程的艺术》JVM看《深入理解Java虚拟机》集合框架和语言特性看《Java核心技术》。笔试前一个月建议保持每天刷一套真题的节奏重点不在于做对多少而在于把每道错题背后的知识点完整展开复习。5.2 实战模拟与IDE调试技巧笔试的编程题光在纸上写是不够的。备考阶段一定要上机实战用IDE跑一遍自己写的每个算法和设计模式。我备考时常用的方式是用LeetCode上Easy和Medium难度的题目来保持手感特别是数组、字符串、链表、二叉树这几类基础题型它们是笔试算法题的绝对主力。围绕这些类型把快速排序、冒泡排序的写法练到可以闭眼编码的程度。这里的重点不是炫技写出最简洁的代码而是要保证在有限时间内写出无语法错误、边界条件完整的可运行代码。还有一个很多人忽略的练习方法用IDE的调试器去跟踪代码的执行过程。比如前面提到的HashMap put流程与其看十篇源码分析文章不如自己写一段代码在put方法调用处打上断点一步一步观察数组下标的变化、链表的插入、扩容的触发时机。这样亲眼见过一遍执行流程比背十遍源码都管用。排查数组越界异常ArrayIndexOutOfBoundsException这类运行时错误也是同一个思路打开调试器看栈帧里当前访问的下标和数组的长度一秒钟就能定位问题。5.3 笔试之后的面试衔接笔试只是校招的第一关但笔试中的知识点往往会以更深入的形态出现在面试环节。你在笔试里写的答案很可能就是面试官追问的起点。比如笔试考了HashMap的底层原理面试官就可能接着问“为什么HashMap线程不安全”“ConcurrentHashMap是怎么保证线程安全的”“分段锁和CAS分别用在哪里”。这些追问环环相扣如果笔试阶段只是背了个大概面试中很容易露馅。所以备考笔试的时候就应该按面试的深度去要求自己。拿到一道笔试题别只满足于做对试着把这道题涉及的上下游知识全部过一遍。比如遇到String相关的题就顺手梳理String、StringBuilder、StringBuffer三者的区别和适用场景遇到异常相关的题就顺手整理常见的运行时异常类型和各自的触发场景。这样笔试准备的每一分钟都在为面试做铺垫效率最高。我在实际辅导过几位学弟学妹备考之后发现凡是能顺利通过欢聚时代这类公司笔试和面试的几乎都有一个共性他们不会把基础知识和实际开发割裂开。在他们眼里HashMap不只是面试题而是日常开发里每天都在用的工具多线程不只是考点而是高并发场景下真实存在的资源竞争问题。带着这种视角去准备笔试整个复习过程会自然很多也不会觉得是在背枯燥的八股文。最后分享一个我一直沿用的习惯准备一个错题本把每次笔试和模拟中做错的题按知识点归类考前只看错题本效率远比翻一遍大部头教材高得多。
返回列表