当前位置: 首页 > news >正文

Java 开发面试复盘 - 杭州 2025

一、某建筑公司

1. 有没有创建过项目?创建项目的思路是怎样的?

整体:首先是分析业务,做技术选型,搭建项目的骨架,然后添加功能,制定好任务排期,最后将代码打成 Docker 镜像,部署到云上。

1) 技术选型。

以我们的项目为例,开发框架选择了 Spring Boot,相较于 SSM,它的优势在于自动化配置、起步依赖(起步依赖的优势在于解决版本冲突)。

数据库框架选择了 JPA + Hibernate(选择理由见下文)。

数据库选择 MySQL,缓存选择 Redis,消息队列用的 RabbitMQ(可靠 + 易用)。

2) 功能设计(领域驱动设计)。

代码分层:我设计了 Controller、Manager、Service、Repository 四个层级。

Controller 层用来定义接口;Manager 层用来协调多个 Service 或调用外部接口;Service 层是领域层,负责实现单一、完整的业务逻辑;Repository 层负责做数据库操作。

3) 服务部署。

通过流水线触发构建过程,并使用 Dockerfile 将应用打包成镜像。

由于我们的镜像仓库和云平台处于不同的网络环境,所以需要以镜像文件为媒介,将其加载到云平台所在的环境,再推送到云平台。

在云平台上创建工作负载,然后运行镜像,对外提供服务。

为什么要选择 JPA + Hibernate 而不是 MyBatis Plus?

JPA + Hibernate 是一种 ORM(对象关系映射)框架,开发者操作的是 Java 对象,框架负责将其自动转换为数据库操作。更契合 DDD 的思想。

MyBatis Plus 是基于 SQL 的半自动化框架,需要编写和维护大量的 XML 或 SQL 语句。

其他原因:团队成员更熟悉 JPA + Hibernate,学习成本较低。

2. 在什么场景下用过分布式锁?锁的是什么?

在之前的职业生涯中,我参与过一个证券综合金融服务平台的开发,我们组主要负责融资融券交易模块。

融资融券交易和普通证券交易的区别在于,它属于信用交易,用户可以凭借自己的信用,从证券公司那边获得资金或者证券来做交易。

证券公司的资金和证券不是无限的,它有一个总的额度,所有的用户要共用这个总额度,这个总额度叫作核心头寸。

业务层面上,为了更好地管理和隔离风险,核心头寸被划分成了多个业务头寸,每个业务头寸可以被多个用户同时操作。换言之,核心头寸和业务头寸之间是一对多的关系,业务头寸和用户之间也是一对多的关系。

当位于多个分片上的用户同时对同一个业务头寸进行操作时,需要加分布式锁。

分布式锁锁的是业务头寸 ID。(不同头寸之间的操作是并行的,理论上只要券商划分的业务头寸足够多,大多数交易请求都不会相互阻塞)

分布式锁的实现用的是 Redisson 锁(带看门狗机制)。

【模拟】业务头寸的持久化问题。

对于融资融券业务头寸这种跨分片共享、高频访问、对一致性要求很高的共享资源,会放在分布式缓存(Redis)中。

业务头寸最终也要同步存入数据库(Oracle)中,目的是 保障数据不丢失、系统重启或新增节点时加载到 Redis 中。

在高并发场景下,同步策略为:(高性能、高吞吐量、最终一致性)

1) 交易场景下,交易服务获取分布式锁后,对 Redis 中的头寸数据进行查询和扣减,一旦 Redis 写入成功,即可认为交易成功,释放分布式锁。

2) 头寸数据更新后,通过 RabbitMQ 发送一个头寸更新的消息,将头寸的变动同步给 Oracle。(头寸更新消息设计:头寸ID + 最新的头寸值 + 时间戳)

补充:并不是头寸数据每次发生变化都要同步给 Oracle,同步的三个场景:

1) 每 30 秒自动同步一次。

2) 在交易高峰期,当待同步的、不同的头寸 ID 数量积累到 1000 个(数量阈值触发)时触发。

3) 关键事件指定触发(如日终清算)。

【模拟】放在分布式缓存中的数据,大家都原子性地修改不就好了吗?为什么还需要加分布式锁?

业务逻辑通常是一个多步操作:读-校验-写(R-C-W),必须保证整体的原子性。Redis 原子操作只能保证数字加减的原子性,无法在执行加减操作前进行复杂的业务校验。

