ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

2020年Java大厂面试复盘:基础、JVM、并发与分布式高频考点全解析

2020年Java大厂面试复盘:基础、JVM、并发与分布式高频考点全解析 2020年对Java程序员来说最直观的变化就是面试几乎全部转到了线上。笔试放在牛客网视频面试一场接一场我前前后后投了阿里、腾讯、字节跳动、美团、拼多多几家Java后端岗把大厂笔试和面试的流程完整走了一遍。这篇文章就是我的笔经面经复盘覆盖Java基础、集合、JVM、并发、Spring、分布式和项目场景题也把面试现场踩过的坑一并整理出来给正在准备Java大厂面试的人做个参考。1. 2020年Java大厂面试到底在考什么1.1 线上面试带来的流程变化2020年的招聘节奏和以往明显不一样。因为客观环境影响大厂校招和社招基本都转成了全程线上进行流程一般是简历筛选、在线笔试、两到四轮技术面、主管面最后是HR面。笔试从线下纸质答题换成了牛客网、赛码网这类在线平台摄像头监考有的公司还会要求开屏幕共享现场写代码。线上面试对候选人来说有利有弊。好处是省去了异地奔波的成本面试场次可以安排得比较密集我最多的时候一天面了三家。坏处是网络和设备不可控尤其屏幕共享手写代码时如果IDE突然报错或者网络卡顿整个节奏都会被打乱。后来我学乖了每次线上面试前都会提前检查本地JDK版本、Maven仓库、IDE编译设置确保打开就能写代码。有一个小技巧关闭IDE里所有不相关的弹窗和插件更新提示避免写代码时突然弹出影响观感。1.2 我理解的考察链路大厂Java面试的考察链路其实高度统一Java基础、集合容器、JVM、并发、Spring框架、分布式、项目场景基本按照这个顺序从浅入深。第一轮技术面通常在Java基础、集合、并发和JVM上展开第二轮开始聊项目、分布式、系统设计主管面更关注项目深挖、解决问题能力和业务理解。这也解释了为什么八股文在大厂面试里地位这么高。很多人反感背八股文但如果把这些知识点理解透了它们其实就是一张完整知识网的纲目。关键问题在于怎么背。我的做法是每个知识点都问自己一个为什么比如HashMap为什么阈值是0.75ConcurrentHashMap为什么在Java 8里改了锁粒度Spring为什么推荐构造器注入。这些为什么才是面试官真正想听到的东西也是区分背书和真懂的分水岭。2. 笔试环节算法与基础选择2.1 牛客网在线笔试经验在线笔试是很多人的第一道坎。我归纳了自己2020年参加过的笔试题型比例大概是选择题30%到40%算法编程题两到三道剩下的是简答或设计题。选择题考得特别细经常出现以下哪个选项是正确的这种组合型题目覆盖Java基础、集合、并发、JVM、Spring等稍不注意就掉坑。笔试经验有三条。第一时间分配要提前规划。编程题分值最高但耗时最长我会先快速过一遍选择题拿不准的标记跳过把大块时间留给编程题。第二要提前适应牛客网的环境。在线IDE没有智能补全很多平时依赖IDEA的人写起代码来明显变慢建议在正式笔试前用牛客网的模拟环境做几道题热身顺便把输入输出模板练熟。第三重点注意输入输出格式。大厂笔试的用例经常是循环读取、多组输入字符串要自己split和解析不少人的算法明明对了却卡在输入输出上特别可惜。2.2 手写排序算法冒泡与快排排序算法是笔试和面试手写代码的重灾区。冒泡排序考逻辑快速排序考分治思想面试官还会追问时间复杂度、空间复杂度、稳定性以及优化方案。我2020年面试时被要求手写过三次冒泡、两次快排这个频率足够说明它的重要性。先看冒泡排序的Java实现public static void bubbleSort(int[] arr) { if (arr null || arr.length 2) { return; } int n arr.length; for (int i 0; i n - 1; i) { boolean swapped false; for (int j 0; j n - 1 - i; j) { if (arr[j] arr[j 1]) { int tmp arr[j]; arr[j] arr[j 1]; arr[j 1] tmp; swapped true; } } if (!swapped) { break; } } }里面有一个很重要的优化点swapped标志位。如果某一轮扫描没有任何交换说明数组已经有序直接退出。这个点面试官经常追问能主动写出来并解释原因说明你真的理解了冒泡排序而不是死记代码。快速排序面试频率更高核心是分区函数public static 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 static int partition(int[] arr, int left, int right) { int base arr[left]; int i left; int j right; while (i j) { while (i j arr[j] base) { j--; } while (i j arr[i] base) { i; } if (i j) { int tmp arr[i]; arr[i] arr[j]; arr[j] tmp; } } arr[left] arr[i]; arr[i] base; return i; }快排的高频追问点有几个为什么最坏情况是O(n^2)因为如果每次选的基准值都是最大或最小元素分区极不平衡退化成和选择排序一样。怎么优化三数取中或者随机选基准。还有一个容易忽略的细节分区时对相等元素的处理。上面代码用和目的是把相等的元素均匀分到两边避免出现无限递归或死循环。这些细节能答上来面试官会认为你有过真实的代码实践。笔试算法题不止排序。链表反转、二叉树层序遍历、滑动窗口、动态规划里的爬楼梯和背包都是常客。我的体会是笔试不需要追求难题偏题中等难度以下的题目要吃透尤其是原题要能快速写出。因为笔试时间紧考察的就是熟练度。2.3 选择题里反复出现的Java基础笔试选择题中Java基础占比很高而且题目出得非常细。我整理了2020年笔试里反复出现的几类运算符优先级和表达式求值。面向对象特性、重载与重写的区别。集合类的线程安全性。字符串常量池与String、StringBuilder、StringBuffer。异常体系、数组越界。枚举类型、Lambda表达式。运算符和表达式看起来基础但笔试的坑特别多。比如i和i的求值顺序和equals的区别String的拼接到底走了什么逻辑。有一道高频题我印象很深Integer a 127; Integer b 127; a b结果是true因为Integer缓存范围是-128到127但把值改成128结果就是false。这种题如果没看过源码现场只能靠猜。枚举类型也是选择题常客。比如enum能不能继承别的类不能因为枚举默认继承java.lang.Enum。枚举能不能实现接口可以。枚举的构造方法能不能是public不能必须是私有。还有个冷门考点values()和valueOf()是编译器自动生成的静态方法而不是Enum类自带的。这些点平时写业务代码根本遇不到但笔试选择题就是喜欢考。Lambda表达式在2020年面试里越来越常见因为Java 8已经普及多年。面试官会问Lambda的本质、函数式接口、方法引用、stream流的中间操作与终止操作。笔试里常见的是让你写出filter、map、collect的调用结果。我建议把java.util.function包里的核心接口都过一遍包括Function、Predicate、Consumer、Supplier以及方法引用的四种形式这样现场不会反应不过来。对了排序相关的Comparator.comparing在笔试和面试手写题里很实用。比如把List按某个字段降序排序可以写list.sort(Comparator.comparing(User::getAge).reversed())。如果想把某个特定值排在最前面可以用Comparator.comparing(u - u.getStatus() 1 ? 0 : 1).thenComparing(User::getAge)。这个写法在技术社群里被问过很多次不算难但思路要清晰。3. 面试重头戏Java基础与八股文3.1 面向对象三特性怎么回答不落俗套面试官问讲讲面向对象时大部分人都会先背封装、继承、多态六个字然后就接不下去了。这种回答很容易被追问到死角。我的答法是先给定义再给例子最后讲底层限制把主动权拿在自己手里。封装把对象的属性和行为绑定在一起对外暴露必要的方法隐藏内部实现。好处是降低模块间耦合便于维护。比如private字段配public的getter/setter就是最基本的封装。这里可以顺势提到访问控制符的作用范围让回答更完整。继承子类复用父类的属性和方法体现is-a关系。注意Java是单继承但接口可以多实现。面试官通常会追问为什么Java不支持多继承答案是菱形继承问题如果多个父类有同名方法子类无法确定调用哪个。另外还要补充子类构造器会隐式调用父类无参构造器这也是一个常考点。多态同一操作作用于不同对象产生不同结果。多态实现的基础是继承、方法重写和向上转型。Java的方法调用默认是动态分派所以运行时才决定实际调用哪个方法。讲到这层可以自然引出JVM的方法表和invokevirtual指令很多面试官到这里就会满意地点头。我在面试中还遇到过重载和重写的区别。重载发生在编译期方法名相同、参数列表不同看的是静态类型重写发生在运行期子类覆盖父类方法要求方法签名相同、访问权限不能更严格、返回值可以协变。额外注意重写时子类抛出的异常不能比父类更宽泛static方法不能重写只能隐藏。3.2 细节题运算符、表达式、枚举、Lambda第一轮面试经常出一些看似简单的细节题其实是在试探基本功。比如运算符和的区别、|和||的区别。有短路效果左边为false时右边不会执行没有短路两边都会执行。|和||同理。还有一个容易混淆的点equals和hashCode的约定以及位运算在HashMap源码中的应用比如用(n - 1) hash计算数组下标。这些细节题和源码题经常混在一起想答好必须自己看过源码。枚举的考察点前面提过一些。我补一个实用场景用枚举实现单例模式。这是《Effective Java》作者推荐的写法因为枚举天然保证线程安全并且反射无法破坏。如果面试时你能主动提出这个点会被明显加分。笔试中还出现过枚举不能继承类但可以实现接口这类判断题处理起来也很简单记住枚举的基类是java.lang.Enum就够了。Lambda的考察点不能停留在语法层面。面试官问Lambda是怎么实现的时要能回答出函数式接口加invokedynamic这层。Java 8的Lambda表达式经过编译后会生成一个静态方法和invokedynamic指令运行期通过LambdaMetafactory生成函数式接口的实例。能讲到这里说明你读过字节码或源码而不是只会写箭头语法。我建议有时间可以用javap反编译一个Lambda方法看看字节码面试时的底气完全不一样。3.3 集合容器的底层逻辑集合这块面试官特别喜欢问为什么有的集合线程不安全、有的安全以及底层数据结构是什么。我把高频考点按容器列了一遍ArrayList底层是Object数组默认容量10扩容后变成原来的1.5倍。查询快、增删慢线程不安全。LinkedList底层是双向链表增删快、查询慢随机访问需要遍历。HashMap底层是数组加链表加红黑树初始容量16负载因子0.75链表长度超过8且数组长度大于等于64时转红黑树扩容为原来的两倍。ConcurrentHashMapJava 8是用CAS加synchronized保证线程安全锁粒度精确到桶比Java 7的分段锁并发度更高。HashSet底层就是HashMapvalue统一为同一个Object对象。LinkedHashMap在HashMap基础上维护了双向链表适合实现LRU缓存。HashMap是当之无愧的第一高频考点。我2020年面试的每一家公司都问了HashMap而且至少追问三层。第一层是底层结构和put流程put大概分四步计算hash、定位桶、判断是否哈希冲突、冲突后判断是链表还是红黑树。第二层是为什么负载因子是0.75这是时间和空间的折中太高会增加冲突太低会浪费空间源码注释里给出了基于泊松分布的统计依据。第三层是为什么链表转红黑树的阈值是8因为随机哈希下链表长度到达8的概率极低大概千万分之六如果真到了8说明哈希函数可能有问题这时用红黑树来抵御极端冲突。把这几个为什么都答下来HashMap这一关才算真正过了。4. JVM与并发拉开差距的深水区4.1 内存结构、GC和类加载JVM是2020年大厂面试的必考板块被问到的概率高得惊人。核心就三大块内存结构、垃圾回收、类加载。内存结构要能画出六个区域程序计数器、虚拟机栈、本地方法栈、堆、方法区Java 8以后改为元空间、运行时常量池。特别要区分线程私有和线程共享程序计数器、虚拟机栈、本地方法栈是线程私有堆和方法区是线程共享。面试官喜欢追问栈上对象会不会被GC元空间会不会OOM这类问题答的时候要明确栈上一般没有GC但栈帧会随着方法结束弹出元空间用本地内存如果动态生成类过多一样会OOM。垃圾回收的核心是判断对象是否已死。引用计数法实现简单但有循环引用问题所以主流JVM用可达性分析。从GC Roots出发遍历不可达对象可回收。GC Roots包括虚拟机栈中的引用、静态属性引用、常量引用、本地方法栈中的引用。分代收集的思路也要会讲新生代Eden加两个Survivor用复制算法老年代用标记整理或标记清除。常见的收集器CMS和G1要能说清楚适用场景和优缺点尤其G1的目标是把STW时间控制在可预测范围内。类加载机制同样是高频题。双亲委派模型要讲透一个类加载器收到类加载请求时先不自己加载而是把请求委托给父类加载器最终由Bootstrap ClassLoader处理父类加载不了才由子类加载器自己加载。好处是避免相同类被重复加载也防止核心类库被篡改。面试官常追问什么场景需要自定义类加载器比如热部署、Tomcat的Web应用隔离、OSGi模块化等。能答出打破双亲委派的实践场景比如JDBC用SPI加载驱动会让回答更完整。4.2 OutOfMemoryError排查实战要说2020年面试里最常被问到的异常OutOfMemoryError一定排在前列而且很多公司要求你结合项目讲一次真实的OOM排查经历。先区分一个常见误区。IDE里运行Spring Boot项目时IDEA偶尔会提示java: OutOfMemoryError: Insufficient memory这个错误其实是IDE编译器或Maven构建进程的堆内存不足不一定是应用本身的JVM堆不够所以解决办法是调整IDEA自身的内存设置而不是改-Xmx。面试时如果把这个和Java堆OOM混为一谈容易被面试官当场纠正。项目里真正出现的OOM主要有这几种报错类型常见原因排查方向Java heap space对象堆积、大对象过多jmap -histoMAT分析Metaspace动态生成类过多检查CGLIB代理、反射调用unable to create new native thread线程数超过系统限制查看线程栈调整系统线程数配置GC overhead limit exceededGC频繁但回收效果差观察GC日志优化堆大小或代码讲排查经历时我一般按这个套路先看异常类型再用jps找到进程ID用jmap -heap看堆使用情况用jstat -gcutil观察GC频率最后用jmap -dump:formatb,fileheap.bin导出堆转储用MAT分析大对象和对象引用链。这套流程我在项目里实际操作过面试时讲出来很有说服力。我有一个真实的案例。一个定时任务每天凌晨跑运行两周后突然OOM。第一次处理时我只是把-Xmx调大了结果只是延长了崩溃时间并没有解决问题。后来我在崩溃前导出了堆转储用MAT打开后发现问题出在一个缓存Map上任务不断向Map里放数据但清理线程因为锁竞争没有及时执行Map越滚越大。定位到根因后把缓存换成带过期时间的本地缓存问题才彻底解决。这个案例让我记住两件事。第一调大堆内存是应急不是根因OOM一定要找到谁占用了内存。第二排查顺序可以先用jmap -histo观察类实例数量快速锁定可疑对象不一定每次都要直接上MAT。4.3 并发编程高频考点并发是Java后端面试的另一个深水区。2020年大厂问到的并发题集中在这些类synchronized和ReentrantLock的区别、volatile能保证什么、CAS是什么且有啥缺点、线程池核心参数和执行流程、ThreadLocal原理和内存泄漏、CountDownLatch和CyclicBarrier的区别。先说synchronized和ReentrantLock。synchronized是JVM层面的关键字ReentrantLock是JDK层面的API。Java 6之后synchronized引入了偏向锁、轻量级锁、重量级锁的升级过程性能并不差。ReentrantLock的优势在于支持可中断、支持公平锁、可以绑定多个Condition。但多数业务场景能用synchronized就用synchronized代码简洁不容易出问题。volatile是最容易被讲错的点。它保证可见性和有序性但不保证原子性。volatile变量每次读取都从主内存读取写入后立即刷新到主内存并通过内存屏障禁止指令重排序。经典应用是双重检查锁定的单例模式单例字段必须要加volatile否则会发生指令重排序导致某个线程拿到未初始化完成的对象。线程池是面试官最爱问的项目场景题。核心参数有七个核心线程数、最大线程数、空闲存活时间、时间单位、工作队列、线程工厂、拒绝策略。执行流程要能完整说下来提交任务时先判断核心线程是否满没满就创建新线程执行满了放队列队列满了并且核心线程也满就创建非核心线程最大线程数也满了走拒绝策略。拒绝策略有四种AbortPolicy抛异常、CallerRunsPolicy由调用者执行、DiscardPolicy直接丢弃、DiscardOldestPolicy丢弃最老的任务。面试官还会追问线程池参数怎么设置。这里可以按任务类型区分CPU密集型任务的核心线程数可以设置为CPU核数加1因为很少阻塞IO密集型任务由于有大量等待核心线程数可以设置为CPU核数的两倍甚至更多。这类实战问题如果能结合自己项目的压测情况讲面试官会更认可。5. 框架与项目从八股到场景题5.1 Spring核心设计思想Spring在2020年Java大厂面试中也是必考高频题包括IOC和AOP的理解、Bean的生命周期、Spring事务的传播行为与失效场景、Spring Boot的自动配置原理。IOC控制反转可以从谁控制谁讲起。传统开发中对象由自己new控制权在程序员IOC把对象的创建和管理交给容器控制权反转了我们只需要声明依赖。DI依赖注入是IOC的一种实现方式有构造器注入、Setter注入、字段注入三种推荐构造器注入因为它能保证依赖不为空且不可变也方便单元测试。AOP的核心是动态代理。Spring AOP默认对接口使用JDK动态代理对类使用CGLIB代理。从Spring Boot 2.x开始spring.aop.proxy-target-class默认为true即默认使用CGLIB。AOP的典型场景是日志、事务、权限校验等横切逻辑。这里可以提一句aop失效的常见原因是类内部调用因为内部调用的this对象不是代理对象。Bean生命周期是面试官喜欢画图追问的题目。简化版是实例化、属性填充、初始化InitializingBean、PostConstruct、使用、销毁DisposableBean、PreDestroy。完整版要提到BeanPostProcessor它是Spring扩展机制的核心AOP就是通过BeanPostProcessor在Bean初始化后创建代理对象的。能提到BeanPostProcessor说明你看过Spring源码。Spring事务的失效场景是项目经验的试金石。方法内部自调用、方法不是public、异常被catch住没向上抛、抛出了检查异常而事务默认只回滚RuntimeException这些都是经典案例。面试官问这些其实是想确认你在真实项目里写过事务代码而不是只背过注解。5.2 分布式与微服务场景题到了第二轮或主管面分布式是绕不开的话题。高频考点有分布式锁实现Redis、Zookeeper、分布式事务2PC、TCC、消息最终一致性、Seata、服务降级和熔断限流、消息队列可靠性、Redis缓存穿透击穿雪崩。2020年大厂特别爱考缓存三大问题。缓存穿透是查询一个不存在的数据请求直接打到数据库解决思路是布隆
返回列表