AI+Java中级开发面试题库
生成时间:2026-05-20 23:02 (Asia/Shanghai)
【面试题】1. RAG系统中检索召回率低怎么优化?请从工程角度给出具体方案。
【标准答案】召回率低通常从三个维度优化:第一,分块策略升级——别用固定长度切分,改用语义分块或按段落/章节切,保持上下文完整;同时加overlap窗口(比如50~100字重叠)防止关键信息被截断。第二,检索策略混合——单路向量检索容易漏,改用混合检索:向量检索 + BM25关键词检索,结果做RRF(Reciprocal Rank Fusion)融合排序,召回率能提升15%~30%。第三,查询增强——用户原始query往往太短或太模糊,用LLM先做Query Rewriting,扩展出2~3个相关子查询并行检索再合并,也就是多查询检索。落地时还要注意embedding模型的领域适配,通用模型在垂直领域表现差,可以用领域语料微调embedding模型。另外元数据过滤(按时间、来源、标签)能精准缩小检索范围,间接提升有效召回。
【面试题】2. Java中synchronized和ReentrantLock的核心区别是什么?什么场景该选哪个?
【标准答案】核心区别有四点:第一,实现层面——synchronized是JVM层面的关键字,基于Monitor对象实现;ReentrantLock是JUC包下的API类,基于AQS实现。第二,功能丰富度——ReentrantLock支持公平锁、可中断获取(lockInterruptibly)、超时获取(tryLock带时间)、多条件变量(Condition),synchronized全不支持。第三,锁释放——synchronized自动释放,代码块退出就释放;ReentrantLock必须手动unlock,一般放在finally块里,忘了就死锁。第四,性能——JDK 1.6之后synchronized做了大量优化(偏向锁→轻量级锁→重量级锁升级),低竞争场景性能和ReentrantLock基本持平。选型建议:简单同步用synchronized,代码简洁不犯错;需要公平锁、可中断、超时、多条件队列这些高级特性时用ReentrantLock。面试中要强调:没有绝对谁好,看场景选工具。
【面试题】3. Spring AI中如何实现一个多轮对话的ChatClient?对话历史怎么管理?
【标准答案】Spring AI的ChatClient支持链式调用,核心是ChatClient.builder()模式。多轮对话的关键在于对话历史管理:第一,用ChatMemory管理上下文——Spring AI提供了InMemoryChatMemory(内存版)和持久化实现,每次对话把历史消息塞进Prompt。第二,具体做法是维护一个List<Message>,包含SystemMessage(角色设定)和历次的UserMessage/AssistantMessage,每次请求把完整历史传给模型。第三,生产环境必须做历史裁剪——Token有限,不能无限堆积,常见策略:滑动窗口保留最近N轮、按Token数截断、或用摘要压缩早期对话。第四,如果对话要跨请求持久化,自己实现ChatMemoryRepository接口,底层存Redis或数据库,用conversationId关联用户会话。面试时要提到:Spring AI的Advisor机制可以在请求链中自动注入历史,避免每次手动拼装,代码更优雅。
【面试题】4. JVM中新生代为什么用复制算法而老年代用标记-整理?如果全用一种算法会怎样?
【标准答案】这是基于对象生命周期特征做的设计取舍。新生代特点:对象朝生夕死,99%都是垃圾,存活率极低。复制算法只需要复制那1%的存活对象到Survivor区,剩余直接清空,效率极高;时间复杂度和存活对象成正比,存活少就快。而且不会产生内存碎片。代价是浪费一半空间(两个Survivor区轮换),但新生代本身就不大,浪费可接受。老年代特点:对象存活率高,垃圾少。如果用复制算法,要复制大量存活对象,效率极低,而且浪费一半老年代空间(老年代大,浪费量巨大)。标记-整理虽然要移动对象有停顿,但不需要额外空间,且整理后无碎片,对老年代更划算。如果全用复制算法——老年代空间浪费50%不可接受;全用标记-整理——新生代每次都遍历全量对象效率太低。所以分代选算法是工程上的最优解。
【面试题】5. 设计一个AI Agent时,如何决定用单Agent还是多Agent协作?多Agent之间怎么通信?
【标准答案】决策标准看任务复杂度:单Agent适合线性流程、工具少、决策路径明确的场景,比如一个客服问答Agent,调个RAG+工具就搞定。多Agent适合任务可分解、需要不同专业能力、或存在并行执行空间的场景,比如一个软件开发流程:需求分析Agent→设计Agent→编码Agent→测试Agent,各自擅长的领域不同。多Agent通信常见三种模式:第一,中心编排模式(Orchestrator)——一个主控Agent分发任务、汇总结果,各子Agent不直接通信,最简单可控;第二,消息传递模式——Agent通过消息队列或共享状态通信,适合松耦合、需要异步的场景;第三,对话协作模式——Agent之间直接对话协商,类似多角色讨论,灵活但难控制。工程上推荐Orchestrator模式,可控性强,LangGraph就是这种思路——定义StateGraph,节点是Agent,边是路由逻辑,状态在节点间流转。多Agent最大挑战是上下文共享和任务冲突,必须有统一的State管理。
【面试题】6. MySQL中索引失效的常见场景有哪些?如何用EXPLAIN分析定位?
【标准答案】七大常见失效场景:第一,对索引列做函数运算或隐式类型转换,比如WHERE YEAR(create_time)=2026,索引列被函数包裹直接失效;第二,联合索引不满足最左前缀原则,建了(a,b,c)但查询条件只有b和c,跳过了a;第三,LIKE以通配符开头,LIKE '%abc'走全表;第四,OR条件中有非索引列,整个索引失效;第五,NOT IN、NOT EXISTS、!=在部分场景下优化器放弃索引;第六,索引列用了计算,WHERE id+1=10,索引失效;第七,优化器判断全表扫描成本更低时主动放弃索引(小表或数据分布不均)。用EXPLAIN定位:重点看type列——ALL是全表扫描,index是全索引扫描,range是范围扫描,ref是等值匹配,const是最优;看key列确认实际用了哪个索引;看rows列估算扫描行数;看Extra列——Using index是覆盖索引(好),Using filesort/Using temporary是额外排序和临时表(需优化),Using where是回表过滤。面试中加一句:MySQL 8.0的索引下推(ICP)能减少回表次数,Extra里出现Using index condition就是触发了ICP。
【面试题】7. 提示词工程中Chain-of-Thought(CoT)和Few-Shot的区别与适用场景是什么?
【标准答案】两者解决不同问题。Few-Shot是提供几个示例让模型学习输入输出格式和模式,本质是"照猫画虎"——适合格式固定、模式明确的任务,比如文本分类、结构化提取、特定风格的翻译。CoT是在提示中引导模型逐步推理,本质是"想清楚再说"——适合需要多步推理的任务,比如数学应用题、逻辑推理、复杂判断。关键区别:Few-Shot靠模式匹配,CoT靠推理链分解。两者可以组合——Few-Shot CoT,给几个带推理过程的示例,效果比单独用任何一个都好。实际工程中的经验:简单分类任务Few-Shot够用,复杂推理必须CoT;CoT有Zero-Shot版本就是在提示里加"Let's think step by step",不需要示例也能激活推理能力。还有Self-Consistency策略——同一问题跑多次CoT取多数结果,能显著提升准确率。面试时要强调:CoT不是万能的,对不需要推理的任务反而增加token消耗,按任务复杂度选策略。
【面试题】8. Redis分布式锁实现的关键坑有哪些?Redlock算法解决什么问题?
【标准答案】核心坑有三个:第一,锁过期释放但业务未完成——加锁时设TTL防死锁,但如果业务执行时间超过TTL,锁自动释放其他客户端拿到锁,导致并发冲突。解决:用Redis的Hash结构存锁,开个看门狗线程定时续期(Redisson就是这么干的),默认每10秒续期30秒。第二,锁误删——客户端A的锁过期后客户端B加锁,A执行完DEL把B的锁删了。解决:锁值存唯一标识(UUID),释放时Lua脚本先比较再删除,保证原子性。第三,主从切换丢锁——主节点加锁成功但未同步到从节点就挂了,从节点升为主没有锁信息,另一个客户端又能加锁。Redlock算法解决的就是这个问题:向N个独立Redis实例(通常5个)都加锁,超过半数(3个)成功才算获取锁,总耗时不能超过锁的过期时间。Redlock争议很大(Martin Kleppmann和Antirez的著名论战),工程上多数团队用单实例Redisson+看门狗就够了,强一致性需求考虑ZooKeeper。
【面试题】9. 大模型微调中LoRA的原理是什么?为什么它比全量微调更高效?
【标准答案】LoRA(Low-Rank Adaptation)核心思想:大模型微调时不需要更新全部参数,只在一个低秩子空间里学增量。具体做法:冻结原始权重W,旁边加一个旁路ΔW = A×B,其中A是d×r的矩阵,B是r×d的矩阵,r远小于d(比如r=8,d=4096,参数量从d²降到2dr,减少几百倍)。训练只更新A和B,推理时把ΔW合并到W上(W' = W + A×B),不增加推理延迟。为什么更高效:第一,可训练参数量极少——7B模型全量微调要更新70亿参数,LoRA只更新几百万,显存占用从几十GB降到个位数GB,单卡就能跑。第二,训练速度快——参数少梯度计算少,训练时间大幅缩短。第三,可插拔——不同任务训练不同的LoRA权重,推理时切换,一个基座模型服务多个任务。关键超参是r(秩)和alpha(缩放系数),r越大表达能力越强但参数越多,一般r=8~64;alpha控制LoRA增量的影响强度。面试加分点:QLoRA在LoRA基础上加4-bit量化,进一步把显存降到极致,微调65B模型单卡就能跑。
【面试题】10. Kafka如何保证消息的精确一次(Exactly-Once)语义?生产者和消费者各自怎么实现?
【标准答案】Kafka的Exactly-Once是三层面配合的结果。生产者端:开启幂等性(enable.idempotence=true),Kafka给每个Producer分配PID,每条消息带Sequence Number,Broker端对<PID, Partition, SeqNum>去重,自动过滤重复消息,保证单分区内不重复。再配合事务(transactional.id),跨分区的写入可以原子提交或回滚。消费者端:消费者天然是at-least-once——处理后提交offset,如果处理中途挂了重启会重复消费。要实现Exactly-Once需要业务端做幂等:第一,用业务唯一ID去重——消费时先查数据库是否已处理过该消息ID;第二,消费和offset提交放在同一个数据库事务里,要么一起成功要么一起回滚;第三,用Kafka的事务API让消费者只读取已提交的事务消息(isolation.level=read_committed)。面试关键总结:Kafka的Exactly-Once = 生产者幂等/事务 + 消费者业务幂等,不是单靠Kafka配置就能完全解决,业务端去重是必要补充。