分布式锁的作用是把 R-C-W 看作一个不可分割的整体,强制串行化执行。

3. 怎么排查死锁?

现象:CPU 飙升、QPS 暴跌、大量线程阻塞。

1) 排查数据库行级锁死锁。

登录数据库,查看数据库的死锁日志(Show Engine InnoDB Status),排查是否发生了数据库行级锁死锁。

如果发生了数据库死锁,数据库会自动进行回滚,打破循环等待,但工程师仍然需要彻底消除数据库死锁问题。

2) 排查应用内部死锁。

根据监控系统显示的异常 CPU,锁定故障服务器。然后登录故障服务器,使用 Arthas(thread -b)排查 Java 内部的循环等待死锁。

如果发生了应用内部死锁,临时解决方案是重启服务器释放占用的 CPU 和内存资源,根本解决方案是修改代码。

3) 排查分布式锁。

分布式锁死锁可能有两种情况:锁未释放和跨资源多锁死锁。

由于 Redisson 有看门狗机制,大概率不会发生锁未释放,除非看门狗失效或程序崩溃。

排查跨资源多锁死锁,首先用 Arthas 查看阻塞线程的堆栈信息(thread -n 10),确定线程卡在获取什么资源的过程中,然后做代码层面的根因分析,重构代码解决。

预防死锁的核心原则:制定并严格遵守统一的资源获取顺序,比如 Redis 客户锁 -> Redis 头寸锁 -> DB 行锁

4. 垃圾收集器,G1 和 CMS 有什么区别?G1 的表现总是优于 CMS 吗?

两者的区别就略过了。

G1 的表现是否总是优于 CMS?结论:G1 的内存开销和 CPU 开销都比较大,在小堆和低延迟场景表现不佳。

5. 假如 MySQL 中有两张表,AB 和 CD。联表查询时,通过 B 和 C 两个字段连接,where 条件中的查询字段是 A 和 D,两张表要怎么创建索引?

面试官想考察的点有两个,一个是使用覆盖索引避免回表查询,另一个是最左前缀原则,因此创建的索引是 AB 和 DC。(面试官的关键错误认识是,把 B 和 C 两个连接字段等同于了两个查询结果字段)

实际上跟 AI 讨论后得到结论,驱动表的等值查询字段优先于连接字段,被驱动表的连接字段优先于等值查询字段,所以创建的索引应该是 AB 和 CD。

6. 有没有看过 netty 源码?什么是零拷贝?

我没有看过,不太了解。

7. 一个算法问题,把一个数组里的数据,构建成一个多叉树,时间复杂度要求 O(n)。

我没听懂这个问题,面试完搜了下,面试官的意思应该是创建一个文件目录树形结构,每个节点有一个 id、一个 parentId 和一个 children 列表。

核心思路是,进行两次 O(n) 的遍历:

1) 第一次遍历,构建 HashMap。

把所有的节点存入 HashMap,实现 O(1) 级别的节点查找。

2) 第二次遍历,构建树形结构。

再次遍历所有节点,通过 HashMap 找到其父节点(O(1) 时间复杂度),然后将当前节点添加到父节点的子节点列表中。

8. 有没有运维经验?

有构建镜像、容器化部署、创建云上工作负载等经验。

二、某国际互联网金融公司(AI 岗位)

1. 是否了解 RAG?

RAG:检索增强生成。它的核心思想是,当接收到一个问题时,先从知识库中找到与问题最相关的信息,然后将问题和相关信息一起打包,生成 prompt,交给大模型,大模型会根据 prompt 生成回答。它的优势在于,回答更准确、更深入特定领域、相对于微调大模型的成本更低、可溯源。

2. 对分布式锁有什么了解?

在单个服务节点,可以使用 synchronized 锁或者 Lock 锁保证多线程之间的同步,然而在高并发场景下,多个服务进程同时访问一个共享资源时,需要加分布式锁,保证在任意时刻,只有一个服务进程能够获得锁并操作共享资源,保证数据一致性。

分布式锁可以使用 Redis 的哈希(Hash)数据结构实现:

Key:分布式锁的名称,可自定义,如 "myLock";

Value:Hash 数据结构,示例:{"uid": 锁的唯一标识符, "count": 重入的次数}。

