返回Notes

/ notes

快手后端日常一面

日常一面问答整理

快手后端(AI 方向)日常一面

几乎没问AI相关的东西,Java八股没咋准备。。。

基本信息

  • 岗位: Java 后端 / AI 应用开发
  • 时长: 约 50 分钟
  • 考察: Java 基础、线程池、MySQL、项目设计、并发控制、大数相乘。

一、自我介绍


二、Java 基础

Q:ErrorException 的区别与共同点?
A: 二者都继承自 ThrowableError 多为 JVM 或系统级严重问题,如 OutOfMemoryError,通常不建议业务代码处理;Exception 是程序运行中可处理的异常。Exception 分为受检异常(如 IOException)和非受检异常 / 运行时异常(RuntimeException)。

Q:Java 和 Go 的异常处理有什么区别?
A: Java 使用 try-catch-finallythrow/throws,异常可沿调用栈传播,并区分受检和非受检异常。Go 通常通过 (value, err) 返回值显式处理错误,使用 if err != nil 判断;panic/recover 仅适用于不可恢复的程序级故障。


三、Java 线程池

Q:ThreadPoolExecutor 的七个核心参数?
A: corePoolSizemaximumPoolSizekeepAliveTimeunitworkQueuethreadFactoryhandler

Q:线程池提交任务的执行流程?
A:

  1. 当前线程数小于核心线程数:创建核心线程执行;
  2. 否则任务进入阻塞队列;
  3. 队列满且线程数小于最大线程数:创建非核心线程;
  4. 队列满且达到最大线程数:执行拒绝策略。

Q:常见阻塞队列?
A: ArrayBlockingQueue(有界数组)、LinkedBlockingQueue(链表)、SynchronousQueue(不存储任务,直接交接)、PriorityBlockingQueue(优先级)、DelayQueue(延迟)。

Q:常见拒绝策略?
A: AbortPolicy(抛异常,默认)、CallerRunsPolicy(提交线程执行)、DiscardPolicy(丢弃)、DiscardOldestPolicy(丢弃最旧任务后重试)。

Q:CPU / IO 密集型任务如何设置线程数?
A: CPU 密集型通常接近 CPU 核数(常见为核数 + 1);IO 密集型因大量等待可配置更多线程,但应根据等待时间、CPU 核数及压测结果决定。不同类型任务应拆分线程池。


四、MySQL 索引

Q:MySQL 常见索引结构及特点?
A:

  • B+ 树: InnoDB 主流结构;层高低、磁盘 IO 少,叶子节点链表支持范围查询。
  • 哈希: 等值查询快;不支持范围、排序、模糊查询。
  • 二叉搜索树 / 红黑树: 树高相对更高,磁盘 IO 更多,不适合数据库索引的页存储场景。

Q:100 亿条 int 数据的 B+ 树约几层?
A: 需要先说明页大小、索引项大小、指针大小、行大小等假设。以 InnoDB 默认 16KB 页估算,每页通常可容纳数百个索引项;B+ 树 3~4 层可覆盖千万至数十亿级数据,具体取决于聚簇 / 二级索引及数据行大小。


五、项目

Q:拍照搜索流程是什么?

Q:如何降低拍照搜索耗时?

Q:超时与失败如何设计,避免用户频繁重试?


六、额度扣减与并发控制

Q:余额仅够一次调用,并发 100 个请求时如何避免超额?


七、算法:大数相乘

Q:如何实现两个超长数字字符串相乘?
A: 使用长度为 m + n 的数组模拟竖式乘法:从低位开始两层循环相乘,将结果累加到对应位置并处理进位,最后去除前导零。

public static String multiply(String num1, String num2) {
    if ("0".equals(num1) || "0".equals(num2)) return "0";

    int[] result = new int[num1.length() + num2.length()];
    for (int i = num1.length() - 1; i >= 0; i--) {
        for (int j = num2.length() - 1; j >= 0; j--) {
            int product = (num1.charAt(i) - '0') * (num2.charAt(j) - '0')
                    + result[i + j + 1];
            result[i + j + 1] = product % 10;
            result[i + j] += product / 10;
        }
    }

    StringBuilder sb = new StringBuilder();
    int i = 0;
    while (i < result.length && result[i] == 0) i++;
    while (i < result.length) sb.append(result[i++]);
    return sb.toString();
}
  • 时间复杂度:O(mn)
  • 空间复杂度:O(m+n)
  • 注意字符转数字要减 '0'