资讯动态

Java面试八股文100题:基础、集合、并发与JVM全解析

发布时间:2026/8/30 5:48:39 来源:尧图企业网站定制
八股文这个词在Java面试圈里被骂了很久但骂归骂该背还是得背。我这些年既当过面试官也帮不少朋友做过模拟面试一个很真实的感受是八股文不是面试的全部但它是一块敲门砖——基础题答得稀碎面试官很难相信你能写出靠谱的并发代码或调优方案。这份100道的整理不是让你死记硬背而是帮你在面试前把知识体系里的关键节点全部过一遍。说下我整理这套题的原则覆盖Java基础、集合、并发、JVM、Spring、MySQL、Redis、消息队列等面试必问模块每道题都标注了“为什么问”和“追问点”因为面试官很少只问一道题他一定是从一个点开始一步步往下深挖直到挖到你的知识边界。你只有把每个点背后的原理链条打通才算真正掌握了这道题。1. 为什么Java面试绕不开八股文先搞清楚面试官到底在考什么1.1 八股文不是背答案而是对知识体系的抽检很多人把八股文理解成“死记硬背的面试题”这个看法其实低估了它的作用。面试官问你“HashMap底层实现”表面上在考你数据结构实际上是在抽检你的知识体系是否完整如果你能答出数组加链表、红黑树、扩容机制、负载因子那说明你对数据结构和源码有过系统研究如果你还能顺手说出JDK 1.7和1.8在头插尾插上的变化以及为什么变化那说明你不仅看了源码还理解了并发场景下的设计取舍。所以在整理这100道题时我没有把答案写成“标准答案”而是按照“一句话结论 原理拆解 延伸追问”的结构来组织。一句话结论用来快速回答原理拆解展示深度延伸追问则是模拟真实面试时面试官的连环炮。1.2 从热搜词看面试风向这些高频考点背后有规律我翻了最近几个月Java相关的搜索热词出现频率最高的几类包括Java基础、Java面试题、面向对象编程、集合类、并发编程、JVM调优、Spring、MySQL、Kafka、Redis。这些词看起来分散背后其实有一条清晰的逻辑线基础题考察的是“语言功底扎不扎实”比如String、集合、异常、泛型、反射。并发和JVM考察的是“能不能写出高性能、高可用的代码”这是中高级工程师的分水岭。框架和中间件考察的是“有没有真正的项目经验”Spring IoC/AOP、Redis缓存、Kafka高并发这些都需要在实战中理解。基于这个规律这套100道题的模块权重我也做了分配Java基础约25道集合约15道并发约15道JVM约10道Spring约10道MySQL与Redis约10道消息队列与分布式约10道再加5道场景题和开放性题目。这不是随意定的而是根据大厂面试题型的出现频率统计出来的。下面我在每个模块里挑几道最有代表性的题完整拆解一遍你可以直接拿去用。1.3 100道题的正确打开方式拿到题单后不要从第一道开始挨着背那样效率很低。我的建议是先按模块做一次“自测”看到题目先在心里组织答案30秒内说不出核心结论的题就是你的薄弱点优先补。能说出一句话结论但说不清原理的属于“半懂”需要重点看原理拆解部分。能完整讲清楚而且能答出追问的直接跳过。这套题还有个用法是“互相提问式复习”找一个小伙伴轮流当面试官严格按照“一个知识点问到底”的节奏来这比一个人对着题单背效果好得多。2. Java基础高频题实战拆解从String到集合从equals到异常2.1 String、StringBuffer、StringBuilder一道题背后的三张内存图这道题几乎100%出现在Java基础面试中但大多数人只背了“String不可变、StringBuffer线程安全、StringBuilder线程不安全”这个结论一到追问就露馅。面试官真正想听的逻辑是String为什么不可变因为它的内部是用final char数组JDK 9之后是byte数组存储的而且类本身也是final的没有提供任何修改内部字符数组的方法。所以你每次对String做拼接实际上是创建了一个新的String对象而不是修改原来的对象。这就是为什么大量字符串拼接时String的性能远低于另外两个。那StringBuffer和StringBuilder的区别在哪关键看它们的方法上有没有synchronized关键字。StringBuffer的append、insert等方法都加了synchronized所以多线程环境下是安全的但代价是性能下降。StringBuilder没有加锁单线程下拼接效率最高。面试时如果能把这三者的适用场景一起说出来——String用于少量字符串或字符串常量StringBuilder用于单线程大量拼接StringBuffer用于多线程少量操作——那这道题就稳了。另外还有一个高频追问字符串常量池。字符串字面量会直接放进常量池使用new String(abc)则会创建两个对象一个是常量池中的abc一个是堆中的String对象。这个知识点最好配合代码示例来看String s1 abc; String s2 abc; String s3 new String(abc); System.out.println(s1 s2); // true同一个常量池对象 System.out.println(s1 s3); // false堆中的新对象 System.out.println(s1.equals(s3)); // trueequals比较的是内容建议把这段代码背下来并能画一下内存图这题基本就没问题了。2.2 equals与hashCode为什么重写equals必须重写hashCode这题考的是对Object类设计的理解。面试官会先问equals和的区别这个是送分题比较的是引用地址equals默认也是比较地址但重写后比较内容。接着会追问为什么重写equals时一定要重写hashCode这里要答出hashCode的“契约”两个对象equals相等则hashCode必须相等两个对象hashCode相等equals不一定相等。如果不重写hashCode会出现什么后果看这段代码MapPerson, String map new HashMap(); map.put(new Person(张三), value); String result map.get(new Person(张三)); // 如果不重写hashCode这里返回null原因在于HashMap查找时先算key的hashCode定位到桶再在这个桶里用equals找目标。创建两个Person对象不重写hashCode它们的hashCode大概率不同get的时候会定位到不同的桶自然找不到。这个场景面试官特别喜欢问因为它把equals/hashCode和集合原理串起来了。日常开发中用Lombok的Data注解可以直接生成equals和hashCode但要注意Set去重、Map的key这些场景下如果业务对象参与哈希运算一定要确认equals和hashCode的实现是完整且一致的。2.3 ArrayList与LinkedList别只背“数组vs链表”这道题是集合模块的送分题人人都知道ArrayList底层是数组、LinkedList底层是双向链表但面试官稍微追问一下水平差距就出来了。先说核心结论ArrayList随机访问快O(1)插入删除慢因为需要移动元素LinkedList插入删除快但随机访问慢O(n)因为需要遍历。这个结论本身没错但要加一个前提LinkedList的插入删除快指的是在已知节点位置的情况下插入删除比如使用迭代器在遍历过程中删除。如果只是调add(index, element)或remove(index)LinkedList依然需要先遍历到指定位置时间复杂度也是O(n)。还有一个经常被忽视的点内存占用。ArrayList是连续内存每个元素只存对象引用LinkedList每个节点除了存储数据还要维护pre和next两个引用内存开销明显更大。所以数据量大且随机访问频繁的场景ArrayList是绝对首选。高频追问里还有一个“扩容机制”ArrayList默认容量10添加元素超过容量时会按旧容量的1.5倍扩容也就是newCapacity oldCapacity (oldCapacity 1)。扩容需要复制旧数组到新数组这个操作很消耗性能所以如果可以预判数据量最好在构造时指定初始容量避免频繁扩容。这也是“ArrayList构造器传初始容量”这条最佳实践的底层原因。2.4 异常体系受检异常与运行时异常的分工逻辑异常题看起来简单但不少人连“受检异常和运行时异常的区别”都说不利索。面试官问这题是想确认你写的代码是否具备“防御性思维”。核心要点有三个第一Throwable是所有异常和错误的父类下面分Error和Exception两类。Error是JVM层面的严重问题比如OutOfMemoryError、StackOverflowError这些不用抓也抓不住程序已经崩了。第二Exception分为受检异常checked和运行时异常unchecked。受检异常比如IOException、SQLException编译器强制要求你处理要么try-catch要么throws声明。运行时异常比如NullPointerException、ArrayIndexOutOfBoundsException编译器不强制但运行时一旦发生程序就会中断。第三面试追问常问“为什么Java要区分受检异常和运行时异常”答案是受检异常是“可预期的外部故障”比如文件不存在、网络断开应该强制调用方处理运行时异常是“程序逻辑缺陷”比如数组越界、空指针更应该通过修复代码来避免而不是通过catch掩盖。这里有个加分技巧给面试官举例说明工作中怎么处理异常。比如调用外部RPC接口时要对超时和网络异常做try-catch并降级但捕获NullPointerException这种运行时异常就不是好习惯说明代码的判空逻辑有问题。3. 并发编程八股文里的“重灾区”也是区分度的分水岭3.1 volatile与synchronized可见性、原子性、有序性一次说清并发三要素——原子性、可见性、有序性是并发面试的地基。面试官经常用一道题开场“volatile能保证原子性吗”很多人脱口而出“不能”但你要能说清楚为什么不能这题才算答完。volatile保证的是可见性和有序性不保证原子性。什么叫可见性就是当一个线程修改了volatile变量的值新值会立即刷新到主内存其他线程读到的都是最新值。它通过内存屏障禁止指令重排这是有序性。但为什么不能保证原子性因为像count这种操作底层是“读-改-写”三步volatile管不了三步之间的并发竞争。比如两个线程同时执行count读到的都是1写回的都是2最终值丢失了一次更新。synchronized则是通过加锁同时保证了原子性、可见性和有序性代价是性能损耗和可能的线程阻塞。所以面试官问到“什么时候用volatile”你可以回答适合一写多读的场景比如状态标志位的开关volatile boolean running true; // 线程A running false; // 线程B while (running) { // 继续执行 }这种场景不需要原子性只要保证B线程能立刻感知A线程的修改volatile完美胜任。把这段代码背下来配合“为什么不能保证原子性”的解释整道题就非常完整了。3.2 ConcurrentHashMap从分段锁到CAS扩容的演进思路ConcurrentHashMap几乎是并发集合面试的代名词。这道题要拿高分需要先捋清楚JDK 1.7和1.8的架构差异。JDK 1.7的ConcurrentHashMap采用Segment分段锁机制把整个Map分成16个Segment每个Segment是一把独立的锁不同线程操作不同Segment时可以并发进行提高并发度。但问题在于如果操作同一个Segment内的数据依然会锁竞争而且Segment的个数在初始化后不能扩容并发度固定为16。JDK 1.8放弃了Segment方案直接用Node数组加链表/红黑树并使用CAS操作加synchronized来保证线程安全。细节点在于当某个桶为空时用CAS直接放入新节点不加锁当桶内已有节点时对桶的头节点加synchronized锁再进行插入或更新。这样锁的粒度从Segment降到了单个桶并发度大幅提升。同时引入了红黑树当链表长度超过8且数组长度超过64时链表转化为红黑树避免哈希冲突严重时查询退化为O(n)。面试官常追问的一个点是“为什么链表转红黑树的阈值是8”。这个和泊松分布有关在随机哈希函数下链表节点数达到8的概率极低是百万分之六左右。如果链表长度真的到了8说明哈希函数出现了严重问题或遭遇了恶意哈希碰撞攻击此时应该用红黑树来兜底。3.3 线程池七大核心参数的执行链路线程池是并发模块里最容易考“细节”的题目。面试官会让你说出ThreadPoolExecutor构造方法的七个参数然后问你“线程池的提交任务流程”。七大参数分别是核心线程数corePoolSize、最大线程数maximumPoolSize、空闲线程存活时间keepAliveTime、时间单位unit、任务队列workQueue、线程工厂threadFactory、拒绝策略handler。这些都必须能流利说出没有商量余地。提交任务的执行链路要按这个顺序讲当线程池收到新任务时如果当前工作线程数小于corePoolSize直接创建核心线程执行任务如果已等于corePoolSize则把任务放入workQueue队列如果队列也满了且工作线程数小于maximumPoolSize则创建非核心线程执行任务如果线程数已达最大值且队列也满了则执行拒绝策略。拒绝策略有四种AbortPolicy直接抛异常、CallerRunsPolicy让提交任务的线程执行、DiscardPolicy静默丢弃、DiscardOldestPolicy丢弃队列中最旧的任务。面试中还有一个高频追问“核心线程会被回收吗”这题要分情况默认情况下核心线程即使空闲了也会一直存活不会被回收。但如果你用allowCoreThreadTimeOut(true)方法设置了允许核心线程超时那么核心线程在空闲超过keepAliveTime后也会被回收。很多有经验的人会在这个点上主动提出“生产环境如果任务波动大可以设置allowCoreThreadTimeOut避免大量空闲线程占用资源”这句话是加分项说明你不只会背参数还知道怎么用。4. JVM与性能调优OOM、类加载、Lombok报错背后的同一套逻辑4.1 运行时数据区域哪些异常分别在哪些区发生JVM运行时数据区是JVM面试的起点。需要牢记的五个核心区域是程序计数器、虚拟机栈、本地方法栈、堆、方法区JDK 8之后用元空间实现。程序计数器是唯一不会发生OutOfMemoryError的区域虚拟机栈和本地方法栈发生StackOverflowError或OutOfMemoryError堆和方法区都会发生OutOfMemoryError。这些异常和区域要能对号入座。面试官非常喜欢用表格考察这个知识点所以把对应关系整理清楚很关键JVM区域数据类型异常典型场景程序计数器线程私有无字节码行号指示器虚拟机栈线程私有StackOverflowError / OOM递归调用过深本地方法栈线程私有StackOverflowError / OOMnative方法调用过深堆线程共享OOM: Java heap space创建大量对象无法回收方法区/元空间线程共享OOM: Metaspace加载大量类如热部署把这张表刻在脑子里答JVM题时随时可以引用。4.2 类加载机制从“源发行版17需要目标发行版17”说到双亲委派JVM类加载的经典题是双亲委派模型但要答出新意可以从一个实际报错切入。很多人在新电脑上首次运行项目时会遇到“java: 警告: 源发行版17需要目标发行版17”或者“Error: java: invalid source release: 17”这类问题这看起来是个环境配置问题本质上是编译器版本与运行时版本不一致。IDE里的Project SDK指定了Java 17但编译选项里的target依然指向旧版本或者Maven的compiler插件没有配置maven.compiler.source/target。这类问题在面试中不太会直接问但理解“源代码编译时需要同时指定source和target版本”这个逻辑能体现你对Java版本机制的熟悉度。回到双亲委派模型当一个类加载器需要加载一个类时它不会自己先去加载而是先把这个请求委派给父类加载器每一层的加载器都如此只有当父类加载器反馈自己无法加载时子加载器才会尝试自己加载。这种模型的好处有两个一是防止核心类库被篡改比如你写了一个java.lang.StringApplication ClassLoader会把加载请求一直委派到Bootstrap ClassLoader发现已经有rt.jar中的String了就直接返回不会加载你写的那个二是避免类被重复加载。高频追问是“怎么打破双亲委派模型”常见的答案是通过继承ClassLoader并重写loadClass方法来实现。典型场景包括Tomcat的WebAppClassLoader为了支持Web应用使用不同版本的类库以及JDBC里用SPIService Provider Interface机制加载驱动。4.3 OOM实战insufficient memory这类报错的定位思路搜索结果里出现了一个很常见的报错关键字java.lang.OutOfMemoryError: insufficient memory。很多人一看到就慌实际上这类报错的本质是JVM申请不到足够的内存了可能是堆内存不足也可能是元空间内存不足甚至可能是操作系统级别的内存耗尽。排查思路我建议按照“三看”来做一看JVM启动参数确认-Xmx和-Xms是否正确设置了预期堆大小二看GC日志确认是否出现频繁Full GC或GC回收不掉的大对象如果Full GC之后内存依然居高不下大概率是堆泄漏三看线程栈和堆快照用jmap命令dump出堆文件再用MATMemory Analyzer Tool分析大对象和引用链。如果是真正的高并发场景下出现insufficient memory还要注意一个容易被忽略的点JVM不仅使用堆内存还包含线程栈、元空间、直接内存Direct Memory。Netty这种NIO框架会大量使用堆外内存如果堆内存设置了很大但堆外内存没有限制最终触发的OOM信息可能显示的是系统物理内存不足。所以排查OOM时不要只盯着-Xmx还要关注-XX:MaxDirectMemorySize和-XX:MaxMetaspaceSize。4.4 其他高频JVM题垃圾回收算法与收集器JVM模块里还有一类必问题垃圾回收算法和垃圾收集器。Minor GC和Full GC分别在什么时候触发引用计数法和可达性分析的区别CMS和G1各自的特点是什么这里说一个容易被问到但不常答好的点G1收集器的region分区和可预测停顿模型。G1把堆拆分成多个大小相等的Region优先回收垃圾最多的Region通过维护一个优先列表实现“在限定时间内完成垃圾回收”的目标。JDK 9之后G1成了默认收集器面试官问到这里通常会接着问“G1和CMS的区别”重点答题思路是CMS用标记清除算法会产生内存碎片G1用复制算法在Region之间移动对象不会产生碎片而且能指定停顿时间。5. 框架与中间件场景题Kafka为什么能支撑百万并发5.1 Spring的IoC与AOP别只会背定义Spring面试题里IoC和AOP是绕不开的老两样但很多人只会背“IoC是控制反转AOP是面向切面编程”这种答案最多只能拿一半分。IoC要能说清楚传统开发中对象由自己new出来依赖关系由自己维护IoC把对象的创建和依赖注入交给Spring容器你只需要声明Bean和依赖关系容器负责在合适的时机实例化并注入。关键扩展点是Bean的生命周期实例化、属性填充、初始化InitializingBean、PostConstruct、使用、销毁DisposableBean、PreDestroy这套生命周期贯穿于Spring的底层设计。AOP要能说清楚它的核心概念切面Aspect、切入点Pointcut、通知Advice、连接点JoinPoint。它的底层原理要看代理对象如果是接口Spring用JDK动态代理基于Proxy类和InvocationHandler如果没有接口Spring用CGLIB代理通过生成子类来代理。面试时最好能主动说出“Transactional实际上就是基于AOP实现的”因为事务管理器本身就是Spring AOP最典型的应用。这个扩展能体现你对Spring底层机制的理解而不仅仅是背概念。5.2 Kafka为什么能支撑百万并发热搜词里有“kafka 八股文为什么能支撑百万并发”这是一道常考的消息队列设计题。要答好它需要从Kafka的核心架构说起。Kafka的高性能来自几个设计顺序写磁盘。Kafka的消息持久化不是随机写而是追加到日志文件末尾顺序写的速度可以接近内存随机读的速度所以很多人以为Kafka用内存缓存其实它主要是靠顺序写磁盘加页缓存page cache。分区并行。一个Topic可以分成多个Partition每个Partition可由不同消费者并行消费生产端也可以往不同分区并发写入极大地提升了吞吐量。零拷贝技术。Kafka用sendfile系统调用数据直接从磁盘读到内核缓冲区再通过socket发送出去省去了用户态和内核态之间两次拷贝消费效率大幅提升。批量与压缩。生产者把多条消息打包成一个批次发送并支持gzip、snappy等压缩算法减少网络传输量。面试官追问最多的是“Kafka如何保证消息不丢失”。这需要从生产端、Broker端、消费端三个维度回答生产端设置acksall等待所有副本都确认写入Broker端设置min.insync.replicas并禁用unclean.leader.election避免选举出没有全部数据的副本作为leader消费端在消息真正处理完成后再提交offset以免处理失败却提交了偏移量导致消息丢失。这个回答可以立刻展现出你的“生产可用性”意识比单背名词强得多。5.3 Redis缓存穿透、击穿、雪崩三兄弟一次讲清楚Redis在面试中出现频率极高其中缓存穿透、缓存击穿、缓存雪崩这三道题几乎是必问题。它们长得很像但机制和解决方案完全不同。缓存穿透是指查询一个不存在的数据缓存和数据库都没有请求直接打到数据库上。如果是恶意攻击者利用这个特点并发请求大量不存在的key数据库会瞬间被打垮。解决方案有三把空值也缓存起来设置较短的过期时间用布隆过滤器在缓存前拦截一定不存在的key最简单的方案是参数校验对明显不合理的查询直接拒绝。缓存击穿是指一个热点key在失效的瞬间大量并发请求同时打到数据库上。解决思路是“热点key永不过期”或者“互斥锁重建缓存”互斥锁的实现是当缓存失效时只让一个线程去查询数据库并重建缓存其他线程等待缓存重建完成后再读取。缓存雪崩是指大量key在同一时间段集中过期或者Redis服务宕机导致大量请求全部打到数据库。解决方案包括过期时间加随机值避免集中失效Redis集群高可用部署多级缓存架构比如本地缓存配合Redis服务降级和限流。这三个问题最好在回答时用“分别是什么、为什么发生、解决思路是什么”的结构尤其是第三问面试官一般不会满足于只听到一个解决方案多准备一两个备选方案很有必要。6. 100道题怎么背才高效我的整理方法与避坑经验6.1 按“知识域-核心题-追问链”三层结构整理很多人的八股文笔记是平铺式的一百道题按顺序记在文档里每次复习都要从头刷到尾效率很低。我建议用三层结构来整理第一层是知识域也就是Java基础、集合、并发、JVM、框架、中间件这些大模块第二层是每个模块里的核心题通常是那些被问概率超过80%的题目第三层是追问链也就是这道题往下延伸的2到4个子问题。追问链的作用是模拟面试官的真实行为。比如HashMap这道题追问链可能是这样HashMap的put过程是什么为什么链表长度到8转红黑树为什么红黑树转链表的阈值是6HashMap为什么线程不安全ConcurrentHashMap怎么解决线程安全这样一条链下来一道题变成了五道题而这五道题在面试中确实可能被连环追问。整理笔记时就把这条链写在题目后面复习时按链来背比孤立地背单题效率高一倍以上。6.2 用“费曼法”自测讲不出原理就等于没学会八股文最大的坑是“感觉会了”。看答案时觉得自己都懂合上文档去面试一紧张就断片。破解办法只有一个字讲。每学一道题把自己当成讲师对着电脑或手机录音用讲课的方式把这道题完整讲出来讲完回放你会发现很多地方是含糊其辞的比如“反正就是这样”“差不多是那个意思吧”这些词一出现就是你知识盲区的信号。找到它回到笔记里补上再讲一遍直到能流畅完整地把原理说出来。这个方法最初用起来很花时间但效果立竿见影所有的知识点都会在脑子里留下牢固的印记。6.3 复习节奏和错题本人类记忆的衰减规律决定了只背一遍等于没背。我实践的复习节奏是当天整理完的题第二天早上过一遍第三天后继续背新题同时把错题本里标记“红”的题再过一遍一周后做一次模块总复习两周后做一次全量抽查。错题本建议单独建一个文档只记录答不完整或讲不清的题每题附一句“哪里卡住了”比如“记不住LinkedHashMap的accessOrder默认值”下次复习只需要看这些卡点不用整个题库重新看。6.4 三个最常见的误区第一只背不写。八股文虽然叫“文”但很多题是带代码的比如手写单例、手写快速排序、手写生产者消费者。只看不写面试时手写代码很容易卡壳。我的建议是每一道算法题、设计模式题都要在IDE或白纸上默写一遍不要复制粘贴。第二忽视追问。背了主答案但不准备追问等于只穿了一件外套迎接暴风雨面试官一层层改啊直接把你问垮。第三只看技术不看场景。面试官问“HashMap和Hashtable的区别”如果你能补上一句“所以在并发场景下应该用ConcurrentHashMap而不是Hashtable因为Hashtable给整张表加锁并发度太低”这个答案立刻从“及格”跳到“优秀”。另外想多说一句Java八股文更新速度很快。比如JDK 17之后虚拟线程、密封类、模式匹配这些新特性开始出现在面试中Lombok配合新版JDK的兼容问题、新版JDK源发行版配置等也成了高频踩坑点。所以这份100道题不是终点而是一个起点面试前一定要去官网或靠谱社区看看最新的特性变化补充自己的知识盲区。面试最终考的还是你解决问题的能力八股文只是把这个能力变成可被考察的形式而已。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价