同时,必须给分布式锁设置超时时间(TTL),超时时间可以设置为业务平均执行时长的 3~5 倍。

【模拟】为什么要设置超时时间?

对于分布式锁,上锁的代码必须放在 try 块中,解锁的代码必须放在 finally 块中。

如果在任务执行期间服务节点挂了,finally 块中的解锁代码不一定会执行。

所以必须给分布式锁设置超时时间(TTL),防止解锁失败后死锁。

【模拟】怎么保证某个线程设置的分布式锁不会被其他线程解锁?

加锁时,通常会设置一个密语,在解锁时只有对上密语才能解锁(这个密语就是锁结构中的 锁的唯一标识符)。

这个密语可以是一个 UUID,也可以是 锁的持有者的唯一标识(可使用 服务节点 ID + 线程 ID)。

【模拟】分布式锁正在使用时超时了怎么办?

为了防止这件事发生,可以启动一个守护线程,定期检查锁是否仍然存在,如果存在,自动刷新锁的过期时间,实现锁的自动续命。

【模拟】实际开发过程中使用的分布式锁。

在实际开发过程中,使用了 Redisson 分布式锁。

Redisson 分布式锁内部实现了可重入。

Redisson 分布式锁的看门狗机制,实现了锁的自动续命。

3. 怎么排查和解决死锁?

见上文。

4. 如果一个请求处理得很慢要怎么解决?

首先要做打桩测试,找出时间都花在哪里了,然后再做针对性的处理。

如果是数据库查询比较慢,可以考虑 SQL 调优,或者将热点数据保存到缓存中。

如果是数据量太大导致的,可以考虑做读写分离或者分库分表。

如果是循环次数太多,可以考虑做多线程。

如果是某个算法运算比较慢,可以针对性地考虑如何降低时间复杂度。

如果是业务流程太长,可以考虑将一些非实时的操作提出来做异步处理。

5. 对高并发的了解。

高并发问题的本质是如何处理同一时间打到服务器上的海量请求。

流量入口处,做好负载均衡。

使用缓存层,快速消化读请求。

服务层将应用拆分为无状态的微服务,保证服务的可扩展性和高可用性。

数据层实现读写分离和分库分表。

6. 对 AI 的前沿技术有什么了解?

以下是 AI 的总结:

目前 AI 领域的前沿技术主要集中在大模型(LLM/Foundation Models) 的迭代、多模态的融合、以及如何让 AI 应用更可靠。

RAG 架构 和 AI Agent 是目前最具工程价值的前沿方向。RAG 解决了 LLM 的实际落地瓶颈(知识时效性和准确性),而 Agent 则代表了我们未来会如何构建 AI 驱动的应用,是提高业务自动化水平的关键。

三、某互联网大厂

1. 讲一个你之前做过的项目,你在里边都做了什么?

讲的是微博评论情感分析系统。

目的:开发一个 AI 项目,打通 AI 应用的端到端全链路。

需求分析:训练一个文本情感分析模型,然后实现 Java 工程化,开发一个微博评论的情感分析接口(积极、消极、中性)。考虑到做单条评论的情感分析,模型分析无论从效率方面还是准确性方面,都比不上人力分析,为了使系统更有实际应用价值,所以要再开发一个批量情感分析接口,上传 excel、csv、txt 文档,读取评论列表并进行分析,最终得到一个整体的舆情分析结论。

技术学习:以看网课的形式,在 AI 工具(Gemini)的帮助下,快速补充机器学习的知识。

模型训练:首先是收集评论数据,我在网上收集了 4 万条无打标数据和 1.4 万条打标数据,分别用来做模型预训练和微调。(这里我问过 AI 助手,使用 4 万条无打标数据进行预训练,再用 1.4 万条打标数据做微调,和直接使用 5.4 万条打标数据做微调,哪个效果更好,AI 助手说先预训练的效果更好,因为这样可以让模型先学习微博评论的语言风格,再针对性地进行情感分析训练)预训练的基模型我用的是 BERT 模型(选的是一个用全词掩码方法训出来的模型 hfl/chinese-bert-wwm-ext),用 transformers 里的 Trainer 工具完成预训练,把预训练的结果模型保存下来,开始做微调。微调的时候,主要的超参数有学习率、批次大小和训练轮次,我主要通过调整训练轮次来保证训练效果,比方说设置的训练轮次是 5,在第 3 次或第 4 次的测试准确率最高,前两次可能会欠拟合,最后一次可能会过拟合,在做微调的时候,总是保存测试准确率最高的训练模型。最终的测试准确率大概在 78% 左右。微调完成后,还会做 bad case 分析,用测试数据跑一下模型推理,把分类错误的结果按照置信率排一下,然后逐个分析。比如有一个 case,它本身是客观地讲了一件负面的事情,打标结果是消极,模型判定是中性,置信率很高,这种情况可以认为模型判断是正确的,是数据打标有问题;还有的情况是,评论本身的感情比较细腻,模型给了一个错误的分类,但置信率比较低,这种情况也是可以接受的,因为模型对预测结果也没有信心,对这种情况,在做舆情分析的时候可以适当地降低权重。

