Java八股文学习记录之三 1. JUCJava Util Concurrent核心组件JUC 是java.util.concurrent包的简称是 Java 并发编程的核心工具包。锁机制ReentrantLock可重入独占锁支持公平/非公平模式、可中断获取、超时获取。相比 synchronized 更灵活但需要手动释放锁try-finally。ReentrantReadWriteLock读写锁读读共享、读写互斥、写写互斥适合读多写少场景。StampedLockJDK8 引入性能更好的读写锁支持乐观读模式tryOptimisticRead乐观读期间允许写操作读完后需 validate 校验。同步器AQS 体系AQSAbstractQueuedSynchronizerJUC 的基石基于 CLH 队列 state 变量实现。ReentrantLock、CountDownLatch、Semaphore 等都基于它。CountDownLatch一次性门闩一个线程等待 N 个线程完成await()等待countDown()减到 0不可复用。CyclicBarrier循环栅栏N 个线程互相等待全部到达后触发屏障动作可复用。Semaphore信号量控制同时访问资源的线程数限流。CompletableFuture异步编排利器支持链式调用、组合、异常处理。thenApply/thenCompose/thenCombine/allOf/anyOf。并发容器ConcurrentHashMap分段锁JDK7→ CAS synchronizedJDK8红黑树解决哈希冲突size 用 CounterCell 累加。CopyOnWriteArrayList写时复制适合读多写极少场景如监听器列表写操作加锁并复制整个数组。BlockingQueue阻塞队列ArrayBlockingQueue 有界、LinkedBlockingQueue 可选有界生产者-消费者模式的基础。原子类AtomicInteger、AtomicLong、AtomicReference基于 CAS volatile。LongAdderJDK8高并发下比 AtomicLong 性能更好分散热点到多个 Cell。线程池ThreadPoolExecutor 核心参数corePoolSize、maximumPoolSize、keepAliveTime、workQueue有界/无界、RejectedExecutionHandler四种拒绝策略AbortPolicy 抛异常、CallerRunsPolicy 调用者执行、DiscardPolicy 静默丢弃、DiscardOldestPolicy 丢弃最旧。执行流程先 corePool → 队列满 → 扩容到 maxPool → 队列再满 → 拒绝策略。面试常见追问synchronized 和 ReentrantLock 区别synchronized 是 JVM 层面的关键字自动释放锁非公平ReentrantLock 是 API 层面需手动释放支持公平锁、可中断、超时、条件变量多个 Condition。ConcurrentHashMap 为什么线程安全JDK8 用 CAS初始化 table、设置链表头 synchronized锁链表/红黑树的头节点 volatileNode 的 val 和 next粒度更细并发度更高。2. RAG 检索概念RAGRetrieval-Augmented Generation是检索增强生成核心思路是大模型在生成回答前先从外部知识库中检索相关文档将检索结果作为上下文拼入 prompt让模型基于检索到的知识来回答从而减少幻觉、使用最新数据。流程用户提问 → 向量化(Embedding) → 向量数据库检索 → 召回 Top-K 相关文档 → 拼入 Prompt → LLM 生成回答关键技术点文档处理Ingestion文档切分Chunking按字符/段落/语义切分常用策略有固定大小切分如 512 token、递归切分、语义切分。chunk_size 和 overlap 是关键参数。向量化Embedding将文本转为向量。常用模型text-embedding-3-small、bge-large-zh 等。存储向量数据库Milvus、Pinecone、Weaviate、Pgvector、Elasticsearch 8.x。检索优化核心难点混合检索Hybrid Search向量检索 BM25 关键词检索结合兼顾语义和关键词匹配。重排序Re-ranking召回后用 Cross-encoder 模型重新排序提升相关性。如 Cohere Rerank、bge-reranker。查询重写Query Rewriting将用户问题改写成更适合检索的形式如 Multi-Query、HyDE。多路召回从不同数据源/不同粒度同时召回融合排序。生成增强将检索到的文档作为上下文填入 prompt 模板加上约束仅根据以上资料回答不知道就说不知道。引用标注要求模型在回答中标明信息来源。面试追问如何评估 RAG 效果检索指标RecallK、MRR、NDCG 生成指标Faithfulness 忠实度、Answer Relevance 答案相关性、Context Precision。chunk 太大或太小有什么问题太大检索精度下降无关内容多浪费 token。太小上下文不完整语义丢失。需要根据文档类型和模型上下文窗口调整。3. MySQL 索引及其优化索引基础索引本质一种有序的数据结构帮助快速定位数据避免全表扫描。类似于书的目录。常见索引类型按数据结构BTree 索引InnoDB 默认所有数据存在叶子节点叶子节点形成有序双向链表非叶子节点只存 key。适合范围查询、排序、等值查询。Hash 索引只支持等值查询不支持范围查询和排序Memory 引擎使用。全文索引FULLTEXT用于文本搜索。空间索引R-Tree地理数据。按功能分类主键索引聚簇索引数据行物理存储在叶子节点上。一张表只有一个。二级索引非聚簇索引叶子节点存主键值。查询时需要回表拿着主键去聚簇索引查完整行数据。索引优化策略高频考点1. 最左前缀原则联合索引(a, b, c)相当于创建了(a)、(a, b)、(a, b, c)三个索引。查询条件必须包含最左列a才能使用索引。WHERE b 1 AND c 2不走索引。2. 覆盖索引查询的列都在索引中不需要回表性能最好。EXPLAIN看Extra: Using index。例如SELECT a, b FROM t WHERE a 1在联合索引(a, b)上可直接覆盖。3. 避免索引失效WHERE条件中对索引列使用函数或表达式WHERE YEAR(create_time) 2024→ 应改为WHERE create_time 2024-01-01 AND create_time 2025-01-01隐式类型转换WHERE phone 13800000000如果 phone 是 varchar会走全表扫描LIKE %xxx左模糊完全无法使用索引LIKE xxx%可以OR 连接非索引列可能导致全表扫描!、NOT IN、IS NULL在部分场景下不走索引4. 索引设计原则频繁查询 WHERE ORDER BY GROUP BY 的列适合建索引区分度高的列放联合索引前面如 user_id 优于 status不要过多索引影响写入性能大表考虑分区表或分库分表EXPLAIN 解读字段含义type访问类型性能排序system const eq_ref ref range index ALLkey实际使用的索引rows预估扫描行数ExtraUsing index覆盖索引、Using filesort需要额外排序、Using temporary临时表差面试追问为什么用 BTree 而不是 B-TreeBTree 只在叶子节点存数据非叶子节点可以存更多 key树更矮IO 次数更少叶子节点形成有序链表范围查询效率高。聚簇索引和非聚簇索引区别聚簇索引叶子节点直接存数据行非聚簇索引叶子节点存主键需要回表。建议主键使用自增 ID避免随机 UUID 导致页分裂。大表如何加索引不锁表MySQL 5.6 支持 Online DDLALGORITHMINPLACE或使用 pt-online-schema-change 工具通过触发器影子表的方式无损加索引。4. LangChain4j 框架和工具调用概念LangChain4j是 LangChain 的 Java 版本用于构建 LLM 应用的框架。核心目标是简化 LLM 集成、RAG、Agent、工具调用等场景。核心模块模型集成ChatLanguageModel model OpenAiChatModel.builder() .apiKey(sk-xxx) .modelName(gpt-4) .build(); String answer model.generate(Hello);工具调用Tool Calling / Function Calling让 LLM 决定调用哪个 Java 方法根据用户意图自动选择工具并传参。// 1. 定义工具 Tool(查询某城市的天气) public String getWeather(P(城市名称) String city) { return city 晴25°C; } ​ // 2. 注册工具并对话 Assistant assistant AiServices.builder(Assistant.class) .chatLanguageModel(model) .tools(new WeatherTool()) .build(); ​ String result assistant.chat(北京今天天气怎么样); // LLM 会自动识别意图 → 调用 getWeather(北京) → 返回结果RAG 模块EmbeddingStore对接向量数据库Redis、Milvus、Elasticsearch 等EmbeddingModel文本向量化DocumentSplitter文档切分RetrievalAugmentor统一的 RAG 流程编排其他关键模块PromptTemplate模板化 prompt支持变量填充ChatMemory对话记忆管理MessageWindowChatMemory、TokenWindowChatMemoryAiServices声明式 AI 服务接口 注解自动代理生成实现Chain顺序编排多个步骤如 prompt → LLM → 解析输出面试追问AiServices 的原理基于动态代理Proxy解析接口方法上的注解生成 prompt 模板调用 LLM将返回值反序列化为接口返回类型。工具调用流程是什么样用户消息 工具定义列表 → 发给 LLM → LLM 返回 tool_call 请求含参数→ 框架执行对应 Java 方法 → 将结果发回 LLM → LLM 生成最终回答。5. Skill 编写概念Skill技能是给 AI Agent 扩展能力的模块化组件。不同平台有不同实现方式但核心思想一致将特定领域的知识、工具、流程封装成可复用的技能。Claude Code 中的 Skill 编写Skill 是一段 markdown 格式的指令放在.claude/skills/目录下通过 frontmatter 定义元数据正文描述行为。--- name: my-reviewer description: 按照团队规范审查代码 --- ​ ## 执行步骤 1. 读取所有改动的文件 2. 检查是否遵循命名规范方法名 camelCase类名 PascalCase 3. 检查是否有未处理的异常 4. 检查 SQL 是否有注入风险 5. 输出结构化的审查报告按严重程度分级通用 Skill 设计原则单一职责一个 Skill 只做一件事做好一件事清晰的输入/输出定义好入参格式和返回值格式错误处理异常情况下返回友好信息而非直接抛出上下文管理Skill 要知道自己的边界不越界做事可组合多个 Skill 可以串行/并行组合完成复杂任务面试回答思路核心就是把一个特定任务的标准操作流程SOP固化下来让 AI 在遇到这类任务时能按既定规范执行保证输出质量和一致性。关键是要定义清楚触发条件什么时候用、执行步骤怎么做、输出格式做成什么样。6. 如何写提示词Prompt Engineering核心原则1. 结构化最重要好的 prompt 应该有清晰的结构# 角色 你是一个资深的 Java 代码审查专家... ​ # 任务 请审查以下代码检查潜在的性能问题... ​ # 输入 [粘贴代码] ​ # 输出格式 按严重程度严重/一般/建议列出问题每条包含位置、问题、建议修复方案 ​ # 约束 - 不要讨论命名风格 - 只关注性能相关的问题2. 提供示例Few-Shot当希望模型输出特定格式时给 1-2 个示例效果远好于纯文字描述。3. 设约束 分解任务先分析再给出结论一步步思考Chain of Thought复杂任务拆成多个子 prompt 依次执行4. 角色扮演赋予 LLM 一个具体角色能显著提升输出质量。你是一个有 10 年经验的 Java 架构师 比直接问效果更好。常见技巧技巧说明零样本Zero-Shot直接提问题不给示例少样本Few-Shot给 1-3 个示例再提问题思维链CoT让我们一步步思考 引导模型推理过程角色扮演设定专业角色限定回答风格格式约束明确要求 JSON/Markdown/表格 输出反向提示在回答前先列出你可能出错的地方面试追问写 prompt 最常见的问题指令模糊帮我优化这段代码 没有说优化什么2. 信息过载把所有需求堆在一个 prompt3. 没有给定输出格式导致结果不可预测。System Prompt 和 User Prompt 的区别System Prompt 设定模型的行为边界和角色持久性指令User Prompt 是具体的问题输入。实践中 System Prompt 优先于 User Prompt模型更倾向于遵守 System Prompt。7. Spring 注解核心注解分类Bean 定义注解说明Component通用组件标记为 Spring BeanService业务逻辑层Repository数据访问层额外翻译持久层异常ControllerWeb 控制器层RestControllerControllerResponseBodyConfiguration配置类内部Bean方法会被代理依赖注入注解说明Autowired按类型注入默认必须可设 required falseQualifier配合 Autowired按名称指定ResourceJSR-250默认按名称注入Value注入配置文件值或 SpEL 表达式声明周期注解说明PostConstruct依赖注入完成后执行初始化PreDestroyBean 销毁前执行清理Scope作用域singleton/prototype/request/sessionAOP 相关注解说明Transactional声明式事务传播行为、隔离级别、回滚策略Aspect定义切面类Before/After/Around通知类型条件装配注解说明ConditionalOnClass类存在时才装配ConditionalOnBeanBean 存在时才装配ConditionalOnProperty配置值满足条件时才装配Profile指定环境才生效面试追问Autowired和Resource的区别Autowired 是 Spring 的默认按类型注入配合 Qualifier 按名称Resource 是 JSR-250默认按名称注入找不到再按类型。Transactional失效的场景方法非 public2. 同类方法调用this.xxx()不经过代理3. 异常被 try-catch 吞掉没抛出来4. rollbackFor 没配置默认只回滚 RuntimeException 和 Error5. 数据库引擎不支持事务如 MyISAM。8. Spring BeanBean 生命周期高频考点1. BeanDefinition 注册 ↓ 2. BeanFactoryPostProcessor可修改 BeanDefinition如 ${} 占位符替换 ↓ 3. 实例化反射调用构造器创建对象实例 ↓ 4. 属性赋值Autowired、Value 等依赖注入 ↓ 5. Aware 接口回调BeanNameAware → BeanFactoryAware → ApplicationContextAware ↓ 6. BeanPostProcessor#postProcessBeforeInitialization ↓ 7. PostConstruct 标注的初始化方法 ↓ 8. InitializingBean#afterPropertiesSet() ↓ 9. init-methodXML 或 Bean 的 initMethod ↓ 10. BeanPostProcessor#postProcessAfterInitializationAOP 代理在此生成 ↓ 11. Bean 就绪可被使用 ↓ 12. PreDestroy / DisposableBean#destroy() / destroy-method容器关闭时销毁关键要点BeanPostProcessor是 Spring 的扩展核心AOP 代理就是在postProcessAfterInitialization中通过 JDK 动态代理或 CGLIB 创建的。单例和多例的区别singleton 在容器初始化时就创建完成急加载prototype 每次获取时才创建懒加载Spring 不管理 prototype Bean 的完整生命周期不调用销毁方法。解决循环依赖Spring 通过三级缓存解决 singleton 的 setter 注入循环依赖一级singletonObjects完整 bean二级earlySingletonObjects早期引用三级singletonFactoriesObjectFactory 可生成代理对象。构造器注入的循环依赖无法解决会抛 BeanCurrentlyInCreationException。Bean 的作用域作用域说明singleton默认IoC 容器中唯一实例prototype每次获取创建新实例request每个 HTTP 请求一个实例Websession每个 HTTP Session 一个实例Webapplication每个 ServletContext 一个实例Web面试追问为什么构造器注入比字段注入好依赖不可变final2. 依赖明确构造器参数就是所有依赖3. 方便单元测试不需要启动 Spring 容器4. 避免 NPE编译期确保注入不为 null。Spring 官方推荐构造器注入。Spring 如何解决循环依赖三级缓存机制核心是用 ObjectFactory 提前暴露半成品bean 的引用刚实例化未注入属性让另一个 bean 先持有它的引用等两个 bean 都初始化完后再注入最终版本。只解决单例 setter 注入场景构造器注入循环无法解决。9. 如何 SQL 防止事务冲突事务并发问题问题描述脏读读到了另一个事务未提交的修改不可重复读同一事务内两次读取同一行结果不同被别的事务 update 了幻读同一事务内两次范围查询结果集条数不同被别的事务 insert 了丢失更新两个事务同时读再先后更新后提交的覆盖了先提交的解决方案1. 隔离级别Isolation Level级别脏读不可重复读幻读并发性能READ UNCOMMITTED可能可能可能最高READ COMMITTED避免可能可能高REPEATABLE READMySQL 默认避免避免部分避免中SERIALIZABLE避免避免避免低2. 乐观锁推荐用于读多写少-- 版本号机制 UPDATE order SET amount 100, version version 1 WHERE id 1 AND version 5; -- 如果 version 变了受影响行数为 0 ​ -- 或使用时间戳 UPDATE product SET stock 10, update_time NOW() WHERE id 1 AND update_time 2024-01-01 12:00:00;在应用层检查受影响行数为 0 则重试或抛异常。3. 悲观锁适合冲突概率高的场景-- SELECT ... FOR UPDATE行锁其他事务的 SELECT FOR UPDATE 会等待 -- 必须在事务中使用提交/回滚后释放锁 BEGIN; SELECT stock FROM product WHERE id 1 FOR UPDATE; UPDATE product SET stock stock - 1 WHERE id 1; COMMIT;4. 分布式锁跨服务场景使用 RedisSETNX 过期时间 Redisson Lua 脚本保证原子性或 Zookeeper 临时顺序节点。5. 其他策略幂等设计通过唯一索引如订单号的唯一约束防止重复插入CAS 更新UPDATE ... WHERE field old_value发现值被改过则放弃串行化将对同一资源的操作排队如 MQ 单线程消费分段锁将库存分散到多个 slot减少竞争热点如stock_0,stock_1, ...,stock_9实战建议能通过 SQL 原子操作完成的不要用读-判断-写模式如直接UPDATE ... SET count count - 1 WHERE count 0乐观锁适合读多写少、冲突率低的场景如用户资料修改悲观锁适合冲突率高的场景如秒杀扣库存但要注意死锁风险一定要监控事务时长长事务会导致锁持有时间长、阻塞其他请求面试追问MySQL InnoDB RR 隔离级别如何解决幻读通过 Gap Lock间隙锁 Record Lock记录锁 Next-Key Lock锁定一个区间防止其他事务在这个区间内 INSERT。但不完全解决如当前读才能加 gap lock快照读不会。MVCC 机制通过 Read View 在快照读下避免了幻读。10. 如何总结项目的优点亮点回答框架STAR 量化STAR 法则SSituation项目背景、面临的问题TTask你承担的任务/角色AAction你采取了什么行动技术选型、方案设计、关键决策RResult取得了什么结果必须量化提炼亮点的维度维度示例性能提升接口响应时间从 2s 降低到 200ms技术难度自研分库分表中间件支持动态扩容业务影响支撑日均 1000 万笔订单处理成本降低通过 SQL 优化减少数据库 CPU 30%省了两台服务器工程质量引入自动化测试上线回归时间从 4 小时缩到 30 分钟故障处理定位并修复了一个死锁问题避免了线上延迟连续飙升典型回答模板我负责重构了订单查询模块。原有接口在高并发下响应时间超过 2 秒我通过分析慢查询日志发现是索引缺失和多表关联导致。我做了三层优化第一补充联合索引将查询从全表扫描优化为 ref 级别第二引入 CQRS 模式将读写分离查询走 ES 实现毫秒级搜索第三对热点数据加 Caffeine 本地缓存 Redis 二级缓存。最终接口 P99 从 2s 降到 150msQPS 从 500 提升到 5000缓存命中率达到 95%。常见错误只讲功能不讲价值不要说我做了个 CRUD 页面要说我实现的后台管理系统支撑了 50 个运营人员的日常工作。全是团队功劳用我主导/负责/设计而不是我们。没有数据支撑用具体数字替代模糊形容词快了很多 → 响应时间降低了 70%。技术堆砌不用说原理说了用 Redis要说为什么用、怎么用的、解决了什么问题。没有困难没有亮点主动提及遇到的难题和你如何解决的这是最好的加分项。