平安银行 Java 开发一面 面经深度解析
来源:牛客网 王牌飞行员007 岗位:平安银行 Java 开发 特点:27 题覆盖 Java 基础、AOP/字节码增强、JVM 排查、内存泄漏、AI Agent 开发、项目经验,广度极大。
一、Java 基础与 AI
Q1-Q2:自我介绍 + Java 语言了解程度
基础热身题,按简历如实回答即可。Java 了解程度从基础语法 → 集合 → 并发 → JVM → 框架源码递进说明。
Q3:有接触过 AI 相关的开发吗?
参考回答:AI Agent 开发(OpenClaw/Claude Code)、RAG 检索增强生成、Prompt Engineering、大模型 API 调用(Chat Completion API)。
二、AOP 与字节码增强
Q4-Q5:AOP 是什么?应用场景?无侵入字节码植入?
AOP(面向切面编程):将横切关注点(日志、事务、权限)从核心业务逻辑中抽离出来。
@Aspect
@Component
public class LogAspect {
@Around("@annotation(Log)")
public Object around(ProceedingJoinPoint point) throws Throwable {
log.info("方法: {}", point.getSignature().getName());
long start = System.currentTimeMillis();
Object result = point.proceed();
log.info("耗时: {}ms", System.currentTimeMillis() - start);
return result;
}
}
应用场景:日志记录、权限校验、事务管理、性能监控、缓存处理、异常统一处理。
Q6:如何实现无侵入的字节码植入?(其他工程不需要依赖你的代码)
考点:Java Agent + 字节码增强。
|
方式 |
原理 |
侵入性 |
|---|---|---|
|
AOP 切面(Spring AOP) |
编译时/运行时生成代理 |
需依赖 Spring,侵入 |
|
AspectJ 编译时织入 |
编译期修改 .class |
需 AspectJ 编译器 |
|
Java Agent + ASM/ByteBuddy |
运行时修改类字节码 |
无侵入 ✅ |
Java Agent 实现:
// 1. premain-agent.jar —— 代理 JAR
public class MyAgent {
public static void premain(String args, Instrumentation inst) {
inst.addTransformer((loader, className, classBeingRedefined,
protectionDomain, classfileBuffer) -> {
if (className.equals("com/example/target/UserService")) {
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
// ASM 修改字节码:在方法前后插入代码
cr.accept(new ClassVisitor(ASM9, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, ...) {
// 在目标方法前后插入耗时统计
...
}
}, 0);
return cw.toByteArray();
}
return classfileBuffer;
});
}
}
# 2. 目标应用启动时加 -javaagent 参数
java -javaagent:premain-agent.jar -jar target-app.jar
# 3. attach 动态挂载(运行时不重启)
# Arthas 就是用的这个方式
Q7:了解 Arthas 或 SkyWalking 吗?
|
工具 |
原理 |
用途 |
|---|---|---|
|
Arthas(阿里) |
Java Agent + Instrumentation API,运行时动态增强 |
在线诊断:查看方法耗时、反编译 class、监控调用链路、热替换代码 |
|
SkyWalking(Apache) |
Java Agent + 插件体系,自动探针 |
APM 全链路追踪:服务拓扑、调用链 Trace、性能指标 |
# Arthas 常用命令
jad com.example.UserService # 反编译
watch com.example.UserService * '{params, returnObj, throwExp}' # 观察方法入参/返回值
trace com.example.UserService * # 追踪方法调用链路
tt -t com.example.UserService getById # 记录方法每次调用的耗时和参数
核心:二者都基于 Java Agent + 字节码增强,在不修改源码的情况下"插桩"监控。
三、JVM 排查
Q8:Java 项目出现内存溢出怎么排查?
OOM 排查流程:
1. 确认 OOM 类型:
├── java.lang.OutOfMemoryError: Java heap space → 堆溢出
├── java.lang.OutOfMemoryError: Metaspace → 方法区溢出
├── java.lang.OutOfMemoryError: unable to create new native thread → 线程溢出
└── java.lang.OutOfMemoryError: Direct buffer memory → 堆外内存溢出
2. 开启 OOM 自动 Dump:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heapdump.hprof
3. 用 MAT 分析 hprof:
├── Leak Suspects Report → 定位最大嫌疑对象
├── Dominator Tree → 按保留集大小排序
└── Path to GC Roots → 找到引用链
4. 修复 → 压测验证 → 上线
Q9:堆外内存溢出怎么解决?
堆外内存:JVM 堆之外的内存,GC 不管理。
|
堆外内存来源 |
解决方式 |
|---|---|
|
NIO DirectByteBuffer(Netty 大量使用) |
|
|
JNI/Unsafe |
检查代码中 |
|
线程栈(每个线程 1MB 栈空间) |
|
|
GZIP/Deflater |
确认 |
排查工具:pmap -x <pid>、jcmd <pid> VM.native_memory summary(需开启 NMT:-XX:NativeMemoryTracking=detail)。
Q10-Q11:内存泄漏怎么判断?隐蔽的缓慢泄漏怎么排查?
判断方法:
# 1. jstat 持续观察
jstat -gc <pid> 1000 100 # 每秒输出一次 GC 情况,共 100 次
# 关注:OU(老年代使用量)持续上升且 Full GC 后不降
# 2. 多次 Dump 对比
jmap -dump:live,format=b,file=heap1.hprof <pid>
# 等待 30 分钟
jmap -dump:live,format=b,file=heap2.hprof <pid>
# MAT → Compare Basket → 对比两个 Dump → 找出数量持续增长的类
隐蔽泄漏排查(长周期):
策略:长期监控 + 定期 Dump + 对比分析
1. 开启 JMX 监控:
- Prometheus + JMX Exporter 长期记录 JVM 指标
- Grafana 展示堆使用趋势曲线
2. 定期自动 Dump:
- 写脚本每天凌晨 Dump 一次堆快照
- 对比相邻两天的 Dump,找数量异常增长的类
3. 业务维度分析:
- 内存增长率与 QPS 是否有相关性
- 是否某次上线后内存趋势改变
4. Arthas 在线分析:
- vmtool --action getInstances --className com.xxx --limit 100
- 查看具体对象的字段值,判断业务来源
四、数据结构与并发
Q12:大量数据去重用什么数据结构?
|
数据结构 |
时间复杂度 |
空间 |
适合场景 |
|---|---|---|---|
|
HashSet |
O(1) |
O(n) |
内存充足,数据量几百万内 |
|
BitMap |
O(1) |
O(n/8) |
数据是正整数范围(如用户 ID 1~10^8) |
|
布隆过滤器 |
O(k) |
O(m) |
亿级数据,可容忍少量误判(0.1%) |
|
RoaringBitmap |
O(1) |
压缩高效 |
稀疏大范围整数集 |
|
HyperLogLog |
O(1) |
12KB |
只统计去重计数,不存具体值 |
Q13:Set 有并发问题吗?
答案:有。HashSet 非线程安全。
多线程同时 add() 可能导致数据丢失或内部数据结构损坏。并发场景用:
Collections.synchronizedSet(new HashSet<>())ConcurrentHashMap.newKeySet()(JDK 8+)CopyOnWriteArraySet(读多写少)
Q14:HashMap 线程不安全体现在哪些方面?
|
方面 |
说明 |
|---|---|
|
数据覆盖 |
多线程 put 同一桶,value 互相覆盖 |
|
size++ 不准确 |
|
|
链表/红黑树结构破坏 |
并发 put 导致链表成环或树节点丢失 |
|
get 返回 null |
扩容期间 get 可能定位到错误的桶 |
|
死循环(JDK 7) |
头插法扩容导致环形链表,get 时死循环 |
五、项目经验
Q15:实习选择测试开发的原因?职业规划?
测试开发经历对质量意识有帮助,了解自动化测试框架、CI/CD 流程。未来想做后端开发,偏基础设施/AI 中间件方向。
Q16:Agent 验证如何做的?
验证体系:
├── 单元测试:每个 Tool 独立测试
├── 集成测试:Mock LLM 响应,测试 Agent 流程完整性
├── E2E 测试:真实 LLM,固定 Query 集 → 自动评判答案
├── 回归测试:每次 Prompt 变更后跑全量 E2E
└── 人工抽检:随机抽取 5% 用户对话,人工评分
Q17:和需求方怎么交流业务?遇到什么问题?
- 需求评审会 + 需求文档 + 原型图
- 难点:需求变更频繁 → 建立变更流程,评估影响后再排期
- 跨部门对接:接口文档 + 定期同步进度
Q18-Q19:代码缺陷检测?和静态代码扫描的区别?
|
对比 |
静态代码扫描(SonarQube) |
AI Agent 缺陷检测 |
|---|---|---|
|
检测方式 |
基于规则匹配(正则/AST) |
基于 LLM 语义理解 |
|
检测范围 |
已知模式(SQL 注入、空指针) |
业务逻辑缺陷、异常处理遗漏 |
|
误报率 |
中 |
低(LLM 上下文理解) |
|
能发现 |
语法级别 Bug |
语义级别 Bug(如死锁风险、并发安全问题) |
缺陷分类:空指针、资源未关闭、并发安全、异常吞没、逻辑错误、性能隐患。
Q20:基于代码哈希构建方法级缓存表的作用?增量分析?
// 代码哈希计算
class MethodCache {
Map<String, String> methodHashMap; // methodName → methodBodyHash
boolean isAnalyzed(String methodName, String newCodeHash) {
String oldHash = methodHashMap.get(methodName);
return Objects.equals(oldHash, newCodeHash);
}
}
作用:判断某个方法自上次分析后是否变更,未变更则跳过分析。增量分析:只分析 Git diff 中改动的方法 → 大幅节省分析时间。
Q21:后端如何解析前端代码?
- 静态解析:AST 解析器(Babel for JS/TS、
@babel/parser) - 抽象语法树遍历:提取函数调用、变量定义、组件结构
- 数据流分析:追踪变量从定义到使用,检测未定义变量、未使用导入
六、AI Agent 与工具
Q22-Q23:有其他 Offer 吗?AI Agent 开发处于什么阶段?
已落地使用阶段:Agent 验证 + RAG + 多轮对话 + 工具调用全链路都已实现。
Q24:OpenClaw 有部署过吗?
OpenClaw 本地部署:
# 安装
npm install -g openclaw
# 配置
openclaw config init
# 本地拉起
openclaw gateway start
# 配置 Agent 和模型
# openclaw.yml:
agents:
- id: dev-assistant
model: claude-sonnet-4
skills: [java, spring, docker]
Q25:AI 编程工具能解决大部分需求开发吗?
答案:能解决 60-70% 的编码工作,但以下仍需人工:
|
AI 能做 |
仍需人工 |
|---|---|
|
CRUD 样板代码 |
复杂业务逻辑设计 |
|
SQL 编写 |
数据库表结构设计 |
|
Bug 修复辅助 |
架构决策 |
|
单元测试生成 |
跨系统集成方案 |
|
代码重构建议 |
性能极致优化 |
Q26:RAG 混合检索整体思路?
混合检索流程:
用户 Query
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 关键词检索 │ │ 向量检索 │ │ 知识图谱 │
│ (BM25) │ │ (Embedding)│ │ 检索 │
│ 精确匹配 │ │ 语义相似 │ │ 结构化关系│
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└──────────────┼──────────────┘
▼
┌──────────┐
│ 融合排序 │ ← RRF (Reciprocal Rank Fusion)
│ Rerank │ ← Cross-encoder 重排序
└────┬─────┘
▼
取 Top-K 结果
│
▼
注入 LLM Prompt
关键环节:
- 多路召回:关键词(精确)+ 向量(语义)+ KG(结构化),互补覆盖
- RRF 融合:
RRF(d) = Σ 1/(k + rank_i(d)),综合多路排名 - Rerank 精排:用 Cross-encoder(如 BGE-Reranker)对融合结果重新打分
面经总结
|
领域 |
题数 |
核心 |
|---|---|---|
|
AOP/字节码 |
4 |
AOP 原理 → 无侵入植入 → Arthas/SkyWalking |
|
JVM 排查 |
4 |
OOM 分类 → 堆外内存 → 缓慢泄漏对比 Dump |
|
数据结构/并发 |
3 |
去重数据结构选型 → Set/HashMap 线程安全 |
|
AI Agent |
6 |
验证体系 → OpenClaw → RAG 混合检索 |
|
项目 |
7 |
代码缺陷检测 → 增量分析 → 业务沟通 |
|
综合 |
3 |
职业规划 → AI 工具认知 → 反问 |
面试特点:从 AOP 基础一路追问到 Java Agent 无侵入字节码实现(Q4→Q5→Q6→Q7),考察知识深度和广度。AI Agent + Java 后端双线并行,要求候选人既有传统 Java 功底,又了解 AI 开发。