Java 工程化:把训练好的模型导出为 ONNX 格式,然后在 Java 代码中加载模型,提供评论情感分析的能力,然后做了两个 REST 接口,一个是评论的情感分析接口,一个是批量分析接口。

如果你的模型在线上投入使用了,然后出现了 bad case,你要怎么处理?

我觉得还是得继续训练模型,用更多的数据进行训练。

但训练模型是个比较缓慢的过程,根据之前的项目经验,我觉得可以在情感分析之前做一个 FAQ 问答,把 bad case 存到向量库里,在做评论的情感分析之前,先查一下有没有这条 FAQ 记录,如果有,就可以快速返回结果。

FAQ 问答不是说我必须一字不差才能查到之前存的 case,在存的时候,会先用 embedding 模型把评论转成向量,然后在查的时候,也会把评论转成向量,然后去向量库中匹配相似度较高的记录,只要相似度(余弦近似度)超过某个阈值,就可以认为是相似的评论。

你说的 FAQ,是一个临时解决的办法,用这个办法你有了缓冲时间,那你怎么彻底解决出现的 bad case?

使用 FAQ 问答积累的 bad case 来训练模型。

2. 你在前面做过的证券金融服务平台,应该对高并发,以及数据一致性要求很高吧?

简单讲了下业务流程。

你说你们的用户数据是根据用户 ID 保存在多个分片上的,那分片在进行扩展的时候,怎么把用户数据扩展到新的分片上?

在扩展分片的时候需要做数据迁移,关键和难点在于迁移过程中,如何保证业务不中断。

使用 Hash 分片的可扩展性很差,一旦分片数量发生变化,几乎所有的用户数据都需要迁移。但我们觉得,数据迁移是小概率事件,可以接受在某个集中的时间窗口期,来完成迁移工作。

如果真的需要做数据迁移,分三个步骤:

1) 开启双写:将所有的写请求,同时写入旧分片和新分片,所有的读请求仍然打到旧分片上。

2) 异步全量迁移:启动后台程序,计算所有用户数据的新位置,并将需要移动的数据从旧分片复制到新分片(分片数每次扩展都乘二,所有用户数据要么在原分片,要么在同一个新分片)。在这个过程中,双写保证了新数据不会丢失。

3) 路由切换:所有数据迁移完成并且校验无误后,瞬间切换(原子切换)路由逻辑,所有读写请求基于新的分片数执行。

3. 你之前做的 xx 项目是做什么的?你在里边是干啥的?

讲了下业务流程,以及我负责的业务。

4. 手撕代码:有一个图书馆,里边有一个图书管理员,管理了 5 本书(5 本书没有任何区别),你要模拟有 10 个读者同时来借书,每个读者拿到书后,保持 2 ~ 4 秒后还书;没有借到书的读者开始等待,等到有书后,由管理员提醒等待的读者借书。

