简介:这是一份面向Java求职者与初中级开发者的高频面试知识点梳理文档,聚焦Spring体系与数据库优化两大面试主线,适合临考冲刺、系统复盘与查漏补缺。内容围绕Spring Bean生命周期、Spring MVC执行流程、Spring Boot自动装配原理展开,并延伸至SQL执行计划解读、常见优化技巧、MySQL索引类型与B+树底层结构,另附线程池种类、核心参数与拒绝策略等并发要点,知识点以条目化方式组织,便于快速定位与记忆。资源包共1个PDF文件,大小约396KB,单文件结构轻量,适合手机或电脑随时翻阅。目前已有180人学习下载,可作为面试前集中复习的便携资料,帮助读者串联框架原理与SQL调优思路,形成较完整的知识脉络。
1. 从一道“Bean 生命周期”题看这场面试到底考什么
很多人背 Spring 背到滚瓜烂熟,一进面试间被问“Bean 是怎么从一堆字节码变成一个能用的对象,又怎么被销毁的”,脑子里的流程图瞬间糊成一团。Java 高频面试题的真相是:面试官不考你背没背过概念,考的是你能不能把一条链路讲完整、把边界条件说清楚。这份题库覆盖 Spring Bean 生命周期、Spring MVC 执行流程、Spring Boot 自动装配、SQL 执行计划与索引优化、线程池七大参数与五种状态、HashMap 底层结构、事务失效场景、循环依赖三级缓存、集合体系、Redis 数据类型、Stream API、死锁与防重复提交等模块,基本就是中高级 Java 岗一面到三面的主战场。拿分的核心不是“知道”,而是“能按顺序讲出来、能说出为什么这么设计、能指出哪里容易翻车”。适合已经写过 Spring 项目、但一被追问底层就卡壳的备考者,也适合面试前想快速把高频链路重新串一遍的人。
2. Spring 三件套:Bean 生命周期、MVC 流程、自动装配的答题链路
Spring 是这份题库里出现频率最高的模块,几乎每场面试都会从 Bean 生命周期切进去,再顺着问到循环依赖和事务。面试官最想听的不是零散名词,而是一条有先后顺序、有因果关系的链路。这一章把三个必考流程拆成可以直接复述的答题骨架。
2.1 Bean 生命周期:四段式讲法比背八股更稳
Bean 生命周期这类题,最忌讳上来就报“实例化、属性赋值、初始化、销毁”四个词然后停住。面试官接着问“初始化阶段具体调了什么”,你就得能接上InitializingBean的afterPropertiesSet()和init-method的先后关系。建议按“创建 → 注入 → 初始化 → 销毁”四段讲,每段带一个可验证的细节。
创建阶段有两种方式:反射创建和工厂方法创建。反射是容器拿到 BeanDefinition 后通过构造器 new 出实例;工厂方法则是配置里指定factory-method,由工厂类返回对象。注入阶段做两件事:把依赖的其他 Bean 注入进来,把配置里的属性值填进去。初始化阶段是高频追问点,顺序是afterPropertiesSet()先于init-method,因为前者是接口回调、后者是配置声明的方法。销毁阶段同理,DisposableBean的destroy()先于destroy-method。
public class DemoBean implements InitializingBean, DisposableBean { @Override public void afterPropertiesSet() { // 属性注入完成后回调,早于 init-method System.out.println("afterPropertiesSet"); } public void customInit() { // 配置中通过 init-method 指定 System.out.println("customInit"); } @Override public void destroy() { // 容器关闭时回调,早于 destroy-method System.out.println("destroy"); } public void customDestroy() { System.out.println("customDestroy"); } }这段代码的价值在于把四个回调点摆在一起,面试时你可以直接说“实现接口的回调优先于配置声明的方法”,这是区分“背过”和“理解过”的分水岭。参数上没什么可调的,关键是记住调用顺序和触发时机:afterPropertiesSet在属性注入之后、init-method之前;destroy在容器关闭、destroy-method之前。失败时看什么?如果afterPropertiesSet没执行,先确认 Bean 是否真的被容器管理,自己 new 出来的对象不会走生命周期回调。
2.2 Spring MVC 执行流程:抓住 DispatcherServlet 这个核心
MVC 流程题最容易答成流水账,把八个组件从头念到尾,面试官听完不知道你懂没懂。正确的讲法是先立一个核心:DispatcherServlet是前端控制器,除了控制器处理请求、视图渲染和响应客户端这三步,其余环节都要把结果交回给它。围绕这个核心再展开,逻辑就立住了。
客户端发 HTTP 请求 →DispatcherServlet拦截 →HandlerMapping把 URL 映射到具体处理器方法 →HandlerAdapter调用控制器方法 → 控制器执行业务逻辑、准备 Model 数据 → 返回视图名 →ViewResolver解析出实际视图 → 视图渲染生成 HTML →DispatcherServlet把响应返回客户端。答题时把“谁把结果交回给谁”讲清楚,比背组件名有用得多。
面试官常追问“HandlerMapping 和 HandlerAdapter 有什么区别”。标准答法:前者负责“找”,把请求 URL 和处理器建立映射;后者负责“调”,因为处理器有多种写法(注解式、实现接口式),需要适配器统一调用。这个“找”和“调”的拆分是设计上的解耦点,能说出来就比只报名字高一档。
2.3 Spring Boot 自动装配:从注解到条件生效的完整链路
自动装配的题,很多人只会说“@SpringBootApplication是三个注解的组合”,再往下就断了。要答完整,得把三个核心注解和底层选择器串起来。
@SpringBootApplication包含@SpringBootConfiguration(声明配置类)、@ComponentScan(默认扫描启动类所在包及子包)、@EnableAutoConfiguration(开启自动配置)。第三个是重点,它底层封装了@Import,导入AutoConfigurationImportSelector,调用getAutoConfigurationEntry(),再通过getCandidateConfigurations()读取候选配置类。候选配置类里定义@Bean方法,配合@Conditional系列注解做条件判断,条件满足才把方法返回值注册进 IOC 容器。
@Configuration public class DemoAutoConfiguration { @Bean @ConditionalOnMissingBean public DemoService demoService() { // 容器中没有 DemoService 时才注册,避免覆盖用户自定义 Bean return new DemoService(); } }@ConditionalOnMissingBean是自动装配里最常被问的条件注解,作用是“用户没定义我才兜底”。参数上注意条件注解的判断时机在 Bean 注册阶段,不是运行时。如果自动配置没生效,排查顺序是:确认候选配置类是否被加载、条件注解是否满足、是否有用户自定义 Bean 覆盖。这条排查链路面试时说出来,比背注解列表有说服力。
3. 数据库与并发:SQL 优化、索引失效、线程池与锁的高频问法
这一章是区分“会写代码”和“懂底层”的分水岭。SQL 优化和线程池几乎是中高级岗必问,而且面试官喜欢从一个小场景切入,看你能否把参数、状态、失效条件讲全。
3.1 SQL 执行计划:explain 每一列都要能对上号
SQL 优化题的标准入口是explain。在查询语句前加explain就能看到执行计划,重点看这几列:id是查询序列号,相同则从上到下执行,不同则值大的优先级高;select_type是查询类型;table是涉及的表;type是访问类型,性能从好到坏依次是system、const、eq_ref、ref、range、index、all,看到all基本就是全表扫描;possible_keys是可能用到的索引,为空说明没相关索引;key是实际使用的索引,为 null 就是没走索引;key_len是索引宽度,不损失精度前提下越短越好;rows是预估扫描行数;Extra是额外信息。
EXPLAIN SELECT id, name FROM demo_order WHERE user_id = 1001 AND status = 1;这条语句的看点在于:如果user_id和status上有联合索引,key应显示该索引,type至少到ref;如果key为 null、type为all,说明索引没生效,要回头查字段类型是否隐式转换、条件是否对索引列用了函数。rows数值越大越要警惕,它代表优化器预估要扫多少行。面试时能把“看哪几列、每列说明什么、异常时怎么排查”讲成一条线,这题就稳了。
3.2 索引失效:六种写法逐条对号入座
索引失效是 SQL 题里最容易翻车的地方,因为大家背了结论但说不出原因。常见失效场景有:对索引列使用函数或表达式、使用NOT或!=、使用or且两侧字段索引不一致、对索引列进行运算、like前置%、多个索引查询时只有第一个有效。另外in的值不宜过多,分页只有一页时用limit 1,能用inner join就别用left join和right join,排序字段没走索引就尽量不排序。
为什么前置%会失效?因为 B+Tree 是按前缀有序组织的,前置通配符让数据库无法从根节点定位起点,只能全扫。为什么函数会导致失效?因为索引存的是列原始值,对列做函数运算后索引里的顺序和运算结果对不上。答题时把“索引结构决定了什么写法能用”这句话带出来,面试官就知道你不是死记硬背。
3.3 线程池:七个参数、五种状态、拒绝策略一次讲透
线程池题的高频问法是“核心参数有哪些、任务提交后怎么流转、拒绝策略怎么选”。七个参数是corePoolSize核心线程数、maximumPoolSize最大线程数(等于核心线程加救急线程的最大数)、keepAliveTime救急线程生存时间、unit时间单位、workQueue任务队列、threadFactory线程工厂、handler拒绝策略。
任务流转逻辑:核心线程没满就开核心线程;核心线程满了进队列排队;队列满了才创建救急线程;救急线程也满了触发拒绝策略。四种拒绝策略要能对号:AbortPolicy直接抛异常,是默认策略;CallerRunsPolicy用调用者线程执行;DiscardOldestPolicy丢掉队列里最靠前的任务再执行当前任务;DiscardPolicy直接丢弃。
ThreadPoolExecutor pool = new ThreadPoolExecutor( 4, // corePoolSize 8, // maximumPoolSize 60L, // keepAliveTime TimeUnit.SECONDS, // unit new ArrayBlockingQueue<>(100), // workQueue,有界队列防止无界堆积 Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() // 队列满时由调用线程兜底执行 );这段代码的关键在workQueue用有界队列,以及拒绝策略选CallerRunsPolicy做背压。参数怎么改:核心线程数按任务类型定,CPU 密集型接近核数,IO 密集型可放大;队列容量决定缓冲能力,太大掩盖问题、太小频繁触发拒绝。五种状态是running、shutdown、stop、tidying、terminated,shutdown()等已提交任务执行完再关,shutdownNow()立即停止并返回未执行任务列表。面试官问“怎么监控线程池”,答getPoolSize()、getActiveCount()、getTaskCount()三个方法即可。
3.4 死锁与多线程三大特性:从现象到解法
死锁的定义是多个线程无限期等待彼此释放资源。避免手段有四条:避免循环依赖、统一获取锁的顺序、设置锁超时时间、引入抢占机制,资源分配图可以用来检测环路。答题时先给定义,再给“为什么会发生”,最后给“怎么防”,顺序别乱。
多线程三大特性是原子性、可见性、有序性。原子性指操作要么全完成要么全不做,i++这种复合操作不是原子的;可见性指一个线程改了共享变量其他线程能立刻看到;有序性指程序按代码顺序执行,但编译器和 CPU 可能重排序。解法上,原子性用synchronized或Lock,可见性用volatile或锁,有序性用synchronized、Lock或volatile的写读顺序保证。线程状态要能报全:新建、可运行、运行中、阻塞、等待、超时等待、终止。优雅退出线程的做法是设退出标志、对阻塞线程调interrupt()、用join()等结束、最后清理资源。
4. 集合、事务与缓存:HashMap、事务失效、循环依赖的易错点
这一章的题有个共同特点:结论大家都听过,但一追问“为什么”和“什么情况下不成立”就露馅。下面按易错点逐条拆。
4.1 易错点一:HashMap 死循环只说“1.8 修了”却说不清原因
错在哪:被问 HashMap 为什么线程不安全,只答“1.7 扩容会死循环,1.8 改成尾插法就好了”。为什么错:没讲清头插法在并发扩容时如何形成环形链表,也没说 1.8 虽然修了死循环但依然不安全。怎么答对:先讲结构,1.7 是数组加链表,1.8 是数组加链表加红黑树;再讲 1.7 扩容时头插法会让链表顺序反转,并发下两个线程同时迁移可能互相指向形成环,导致查询时无限循环;最后补一句 1.8 改尾插解决了环,但并发写仍可能丢数据,所以并发场景要用ConcurrentHashMap。
4.2 易错点二:事务失效场景漏掉“同类内部调用”
错在哪:只背了“非 public 方法、异常被吞、异常类型不匹配”几条,漏了方法内部自调用。为什么错:@Transactional靠 AOP 代理生效,同类内部直接调另一个方法走的是this调用,不经过代理,注解形同虚设。怎么答对:把失效场景按“代理没生效”和“回滚条件不满足”两类归。代理没生效包括非 public 方法、同类内部调用、方法被 final 修饰、类没被 Spring 管理、多线程环境;回滚条件不满足包括抛的是非运行时异常、异常被 try-catch 吞掉、传播行为配置不当、表本身不支持事务。这样归类,面试官追问哪条你都能接住。
4.3 易错点三:循环依赖张口就说“三级缓存”,却说不清每层放什么
错在哪:知道三级缓存能解决循环依赖,但说不清三个 Map 各存什么、查找顺序是什么。为什么错:只记了名词没记结构。怎么答对:第一层singletonObjects存完整单例 Bean;第二层earlySingletonObjects存实例化完成但未初始化的半成品;第三层singletonFactories存 Bean 的创建工厂。查找顺序是从第一层到第三层,第三层找到后取出对象放入第二层并从第三层移除。还要补一句边界:构造器注入的循环依赖三级缓存解决不了,得靠@Lazy或重构;字段注入和 setter 注入才能自动处理。
4.4 易错点四:集合选型只报名字不说场景
错在哪:问 List、Set、Map 区别,把实现类名字念一遍就完事。为什么错:面试官要的是“什么场景选什么”。怎么答对:List 保序可重复,ArrayList随机访问快、中间插入删除慢,LinkedList反过来;Set 去重,HashSet无序性能高,LinkedHashSet保插入顺序,TreeSet可排序;Map 存键值对,HashMap无序,LinkedHashMap保插入顺序,TreeMap按键排序。再补一句Vector已不推荐,线程安全场景用并发容器。
5. 收尾:把高频链路练成肌肉记忆的答题方法
走到这里,你会发现这份题库的题有一个共同结构:先问“是什么”,再问“怎么运作”,最后问“什么情况下不成立”。真正拉开差距的是第三层。我的做法是把每个模块压成一条“主链路加三个边界”的卡片,主链路用来开场,边界用来接追问。
以 Bean 生命周期为例,主链路是创建、注入、初始化、销毁四段;三个边界是接口回调与配置方法的先后、构造器注入循环依赖为何三级缓存救不了、@Lazy加在字段和加在类上的区别。以线程池为例,主链路是七参数加任务流转;三个边界是有界队列与无界队列的取舍、shutdown与shutdownNow的差异、拒绝策略选CallerRunsPolicy的背压含义。以 SQL 为例,主链路是explain看type和key;三个边界是前置%为何失效、函数为何失效、联合索引最左前缀怎么断。
练习时不要默读,要出声讲。每张卡片限时两分钟,讲完对照题库看漏了哪个边界。连续三天能把 Spring 三件套、线程池、SQL 索引、事务失效、循环依赖五组卡片讲顺,一面基本不会卡壳。面试官追问时最想听的是“为什么这么设计”和“不这么设计会怎样”,所以每讲一个结论,强迫自己补一句反例。
提示:答题时先给结论再给原因,别从背景铺垫开始讲,面试官没耐心等你绕。
我自己的血泪经验是:第一次面某公司,被问事务失效,我把能想到的场景全倒出来,唯独漏了同类内部调用,面试官追问“那方法里直接调本类另一个带注解的方法呢”,当场愣住。从那以后我每次准备事务题,都强制走一遍“代理是否生效 → 异常是否可回滚 → 传播行为是否合理”这三步检查,再也不敢只背结论。希望帮到你。
本文还有配套的精品资源,点击获取