面灵AI→

用友金融一面面经(Java 后端)

轮次
一面
时间
2026-10
来源
牛客网

《面试题目》

  1. 自我介绍。
  2. 你了解金融行业吗?
  3. ArrayList 和 LinkedList 有什么区别,各自的使用场景是什么?
  4. 讲一下 ACID。
  5. 事务的隔离级别有哪些?
  6. 场景题:比如 A 客户转钱给 B 客户,你会怎么设计?
  7. 你遇到过的最有难度的技术问题是什么?
  8. 你用过哪些 AI 工具?
  9. Java 的内存模型你了解吗?
  10. 高并发你了解吗,自己有实际应用过吗?
  11. 项目里的代码你负责哪些部分,代码量大概多少?
  12. OOM 你了解吗,怎么排查?
  13. 反问。

《参考解析》

ArrayList 与 LinkedList 的区别与选型。ArrayList 底层是动态数组,随机访问 O(1),尾部追加均摊 O(1),中间插入或删除要移动后续元素,复杂度 O(n),扩容按 1.5 倍复制到新数组(Arrays.copyOf),因此已知规模时应当用 new ArrayList<>(capacity) 预分配,避免反复扩容与数组复制。LinkedList 是双向链表,头尾插删 O(1),按索引访问要遍历,复杂度 O(n),每个节点额外持有前后指针,内存开销大约是数组方案的两到三倍,且节点分散分布导致 CPU 缓存命中率差。选型的判据是访问模式而不是「谁更高级」:以随机读和批量遍历为主(绝大多数业务查询结果的封装、列表分页数据)用 ArrayList;以频繁在两端增删为主(队列、栈、LRU 的链表部分)用 LinkedList,但真实工程里做队列更推荐 ArrayDeque——它比 LinkedList 更快、内存更省,这是很多人答不上来的加分点。ArrayList 还有一个常见追问:subList 返回的是视图而不是拷贝,对原列表的结构性修改会让视图失效抛 ConcurrentModificationException;以及 Arrays.asList 返回定长列表,add 会直接抛异常。

ACID 与隔离级别。原子性(Atomicity)指事务内操作要么全成功要么全回滚,靠 undo log 实现;一致性(Consistency)是事务前后数据满足业务约束,它是目的,靠前三者加业务约束(唯一索引、外键、检查条件)共同保证;隔离性(Isolation)指并发事务互不干扰,靠锁与 MVCC 实现;持久性(Durability)指提交后的修改不丢,靠 redo log 加刷盘与崩溃恢复实现。四种隔离级别从低到高是读未提交、读已提交、可重复读、串行化,分别解决脏读、不可重复读、幻读中的一部分:读未提交会脏读;读已提交解决脏读但同一事务内两次读结果可能不同;可重复读解决不可重复读,MySQL 的 InnoDB 默认就是它,并通过 MVCC 快照加间隙锁把大部分幻读也挡掉,但「先读后插」的当前读场景仍可能出现幻读,需要 SELECT ... FOR UPDATE 或串行化;串行化最安全但并发度最低。追问通常落在实现上:RC 与 RR 的区别是快照创建时机(每条语句 vs 事务第一次读),以及 RC 下间隙锁基本被禁用、只剩行锁,所以 RC 并发更好但可能出现不可重复读;binlog 为 statement 格式时 RC 需要额外的行锁保证主从一致。

A 转账给 B 的场景设计。核心是「一个事务内的两条更新加一个不变量」。单库单表的最简实现:开启事务(REQUIRES_NEW 或默认传播行为),按固定顺序加锁(比如始终先锁 id 小的账户,避免两个方向相反的转账互相等待造成死锁),用条件更新校验余额并扣减——UPDATE account SET balance = balance - #{amt} WHERE id = #{a} AND balance >= #{amt},受影响行数为 0 说明余额不足直接回滚;再给 B 加钱,最后写一条流水(金额、双方、前后余额、业务唯一号),提交。关键细节有四个:① 余额必须用 DECIMAL 或整数分存储,不能用 float;② 幂等靠业务唯一号加唯一索引,重复请求直接返回原结果,防止重试造成重复扣款;③ 流水与余额必须在同一事务里,对账任务按「余额快照 + 流水累加」定期核对;④ 跨行跨库时要引入 TCC 或可靠消息的最终一致性方案——先冻结、再确认扣减,失败走冲正,且每一步都要幂等与可查询。可以进一步说明:数据库层面也可以走乐观锁(WHERE version = ?)并在应用层有限次重试,高冲突场景则更适合排队或账户级串行化。

JMM、高并发与 OOM 排查。Java 内存模型描述的是多线程下共享变量的可见性与有序性规则:每个线程有自己的工作内存,变量副本的读写不保证立刻对其他线程可见;synchronized 通过监视器锁保证互斥与内存可见性,volatile 保证可见性并禁止特定重排但不保证复合操作原子性,final 保证安全发布;happens-before 是判断「一个操作的结果是否对另一个操作可见」的准则(程序顺序、监视器锁、volatile 写读、线程启动与终止、传递性)。被问「自己实际用过吗」,不要泛泛而谈:可以讲具体场景,比如用 ConcurrentHashMap 做本地缓存与计数、用 AtomicLong 做无锁统计、线程池隔离不同业务、用信号量限制下游并发、用 CompletableFuture 编排并行调用并设置超时与降级。

OOM 排查按「先保命、再定位」的顺序:先看是哪个区域溢出——java.lang.OutOfMemoryError: Java heap space 是堆,GC overhead limit exceeded 是回收效率过低(多为内存泄漏),Metaspace 是类加载过多(动态代理、热部署),unable to create new native thread 是线程数触顶,Direct buffer memory 是堆外内存。保命手段是提前加 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=,并在容器里把 -Xmx 与容器内存限制对齐(避免被 OOMKiller 直接杀死看不到日志);然后分析 dump:用 MAT 或 JProfiler 看支配树与直方图,找占内存最多的对象与 GC Roots 引用链,判断是「一次性加载太多」(集合没分页、批量查询、缓存无上限)还是「缓慢泄漏」(监听器没注销、ThreadLocal 没 remove、连接或流没关闭)。运行期还可以用 jstat -gcutil 看老年代增长趋势、jmap -histo:live 看对象分布,线上更常用 Arthas 的 dashboard、heapdump、vmtool。高并发场景下的经验回答应当带上量化与降级:压测得出 QPS 与 P99、限流与熔断阈值、缓存抗量、异步化非核心链路、以及故障时的降级开关。

面试节奏与项目责任。原帖提到整场只有 18 分钟、面试官问得比较基础、没有深挖项目,这通常说明是「筛选面」:问题覆盖广、深度浅,目的是快速确认候选人的技术面宽度与到岗意愿。回答策略是「每个问题先给结论再补一层原理」,把有限时间用来展示你会的深度:例如答隔离级别时主动补一句「InnoDB 默认 RR 用 MVCC 加间隙锁」,答 OOM 时主动带出 dump 与 MAT。被问「项目里你负责什么、写了多少代码」时,回答要具体到模块与接口数量而非虚报行数,说明哪些是你独立完成的、哪些是协作的,并准备一个能讲三层追问的难点(现象、定位过程、修复与防复发),这是把浅面试拉深的最有效抓手。