import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;/*** 图书馆类:管理图书资源和借还书逻辑*/
class Library {// 5 本书,使用 Semaphore 来限制并发访问的数量private final Semaphore books;private final int totalBooks;public Library(int numBooks) {this.totalBooks = numBooks;// 初始化信号量,许可证数量等于书的数量this.books = new Semaphore(numBooks);System.out.println("图书馆开放,共有 " + numBooks + " 本书。");}/*** 读者借书的逻辑* @param readerName 读者的名称*/public void borrowBook(String readerName) {System.out.println(readerName + " 尝试借书...");try {// 尝试获取一个许可证,如果许可证数量为0(书都被借出),则读者等待。// 这一步自动处理了“没有借到书的读者开始等待”的需求。books.acquire(); // 成功获取许可证,表示借到书long availableBooks = books.availablePermits();System.out.println("✅ " + readerName + " 成功借到书!(剩余书本: " + availableBooks + ")");} catch (InterruptedException e) {Thread.currentThread().interrupt();System.out.println(readerName + " 借书等待被中断。");}}/*** 读者还书的逻辑* @param readerName 读者的名称*/public void returnBook(String readerName) {// 归还许可证,表示书被归还。// 这一步会自动唤醒一个在 books.acquire() 处等待的线程(读者)。books.release();long availableBooks = books.availablePermits();System.out.println("⬅️ " + readerName + " 归还了书。  (当前书本: " + availableBooks + ")");}
}/*** 读者类:模拟读者的行为*/
class Reader implements Runnable {private final String name;private final Library library;public Reader(String name, Library library) {this.name = name;this.library = library;}@Overridepublic void run() {// 1. 尝试借书library.borrowBook(name);// 2. 只有借到书的读者才会执行下面的阅读和还书操作// 延迟阅读时间try {// 随机生成 2000 ms (2秒) 到 4000 ms (4秒) 的阅读时间int readTime = ThreadLocalRandom.current().nextInt(2000, 4001); System.out.println("   ▶️ " + name + " 开始阅读,预计阅读 " + (readTime / 1000.0) + " 秒...");Thread.sleep(readTime);System.out.println("   ⏹️ " + name + " 阅读完毕。");} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 3. 还书library.returnBook(name);}}
}public class LibrarySimulation {private static final int TOTAL_BOOKS = 5;private static final int TOTAL_READERS = 10;public static void main(String[] args) {Library library = new Library(TOTAL_BOOKS);// 使用线程池来模拟 10 个读者同时来借书ExecutorService executor = Executors.newFixedThreadPool(TOTAL_READERS);for (int i = 1; i <= TOTAL_READERS; i++) {Reader reader = new Reader("读者-" + i, library);executor.execute(reader); // 提交任务给线程池}// 关闭线程池,等待所有任务完成executor.shutdown();// try {//     // 等待所有任务最多 30 秒//     if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {//         System.out.println("\n部分读者未能在规定时间内完成借还书,强制关闭。");//     } else {//         System.out.println("\n所有读者都已完成借还书过程。");//     }// } catch (InterruptedException e) {//     executor.shutdownNow();// }}
}

http://www.dtcms.com/a/463120.html

相关文章:

  • MySQL索引调优之索引顺序必须和字段顺序一致吗?
  • 国内做的比较好的跨境电商网站兰州的互联网公司
  • utf8, utf16, utf32在前256个字符是不是一样的?
  • asp.net网站开发江苏建设工程网
  • 海南州建设厅官方网站微商的自己做网站叫什么软件
  • 深圳有没有做网站的微信小程序云开发模板
  • CodeForces Round 1046(div.1)A题
  • 大连营商环境建设局网站wordpress勋章
  • 专业沈阳网站制作做网站美工要学什么软件
  • 如何设置便于搜索引擎收录的网站结构WordPress切换经典编辑器
  • 优秀网站推荐桂林网红餐厅
  • 细节判断题
  • stm32延时函数
  • 磁共振成像原理(理论)22:图像重建的常见问题 (General Issues of Image Reconstruction)
  • asp转换手机网站网页素材下载
  • 微网站建设正规公司招工网站58同城
  • 企业大型网站开发网站模板设计大岭山建设网站
  • 帝国cms做网站辽宁城乡建设集团成立网站
  • ajaxjsp网站开发从入门到精通销售客户管理软件哪个好
  • 网站的速度品牌策划招聘
  • 第一步:基于node创建一个Nest.js项目
  • wordpress template hierarchyseo平台有哪些
  • 做外贸网站建设南昌网站排名优化报
  • 冲刺校招 打卡 day04
  • mysql数据库学习之SQL优化(八)
  • 超越CNN和Transformer!Mamba结合多模态统领图像任务!
  • 开发网站那个好珠海网站制作策划
  • shtml怎么做网站为什么进不了中国建设银行网站
  • 2017 如何做网站优化wordpress实现预览
  • 02-Media-12-virtual_wbc_rtsp.py 随机位置、颜色和大小显示文本内容并同时进行RTSP推流的示例程序