爱奇艺Java秋招笔试题核心考点与备考策略

发布时间:2026/8/31 5:28:07
爱奇艺Java秋招笔试题核心考点与备考策略 1. 从这套题看爱奇艺Java岗的考察偏好先来聊一个可能让很多人意外的事实爱奇艺这套2019秋招Java笔试题B放到现在依然有很高的参考价值。那年秋招我还在一家小厂做Java后端整套做下来最大的感受是——它的考点分布和大多数互联网公司的Java笔试题高度重合但有几个题目明显是在“筛选”不是单纯考你会不会而是考你为什么。我当时刷完这套题对照牛客网上的讨论区和几份面经整理出的考点分布大概是这样的考察模块题量占比估算典型考点Java语法与面向对象25%左右继承、多态、String、包装类Java集合框架20%左右ArrayList/LinkedList、HashMap、HashSet并发编程15%左右synchronized、volatile、线程池JVM与内存模型15%左右内存区域、GC、类加载算法与数据结构15%左右排序、链表、二叉树网络与操作系统10%左右TCP、HTTP、进程线程注意这只是我根据回忆和面经做的粗略统计不是原卷的标准答案比例但方向是准的。这套题的核心价值不在于题目本身而在于它代表了一类“基础扎实型”笔试题的典型出题思路。如果你准备的是BAT、TMD这类公司的Java校招这套题的复习价值会非常大。为什么这么说因为2019年秋招恰好是“Java八股文”面试文化形成的关键年份。那之前的笔试题更偏向“会不会用”那之后越来越偏向“源码看过没有、原理懂不懂”。这套B卷刚好卡在中间——既有一些纯记忆型的基础题又已经开始出现需要看源码才能答对的题目。做这套题最大的收获是能帮你校准自己的Java基础到底处于哪个水位线上。2. Java基础与面向对象经典题目里的易错点这套B卷里的Java基础题大部分并不难但有几个细节题值得好好掰扯一下。这种题不是考你背不背得出来而是考你在实际写代码时有没有踩过对应的坑。2.1 继承与多态重载和重写的判定顺序有一道很典型的题给出一段父子类代码问调用某个方法时输出什么。这种题我在实际面试中见过不下十次但每次扔给身边的同事做还是有人会答错。核心就三条规则编译看左边运行看右边——变量声明类型决定编译期能调用哪些方法实际执行的是堆上对象类型的方法。重载是静态绑定的编译期根据参数的静态类型决定调用哪个重载方法。重写是动态绑定的运行期根据对象的实际类型决定调用哪个方法。举个例子有一个经典的坑class Animal { public void eat() { System.out.println(Animal eat); } } class Dog extends Animal { public void eat() { System.out.println(Dog eat); } public void bark() { System.out.println(Dog bark); } } Animal a new Dog(); a.eat(); // 输出 Dog eat动态绑定 // a.bark(); // 编译报错编译看左边的Animal类型当时群里有个同学做这道题选了“Animal eat”理由是“变量是Animal类型所以调用Animal的方法”。这就是典型的把编译期类型检查和运行期动态绑定搞混了。方法重写是动态绑定的不管引用类型是什么实际执行的是堆上那个对象的方法。2.2 String相关的陷阱不可变性与常量池String的考察点也几乎每年都有。爱奇艺这套题里有一道和String比较相关的题问的是s1 s2的结果。这类题的核心是字符串常量池和不可变性。最经典的几行代码String s1 hello; String s2 hello; System.out.println(s1 s2); // true都在常量池同一对象 String s3 new String(hello); System.out.println(s1 s3); // falsenew出来的在堆上 String s4 s3.intern(); System.out.println(s1 s4); // trueintern返回常量池中的引用我见过有人把第一行的结果答成false原因是“两个变量指向两个不同的对象”。这个理解不对——直接双引号声明的字符串字面量会先到常量池里找如果存在就直接复用所以s1和s2实际指向同一个常量池对象。还有一个容易忽视的点字符串拼接String a hello; String b a world;这种写法在编译期无法确定值因为a是变量所以实际是通过StringBuilder拼接后生成新对象。如果换成final String a hello那编译期就能确定a的值b会直接指向常量池中“hello world”。这个差异在笔试里容易被考到实际开发中则提示我们频繁拼接字符串一定要用StringBuilder否则会创建大量中间对象。2.3 包装类与缓存机制Integer缓存的范围自动装箱、拆箱也是高频考点。爱奇艺这套题里应该有一道和Integer相关的比较题问Integer a 127; Integer b 127; a b的结果。答案是true因为Integer默认缓存了-128 ~ 127之间的值valueOf方法会直接返回缓存对象。但如果是Integer a 128; Integer b 128;结果就是false因为超出了缓存范围会new出两个新对象。这个机制我在实际开发中踩过坑——当时有一段代码用Integer做 Map 的 key取出来比较的时候用测试环境的数据都在127以内一切正常线上数据超过127就出现了完全无法复现的bug。排查了半天最后发现是比较的锅。教训就是包装类型比较一律用equals不要用。这个习惯如果能在笔试前就养成考试时这种题根本不需要思考。这些基础题在我看来是送分题但也是最容易因为“一看就会、一写就错”而丢分的题。建议做这类题时别急着选答案先在心里把运行过程完整推演一遍尤其是涉及继承、重载、字符串拼接时。3. 集合框架源码题从使用到原理的跨越式考察爱奇艺这套B卷的集合题难度明显比基础题上了一个台阶。如果你只看过《Java编程思想》里集合框架的用法但没有翻过JDK源码这部分会很吃力。3.1 ArrayList和LinkedList不只是“数组vs链表”有一道常见的题是问ArrayList和LinkedList的区别。大部分人能答上来“ArrayList底层是数组LinkedList底层是双向链表查询ArrayList快插入删除LinkedList快”。但在笔试题里这个答案只能拿一半分因为题目会继续深挖。比如ArrayList的初始容量是多少默认是10但注意是懒加载——第一次add时才真正初始化数组为容量10。ArrayList扩容是怎么做的grow方法里新容量是oldCapacity (oldCapacity 1)也就是1.5倍然后通过Arrays.copyOf复制旧数组。LinkedList为什么插入删除快因为它是双向链表插入删除只需要改变指针但如果指定了索引位置LinkedList需要从头或尾遍历到该位置时间复杂度是O(n)。所以“LinkedList插入删除快”这句话是有条件的插入在头部或尾部确实快随机位置插入反而可能比ArrayList慢。我在做这道题时还想到一个问题如果频繁在列表头部插入数据应该选什么ArrayList在头部插入需要移动所有元素LinkedList在头部插入只需要改指针。但ArrayDeque在这个场景下比LinkedList更合适因为它的底层是循环数组头部插入的时间复杂度是O(1)且省去了链表节点的额外内存开销。这个知识点在笔试里如果主动写出来会是一个不错的加分项。3.2 HashMap并发问题与ConcurrentHashMap的演进HashMap相关题目在Java笔试里几乎占了半壁江山。爱奇艺这套题里HashMap的考察点涵盖了数据结构、put流程、扩容机制、并发安全性。JDK 8之后的HashMap结构是数组链表红黑树。当链表长度超过8且数组长度大于等于64时链表会转成红黑树当红黑树节点数小于等于6时会退化为链表。为什么转树阈值是8因为源码注释里有一个泊松分布的分析在负载因子0.75、随机哈希函数的前提下链表长度达到8的概率已经非常低约千万分之六。这是时间和空间的权衡不是随便拍脑袋定的数字。HashMap的put流程笔试时最好能画出来计算(n - 1) hash确定桶的位置如果桶为空直接new一个Node放进去如果桶不为空遍历链表或红黑树如果找到相同key覆盖value并返回旧值如果没找到插入到链表尾部或红黑树插入后检查size threshold如果是触发扩容而并发问题方面有一个经典考题HashMap在多线程环境下put为什么会丢数据因为多线程同时触发扩容时多个线程可能同时在处理同一批链表节点导致节点覆盖、丢失。JDK 7里还会因为头插法形成环形链表导致get时死循环。JDK 8改成尾插法解决了循环问题但数据丢失问题依然存在。所以笔试题里只要问到并发场景下的Map答案必然是ConcurrentHashMap。但更要深入一层的是JDK 8的ConcurrentHashMap抛弃了JDK 7的分段锁改用CAS synchronized锁桶头节点的方式。锁的粒度从段级细化到桶级并发度大幅提升。这里的CAS是用来处理桶为空时的初始化synchronized则用来处理桶不为空时的读写冲突。它依然不能保证绝对一致性强但能保证单个操作的原子性和内存可见性。我当时在准备这部分时是直接把源码里的putVal、resize、treeifyBin这几个方法从头到尾读了一遍然后再回来做笔试题基本就都是降维打击了。我的建议是集合框架这部分一定要看源码不能只看面经总结。4. 并发编程与JVM最能拉开差距的得分点这套题里最拉分的部分我体感是并发和JVM。这里的基础版本大概就是“synchronized和ReentrantLock的区别”“JVM内存分为哪几块”这种但高分版本会深入到底层原理和实际排查经验。4.1 synchronized的锁升级无锁→偏向锁→轻量级锁→重量级锁synchronized的锁升级过程几乎每次面试都会问到。我第一次在笔试里见到这个考点时坦白讲当时是我学习Java以来最崩溃的瞬间之一。为什么我连“synchronized能保证原子性和可见性”都背得滚瓜烂熟了还要知道锁怎么升级后来我才理解锁升级是理解Java并发性能的关键。synchronized在JDK 6之前是重量级锁每次加锁都需要操作系统mutex性能极差。JDK 6引入偏向锁和轻量级锁之后性能大幅提升。现在你写synchronized虚拟机会根据竞争情况自动决定锁的状态无锁状态没有线程访问同步块。偏向锁第一个线程访问时在对象头Mark Word里记录线程ID之后这个线程再次进入时不需要任何CAS操作。适合“同一个线程反复进入”的场景。轻量级锁有其他线程竞争时偏向锁撤销升级为轻量级锁。线程通过CAS在栈帧中创建锁记录自旋等待持有锁的线程释放。重量级锁自旋超过阈值默认10次或自适应或者等待线程数过多升级为重量级锁此时线程进入阻塞队列由操作系统调度。笔试经验是只要题干里出现“单线程反复进入同步块”优先考虑偏向锁“少量线程交替执行”优先考虑轻量级锁“大量线程竞争激烈”锁定重量级锁。4.2 volatile的可见性与禁止重排序volatile也是必考题。很多人能答上来“保证可见性、不保证原子性”但问到“为什么不保证原子性”“底层怎么保证可见性”就卡住了。可见性volatile变量在写操作时会强制将工作内存中的值刷新到主内存并让其他线程的缓存行失效通过缓存一致性协议如MESI。读操作时直接从主内存读取最新值。禁止重排序volatile通过内存屏障实现。在写volatile变量之后插入StoreStore屏障在读volatile变量之前插入LoadLoad屏障防止编译器和CPU重排序。不保证原子性比如count实际上有“读取count→计算count1→写回count”三步volatile只能保证每一步的可见性不能保证这三步作为一个整体不被其他线程打断。有一个常见的坑是很多人以为volatile修饰的变量做自增是线程安全的。不是的。这就是一道经典的笔试题变体。如果自增操作需要原子性应该用AtomicInteger的compareAndSet或incrementAndGet或者用synchronized包裹整个操作。4.3 JVM内存区域与OOM定位JVM内存区域的划分基本是必考的。堆、虚拟机栈、本地方法栈、方法区元空间、程序计数器每个区域的作用、是否线程私有、可能出现的异常都要烂熟于心。爱奇艺这套题里JVM的考察方式更多是给一个场景让你判断会抛出什么异常。比如堆溢出java.lang.OutOfMemoryError: Java heap space栈溢出java.lang.StackOverflowError无限递归元空间溢出java.lang.OutOfMemoryError: Metaspace大量动态生成类直接内存溢出java.lang.OutOfMemoryError: Direct buffer memoryNIO使用不合理实际开发中定位OOM问题的思路基本上是在JVM启动参数里加-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/path/to/dump让虚拟机在OOM时自动导出堆转储文件。用jmap -dump:formatb,fileheap.hprof pid手动导出堆转储。用MAT或VisualVM分析重点看支配树——哪个对象占用了最大内存它的GC Roots引用链是谁。结合业务代码判断是内存泄漏对象无法回收还是内存溢出对象确实都活着但太多了。这些内容笔试不一定考得特别深但面试官经常会顺着笔试题追问下去所以备考时不能只背题要把题背后的排查链路也串起来。我在后来的工作里有一半以上的线上问题排查时间都花在JVM调优和OOM分析上这些功夫在笔试阶段就开始打基础确实不亏。4.4 类加载机制与双亲委派类加载这块也是高频考点尤其是双亲委派模型。原理不复杂当一个类加载器收到类加载请求时它不会自己先去加载而是先把这个请求委派给父类加载器每一层都这么做最终传到启动类加载器Bootstrap ClassLoader只有父类加载器无法完成加载时子类加载器才会自己尝试加载。这里有一个笔试时经常出的细节题双亲委派模型能保证什么答案是保证Java核心类库的类型安全。比如java.lang.String无论哪个类加载器加载它最终都会委托给启动类加载器保证所有类加载器加载到的String都是同一个类避免核心类被随意覆盖。还有一个考察点是打破双亲委派的场景Tomcat的WebAppClassLoader、JDBC的DriverManager、SPI机制下的ServiceLoader等。Tomcat为什么打破因为每个Web应用应该拥有独立的类库互不干扰JDBC为什么打破因为DriverManager在启动类加载器加载而具体的数据库驱动在应用类加载器加载需要逆向委派。5. 算法与手写代码题排序、链表和设计模式爱奇艺这套B卷的算法题不算特别难但有几道很有代表性。网上的解题方案很多这里只聊两个重点排序的边界条件和手写代码的规范程度。5.1 冒泡排序和快速排序的现场实现算法题里出现排序的频率极高2019年的笔试题尤其爱考冒泡和快排。但注意笔试考的不是你能默写出来而是你能不能写出“一遍过”的代码。以快速排序为例最标准的写法是public void quickSort(int[] arr, int left, int right) { if (left right) { return; } int pivot partition(arr, left, right); quickSort(arr, left, pivot - 1); quickSort(arr, pivot 1, right); } private int partition(int[] arr, int left, int right) { int pivot arr[left]; int i left, j right; while (i j) { // 注意先从右往左找小于pivot的 while (i j arr[j] pivot) { j--; } while (i j arr[i] pivot) { i; } if (i j) { swap(arr, i, j); } } swap(arr, left, i); return i; }有几个容易出错的点实战中每次都有人栽在上面递归结束条件必须是left right不是left right。当数组长度为2时pivot可能等于left递归quickSort(arr, left, pivot - 1)时left会大于right少了等号判断就是无限递归。先从右往左找再从左往右找顺序不能乱。因为基准选的是最左边的元素如果先从左往右找最后交换arr[left]和arr[i]时可能把一个大于pivot的值换到最左边导致排序错误。边界条件里和不能丢否则遇到重复元素时可能左右指针无法正常移动陷入死循环。冒泡排序的考点则在优化上如果某一轮比较中没有发生任何交换说明数组已经有序可以提前退出。这个优化在笔试中写出来妥妥的加分项代码也不复杂public void bubbleSort(int[] arr) { for (int i 0; i arr.length - 1; i) { boolean swapped false; for (int j 0; j arr.length - 1 - i; j) { if (arr[j] arr[j 1]) { swap(arr, j, j 1); swapped true; } } if (!swapped) { break; } } }5.2 手写单例模式饿汉式、懒汉式和双重检查锁设计模式在笔试题里常以手写代码的形式出现最常考的就是单例。爱奇艺这套题里应该有一道“写一个线程安全的单例”题。我当时在试卷上写的是双重检查锁public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }这里有个关键点instance必须用volatile修饰。为什么因为new Singleton()在底层有三步分配内存、初始化对象、将引用指向内存。如果不用volatile第二步和第三步可能被重排序——先指向内存此时对象还没初始化完另一个线程拿到引用后发现instance ! null直接返回一个未初始化完成的对象程序就出诡异问题了。volatile通过内存屏障禁止了这个重排序。但笔试时如果只写双重检查锁可能还不够体现水平。更优雅的写法是静态内部类public class Singleton { private Singleton() {} private static class Holder { private static final Singleton INSTANCE new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }静态内部类的方式利用了类加载机制——Holder只有在getInstance()第一次被调用时才会被加载加载时初始化INSTANCE由JVM保证线程安全。这是既懒加载又线程安全的最简洁方案。笔试时如果时间充裕可以把双重检查锁的代码写出来再补充一句“更推荐静态内部类写法”面试官印象分会拉满。5.3 链表反转与二叉树遍历算法题里链表和二叉树也是常客。链表反转要求写出迭代和递归两种实现这个考的是代码熟练度没有太多技巧含量但容易在细节上出错比如反转时要注意保存下一个节点的引用否则指针断了找不到后续节点。递归实现时递归的结束条件是head null || head.next null。二叉树这块爱奇艺这套B卷的题目应该不会太难层序遍历、前中后序遍历、求深度这类基础题要手熟。准备笔试时不需要刷特别难的题但高频基础题必须做到闭着眼睛能写出来。6. 复盘这套题之后我对备考与做题顺序的几点经验最后聊聊备考这套题时的整体复盘和一些实操经验以下内容偏个人向但很多坑是共通的。6.1 先做真题再看面经最后补源码很多人备考时习惯先刷一遍面经觉得面经覆盖全面然后才去做真题。但我的经验是反过来的先限时做真题把不会的题目标记出来再回头去查对应的知识点和面经最后深入研究JDK源码。这样做的好处是你很清楚自己哪里薄弱复习时有针对性不会做大量无效的“扫盲式学习”。做真题时尽量模拟真实考试环境75分钟闭卷不看答案调好闹钟。第一遍做的时候分数低很正常重要的是把每道错题背后的知识点吃透而不是背答案。6.2 检查代码题时重点检查边界条件和空指针笔试时代码题最容易丢分的地方不是算法逻辑而是边界条件。数组为空时怎么办链表只有一个节点时怎么办字符串为null时调用length()会不会NPE我见过很多人算法主体写得完全正确但没判空直接导致用例失败。笔试的评测机通常会特别构造边界用例空数组、单元素数组、超过int范围的值这些都必须考虑到。一个简单的习惯是写完代码后在脑子里手动跑一遍arr.length 0、arr.length 1、n 1的情况。6.3 关于“背八股文”的真实看法网上关于“Java八股文”的争论一直很大但经历过校招之后我的态度是八股文要背但不能只背。背是为了快速建立知识框架确保面试时不会说不出话但笔试和面试真正拉开差距的是你能不能把八股文背后的原理讲清楚。比如你可以背“HashMap线程不安全”但如果面试官追问“为什么不安全”“JDK 7和JDK 8有什么区别”“ConcurrentHashMap为什么线程安全”你还得能答上来。八股文是树干源码和实战经验是枝叶缺一不可。6.4 这套题里少了什么再说几句实话。爱奇艺这套2019年的Java笔试题B覆盖了Java基础、集合、并发、JVM、算法这些核心模块但整体难度属于中等偏上没有深入到Spring、微服务、分布式这些框架层面的知识。如果你拿这套题作为唯一的备考资料可能会导致知识面明显偏窄。我当时做这套题时考完后立刻补了Spring IOC/AOP、Spring Boot自动配置、MySQL索引与事务隔离级别、Redis数据结构与持久化这几块。笔试只是第一次筛选后面的面试会考得更广、更深。这套B卷适合作为校招Java基础的摸底和练手资料但备考体系还需要用更多真题和项目经验来填充。最后再分享一个小细节做题时如果遇到读不懂题意的题目先跳过别硬耗。我当时在爱奇艺这套B卷上有一道关于JVM调优参数的题目题干描述得模棱两可我花了将近十分钟才弄明白它到底想问什么结果挤占了后面算法题的时间。考试时遇到理解成本高的题目先做个标记跳过把有把握的分数拿稳了再回头啃。这个策略帮我保住了一道快速排序的大题后来复盘时觉得当时如果死磕那道JVM题整场笔试的节奏就全乱了。