华为技术有限公司OD · Java 开发2026届

华为OD Java开发面经:八股与项目追问

本文整理华为OD Java开发岗面试流程,含性格测试、资面、技术一面与技术二面。一面以HashMap、并发锁、包装类型、缓存与MySQL隔离级别为主,二面围绕项目方案演进追问,并给出分线备考与手撕语言选择建议。

更新 2026-11-06
华为ODJava开发面经八股项目追问手撕算法
27 浏览

企业概况

公司简介

华为技术有限公司成立于 1987 年,总部位于深圳,业务覆盖运营商网络、企业业务、终端、云计算与数字能源,是全球领先的 ICT 基础设施与智能终端提供商。OD(Outsourcing Dispatch)是华为与人力服务商合作的外包用工模式,由服务商签约、在华为项目组内工作,考核方式与自有员工接近,但合同主体与晋升通道不同。OD 的 Java 岗位一般落在业务系统开发与内部平台建设上。

  • 成立时间:1987 年
  • 总部:深圳
  • 主营业务:ICT 基础设施、智能终端、云计算与数字能源
  • 规模与行业地位:2024 年销售收入 8621 亿元人民币,全球通信设备第一梯队
  • 研发与专利:2024 年研发费用 1797 亿元,占收入 20.8%

岗位定位

OD 的 Java 开发岗以业务系统交付为主:接需求、做设计、写代码、联调上线,与自有员工的日常工作几乎一致。区别在劳动关系与晋升体系——由服务商签约,绩效达标且有名额时才有转正机会。对候选人来说,这类岗位的价值在于能进入体量更大、流程更规范的研发体系。

  • 所属体系:各产品线的项目组,按业务线分配
  • 核心职责:业务系统开发、接口设计、线上问题定位
  • 发展路径:OD 开发 → 高级 OD 开发 → 绩效达标后申请转正
  • 培养体系:导师制 + 项目实战,考核以交付质量与协作评价为主

OD 流程前置有在线性格测试,HR 会提示答题倾向,但不要答得过于极端。技术一面的八股密度高、覆盖面广,是主要的分水岭。手撕必须用目标岗位的语言,投 Java 岗就用 Java 写。

招聘概况

OD 招聘全年滚动,流程比校招短:简历筛选 → 性格测试 → 资面 → 技术一面 → 技术二面 → 主管面 → offer。性格测试与资面属于筛选环节,技术面才是真正的分水岭,一般两轮技术面当天或隔天完成。

  • 招聘规模:按项目组缺口滚动招聘
  • 面向专业:计算机、软件工程等相关专业,本科及以上
  • 工作地点:深圳、东莞、西安、南京、成都等
  • 投递窗口:全年开放

企业概况整理自公司、公开年报与招聘公告,具体数据以官方最新发布为准。

面试总体概述

案例背景

这位同学走的是华为 OD 通道,流程从在线性格测试开始,通过后是资面,再到技术一面与技术二面。技术一面是典型的高密度八股,覆盖 HashMap、并发、锁、缓存、数据库隔离级别,最后手撕三数之和;技术二面则完全转向项目,逐条问「为什么这么实现」并追加限制条件,最后手撕力扣 809。主管面尚未进行。

基本信息

  • 公司:华为技术有限公司
  • 岗位:OD · Java 开发
  • 用工形式 / 部门:OD(外包)
  • 面试轮次:2 轮
  • 平均时长:52 分钟
  • 题目数量:共 20 题,均附参考答案
  • 整体难度:★★★☆☆ 中等
  • 候选人背景:经历 Java 后端,具备项目开发经验
  • 是否参加笔试:是
  • 笔试形式:在线性格测评

高频考点

| 考点 | 分类 | 出现轮次 | 次数 |

| --- | --- | --- | --- |

| HashMap 底层与负载因子 | 基础八股 · 集合 | 技术一面 | 2 |

| 并发与锁(AQS / CAS / 锁分类) | 基础八股 · 并发 | 技术一面 | 3 |

| 包装类型与 equals / hashCode | 基础八股 · 语言 | 技术一面 | 3 |

| 缓存三大问题 | 基础八股 · 缓存 | 技术一面 | 1 |

| MySQL 事务与隔离级别 | 基础八股 · 数据库 | 技术一面 | 2 |

| 项目设计决策与方案演进 | 项目经历 | 技术二面 | 3 |

| 手撕(双指针 / 字符串分组) | 算法 | 技术一面、技术二面 | 2 |

备考建议

| 优先级 | 易失分点 | 准备动作 |

| --- | --- | --- |

| P0 | 八股覆盖广但都答不深,容易在并发与集合上失分 | 按「HashMap → 并发(AQS/CAS/锁)→ 集合对比 → 缓存 → 数据库」五条线各整理一页纸,每条能讲 2 分钟 |

| P0 | 包装类型比较用 == 导致代码出错 | 牢记 Integer 缓存池范围,包装类型一律用 equals 或先拆箱;把这条写进自己的编码检查清单 |

| P1 | 项目只会讲实现,答不出「为什么不选另一个方案」 | 每个关键决策做一张 A/B 对比卡:两个方案的收益、代价、适用条件 |

| P1 | 手撕时用错语言或忽略去重细节 | 按目标岗位语言刷 10 道高频题,重点练去重、边界与自测流程 |

面试流程

流程总览

| # | 环节 | 形式 | 时长 | 筛选重点 |

| --- | --- | --- | --- | --- |

| 1 | 简历投递 | 招聘平台 / 服务商渠道 | 1~2 天 | 年限与技术栈匹配 |

| 2 | 性格测试 | 在线测评 | 约 30 分钟 | 性格倾向与岗位匹配 |

| 3 | 资面 | 电话 / 视频 | 约 20 分钟 | 履历真实性、期望薪资、稳定性 |

| 4 | 技术一面 | 视频面试 | 约 60 分钟 | Java 八股广度 + 一道手撕 |

| 5 | 技术二面 | 视频面试 | 约 45 分钟 | 项目深度、方案权衡、一道手撕 |

| 6 | 主管面 | 视频面试 | 约 30 分钟 | 稳定性、协作、对 OD 的认知 |

各环节说明

1. 简历投递

由服务商的招聘顾问对接,核对年限、期望薪资与到岗时间后推给业务侧。

  • 简历突出 Java 技术栈与项目量级

2. 性格测试

对接的 HR 通常会提示答题倾向(比如稳定性、抗压、团队配合相关的维度要往积极方向答)。测试在工作日提交后一般 1~2 个工作日出结果。

  • 前后答案保持一致,别在同类题上自相矛盾
  • 不要选极端选项,除非确实符合

3. 资面

问校园或工作经历、遇到的最大困难怎么解决、期望薪资、换城市能否适应这类问题。HR 会提示回答方向,当天就能出结果。

  • 期望薪资给一个区间,并说明依据
  • 换城市的问题要提前想清楚,不要临场犹豫

4. 技术一面

面试官会顺着简历问项目,然后铺开一轮八股:集合、并发、锁、缓存、数据库,每个方向都有涉及但不深入,最后给一道力扣题。

  • 八股按「结构—原理—使用场景」三段准备
  • 手撕用目标岗位语言写,别用自己顺手的其他语言

5. 技术二面

纯项目深挖,会顺着你的实现往深里推:为什么这么设计、有没有更优方案、加限制条件后怎么做,最后一道字符串处理题。反馈很快,通常十分钟内出结果。

  • 每个技术选型准备一个被淘汰的替代方案
  • 把「加限制条件」当成常规追问来准备

6. 主管面

本次记录中该环节尚未进行。按 OD 的常规节奏,主管面会关注离职原因、加班接受度、对 OD 身份的认知与长期打算。

  • 提前想清 OD 的接受边界与转正预期
  • 准备一个能体现主动性的项目故事

第 1 轮:技术一面

面试形式与时长

  • 轮次名称:技术一面
  • 面试类型:技术面
  • 时长:60 分钟
  • 形式:视频面试
  • 本轮题量:16 题

重点考查维度

  • 基础八股 —— 本组 15 道题
  • 算法手撕 —— 本组 1 道题

八股密度很高,几乎每个方向都会碰一下:集合、并发、锁、缓存、数据库、语言基础。面试官对深度要求不算苛刻,但要能连续答下来不卡壳。手撕给的是力扣原题,本地 IDE 或共享编辑器都可能。

Q1 讲一下 HashMap 的底层原理。

基础八股 · 集合

参考答案

从结构、定位、冲突、扩容四点讲。

  • 结构:数组 + 链表 + 红黑树。JDK 8 起链表长度到 8 且数组长度不小于 64 时转红黑树,退化到 6 再转回链表。
  • 定位:key 的 hashCode 经扰动(高 16 位与低 16 位异或)后,用 (n-1) & hash 定位桶,等价于取模但更快(n 是 2 的幂)。
  • 冲突:同桶元素先比 hash 再比 equals,都用 == 优先比较引用,避免不必要的 equals 调用。
  • 扩容:默认容量 16、阈值 12,元素数超阈值翻倍。JDK 8 用高低位拆分迁移(hash & oldCap 判断放原位置还是原位置+oldCap),不再重算 hash,也不再有 1.7 头插法成环的问题。
  • 线程安全:HashMap 并发 put 会丢数据甚至死循环(1.7),并发场景用 ConcurrentHashMap。

HashMap 红黑树扩容

Q2 HashMap 的负载因子为什么是 0.75?

基础八股 · 集合

参考答案

本质是「空间与冲突概率的折中」,不是一个随意取值。

  • 定义:负载因子 = 元素数 / 桶数组长度,0.75 意味着装到 3/4 就扩容。
  • 冲突与性能:因子过高,桶内链表变长,查询从 O(1) 退化;因子过低,扩容频繁,空间浪费大。
  • 数学依据:官方注释里给了泊松分布的分析——在 0.75 的负载下,桶内元素个数服从参数约 0.5 的泊松分布,链表长度达到 8 的概率约为千万分之六,这也是树化阈值取 8 的原因。
  • 工程权衡:0.75 让平均查找长度与空间开销都比较均衡,实践中几乎不需要改。

追问预警:如果明确知道元素数量,可以在构造时指定初始容量(预估数量 / 0.75 + 1),避免多次扩容。

负载因子泊松分布

Q3 实现线程的方式有哪几种?

基础八股 · 并发

参考答案

标准说法是四种,实际开发常用的是后两种。

  • 继承 Thread:重写 run,简单但占用了唯一的继承位,不推荐。
  • 实现 Runnable:把任务与线程解耦,无返回值、不能抛受检异常。
  • 实现 Callable:有返回值、可以抛异常,配合 FutureTask 或线程池使用。
  • 使用线程池(ExecutorService):工程上的标准做法,复用线程、控制并发数、统一异常处理。

补充:严格来说只有「继承 Thread」和「实现 Runnable」是创建线程的方式,Callable 与线程池都是 Runnable 的包装。

追问预警:为什么不推荐 new Thread()——无法复用、无界创建会打爆内存,且缺少统一的生命周期管理。

线程 Callable 线程池

Q4 Callable 接口为什么有返回值?它的实现原理是什么?

基础八股 · 并发

参考答案

关键是「返回值怎么跨线程传回来」。

  • 定义:Callable 只有一个 V call() throws Exception,允许返回值和抛出受检异常,弥补 Runnable 只能 void run() 的不足。
  • 原理:Callable 本身不直接跑在线程里,要包一层 FutureTask。FutureTask 实现 Runnable,内部持有 Callable、一个 state 状态位和 outcome 结果字段。
  • 流程:run() 里调用 call(),把结果存进 outcome,把 state 从 NEW 改为 COMPLETING 再改为 NORMAL;调用 get() 时若未完成就挂到 waiters 队列上等待,完成后被唤醒并返回 outcome。
  • 异常:call 抛出的异常会被捕获并存入 outcome,get 时以 ExecutionException 形式抛出,原始异常在 cause 里。
  • 好处:把「异步执行」与「结果获取」统一成一套模型,这也是线程池 submit 能返回 Future 的原因。

Callable FutureTask 异步

Q5 讲一下 AQS 的原理

基础八股 · 并发

参考答案

AQS(AbstractQueuedSynchronizer)是 Java 并发包的基石,ReentrantLock、Semaphore、CountDownLatch 都基于它。

核心三件套:一个 volatile int state(同步状态)、一个 FIFO 双向等待队列(CLH 变体)、以及 Condition 条件队列。

  • 获取:tryAcquire 由子类实现(ReentrantLock 里就是 CAS 把 state 从 0 改成 1,重入则 state+1),失败则把当前线程包装成 Node 入队并 park。
  • 释放:tryRelease 成功后,唤醒队首的后继节点,让它重新竞争。
  • 两种模式:独占(Exclusive)与共享(Shared),CountDownLatch 用共享模式实现多个线程同时等待。
  • 公平与非公平:公平锁入队前先检查有没有前驱,非公平锁直接抢,吞吐更高但可能饿死。
  • 为什么高效:CAS 自旋 + park/unpark 阻塞,避免了无谓的 CPU 空转;队列操作只在必要时加锁。

AQS state CLH 队列

Q6 讲一下 CAS 是什么,有什么问题?

基础八股 · 并发

参考答案

CAS(Compare And Swap)是一条 CPU 原子指令,包含三个操作数:内存地址 V、期望值 A、新值 B。仅当 V 的当前值等于 A 时才把 V 更新为 B,否则失败重试。

Java 实现:Unsafe.compareAndSwapInt,底层是 cmpxchg 指令,多核下会加 lock 前缀保证原子性与可见性。

优点:无锁、不阻塞线程、没有上下文切换开销,适合竞争不激烈的场景(AtomicInteger 计数)。

  • 问题一 ABA:值从 A 改成 B 又改回 A,CAS 察觉不到。解法是加版本号(AtomicStampedReference)。
  • 问题二 自旋开销:竞争激烈时大量失败重试,CPU 空转。解法是限制自旋次数、退化为加锁,或参考 LongAdder 的分段累加。
  • 问题三 只能保证单个变量:多变量要封装成对象用 AtomicReference。

CAS ABA 原子类

Q7 缓存三剑客是什么?分别怎么解决?

基础八股 · 缓存

参考答案

指缓存穿透、缓存击穿、缓存雪崩三个典型问题。

  • 穿透:查询的 key 在缓存和数据库里都不存在,请求全部打到数据库。解法是缓存空值(设短过期时间)或用布隆过滤器提前拦截。
  • 击穿:某个热点 key 过期瞬间,大量并发同时打到数据库。解法是加互斥锁(只让一个线程回源)、逻辑过期(不设物理过期,异步刷新)、或热点数据永不过期。
  • 雪崩:大量 key 在同一时刻集体过期,或缓存服务整体宕机。解法是过期时间加随机抖动、多级缓存、缓存集群高可用、以及降级限流兜底。

补充两个常被一起问的:

  • 双写一致性:常用「先更新数据库再删缓存」,配合延迟双删或 binlog 订阅补偿。
  • 热 key 与大 key:本地缓存 + 拆分,避免单节点压力倾斜。

穿透 击穿 雪崩

Q8 Java 的基本数据类型有哪些?

基础八股 · 语言基础

参考答案

八种,按是否带小数分两类。

  • 整数:byte(1 字节,-128~127)、short(2 字节)、int(4 字节)、long(8 字节)。
  • 小数:float(4 字节,单精度)、double(8 字节,双精度)。
  • 字符:char(2 字节,无符号,存 Unicode 码元)。
  • 布尔:boolean(JVM 规范未规定大小,实际常按 1 字节或 4 字节处理)。

对比要点:

  • 包装类型(Integer/Long/Double…)是对象,可以参与泛型、可以为 null,但比较必须用 equals。
  • 自动装箱有缓存:Integer 默认缓存 -128~127,所以这个区间内 == 成立。
  • 金额用 BigDecimal,不要用 double——二进制浮点无法精确表示 0.1。

基本类型 装箱 BigDecimal

Q9 HashSet 的底层原理是什么?

基础八股 · 集合

参考答案

一句话:HashSet 内部就是一个 HashMap,元素作为 key,value 是一个固定的占位对象(PRESENT)。

  • 添加:add(e) 实际调用 map.put(e, PRESENT),返回值为 null 表示新增成功,非 null 表示元素已存在。
  • 去重:靠 HashMap 的 key 唯一性——先比 hash,再用 equals 判断,所以自定义对象必须同时重写 hashCode 与 equals,只重写一个会出现「重复元素加不进去」或「不同对象被判为重复」。
  • 无序:存取顺序与哈希分布有关,不保证插入顺序;要保序用 LinkedHashSet,要排序用 TreeSet。
  • 复杂度:平均 O(1),最坏(大量冲突退化为链表)O(n)。
  • 非线程安全:并发场景用 Collections.newSetFromMap(new ConcurrentHashMap<>())。

HashSet HashMap equals

Q10 ArrayList 和 LinkedList 有什么区别?

基础八股 · 集合

参考答案

从底层结构推到使用场景。

  • 结构:ArrayList 是动态数组,内存连续;LinkedList 是双向链表,每个节点额外存前后指针。
  • 随机访问:ArrayList O(1);LinkedList O(n),必须从头或尾遍历。
  • 增删:ArrayList 在尾部追加均摊 O(1),中间插入要移动元素 O(n);LinkedList 定位到位置本身是 O(n),定位后改指针 O(1),所以「LinkedList 增删快」只在已有节点引用时成立。
  • 内存:ArrayList 有扩容时的数组拷贝与空间预留;LinkedList 每个节点有额外指针开销,且节点分散不利于 CPU 缓存。
  • 实践建议:绝大多数场景用 ArrayList,即使频繁在头部操作;需要队列语义用 ArrayDeque,它比 LinkedList 更快。

ArrayList LinkedList ArrayDeque

Q11 Java 里的锁有哪几种?它们有什么区别?

基础八股 · 并发

参考答案

按不同维度分类,别混着讲。

  • 按是否阻塞:悲观锁(先加锁再操作,如 synchronized、ReentrantLock)与乐观锁(CAS 或版本号,冲突时重试)。
  • 按特性:公平锁 / 非公平锁(能否插队)、可重入锁 / 不可重入锁、独享锁 / 共享锁(读锁是共享)、分段锁(ConcurrentHashMap 1.7 的思想)。
  • 按实现:synchronized(JVM 层面,monitor 实现,1.6 后有偏向锁、轻量级锁、重量级锁的升级)、ReentrantLock(AQS 实现,可中断、可超时、可多条件)、读写锁 ReentrantReadWriteLock、StampedLock(乐观读)。
  • 选择建议:简单同步用 synchronized(JVM 会优化);需要可中断、超时、公平或条件变量时用 ReentrantLock;读多写少用读写锁。

追问预警:synchronized 的锁升级过程与对象头 Mark Word 的关系。

synchronized ReentrantLock 锁升级

Q12 Integer 为什么小于 128 的数用 == 比较结果是一致的?

基础八股 · 装箱

参考答案

因为 Integer 内部有缓存池。

  • 机制:Integer.valueOf(int) 在 -128~127 范围内返回 IntegerCache.cache[i + 128] 这个预先创建好的对象,超出范围才 new Integer(i)。
  • 所以两个 100 的 Integer 指向同一个缓存对象,== 比较引用自然为 true;而 200 会各自 new,== 为 false。
  • 为什么是 -128~127:这是 byte 的取值范围,覆盖了最常见的计数场景;上限可用 -XX:AutoBoxCacheMax 调整。
  • 正确做法:包装类型比较值一律用 equals,或用 Objects.equals;也可以先拆箱成基本类型再比。
  • 更隐蔽的坑:Integer a = 100; Integer b = 100; a == b 为 true,但 new Integer(100) == new Integer(100) 永远为 false。

这道题在实际编码里也是高频坑,三数之和对撞去重时就栽在这里。

IntegerCache 装箱 ==

Q13 equals 和 == 有什么区别?

基础八股 · 语言基础

参考答案

  • ==:对基本类型比的是值,对引用类型比的是「是否指向同一个对象」(地址)。
  • equals:Object 的默认实现就是 ==,但 String、Integer 等类重写成了比内容。所以能不能比值,取决于有没有重写。
  • 契约:重写 equals 必须同时重写 hashCode,否则放进 HashMap/HashSet 会出问题——两个 equals 相等的对象 hashCode 不同,会被分到不同的桶。
  • 五个约定:自反、对称、传递、一致、与 null 比较返回 false。
  • 实践:比较字符串用 Objects.equals(a, b),或把常量写在前面(如 "abc".equals(x))避免 NPE;枚举用 == 更安全。

追问预警:为什么重写 equals 一定要重写 hashCode——反例是两个内容相同的对象 hashCode 不同,map.get 拿不到值。

equals hashCode ==

Q14 MySQL 的 ACID 是什么?分别由什么保证?

基础八股 · 数据库

参考答案

四个特性对应四种机制。

  • 原子性(A):事务里的操作要么全成功要么全回滚。由 undo log 保证——修改前的旧值记在 undo log 里,失败时反向执行。
  • 一致性(C):事务前后数据满足业务约束。这是目的而不是手段,由 A、I、D 加上业务约束共同保证。
  • 隔离性(I):并发事务之间互不干扰。由锁(行锁、间隙锁)与 MVCC(版本链 + ReadView)共同实现。
  • 持久性(D):提交后即使断电也不丢。由 redo log + WAL 机制保证——先写日志再刷数据页,崩溃后用 redo 重放。

追问预警:redo log 与 binlog 的两阶段提交(prepare → commit),目的是保证两份日志一致,避免主从数据不一致。

ACID undo log redo log

Q15 MySQL 的事务隔离级别有哪几种?分别解决了什么问题?

基础八股 · 数据库

参考答案

四种,按隔离强度递增。

  • 读未提交(Read Uncommitted):能读到其他事务未提交的数据,会脏读,几乎不用。
  • 读已提交(Read Committed):只能读已提交的数据,解决脏读,但同一事务内两次读可能不同(不可重复读)。Oracle、PostgreSQL 的默认级别。
  • 可重复读(Repeatable Read):同一事务内多次读结果一致,解决不可重复读;MySQL 的默认级别,通过 MVCC 的 ReadView 实现。
  • 串行化(Serializable):加锁串行执行,解决幻读,但并发性能最差。

关键点:MySQL 的 RR 级别在「快照读」下靠 MVCC 避免幻读,「当前读」下靠间隙锁(Next-Key Lock)避免幻读,所以严格说 RR 也能防幻读,这是 InnoDB 与标准定义的差异。

追问预警:MVCC 的 ReadView 里四个字段(m_ids、min_trx_id、max_trx_id、creator_trx_id)怎么判断可见性。

隔离级别 MVCC 幻读

Q16 手撕:三数之和

算法手撕 · 排序 + 双指针

参考答案

题目:给定数组 nums,找出所有和为 0 且不重复的三元组。

思路:排序 + 固定第一个数 + 双指针。排序后可以跳过重复元素,也保证了双指针收缩的方向性。

class Solution {
 public List<List<Integer>> threeSum(int[] nums) {
 Arrays.sort(nums);
 List<List<Integer>> res = new ArrayList<>();
 for (int i = 0; i < nums.length - 2; i++) {
 if (nums[i] > 0) break; // 最小的都大于 0,后面不可能有解
 if (i > 0 && nums[i] == nums[i - 1]) continue; // 跳过重复的第一个数
 int l = i + 1, r = nums.length - 1;
 while (l < r) {
 int sum = nums[i] + nums[l] + nums[r];
 if (sum == 0) {
 res.add(Arrays.asList(nums[i], nums[l], nums[r]));
 while (l < r && nums[l] == nums[l + 1]) l++;
 while (l < r && nums[r] == nums[r - 1]) r--;
 l++;
 r--;
 } else if (sum < 0) {
 l++;
 } else {
 r--;
 }
 }
 }
 return res;
 }
}

易错点:① 跳过重复元素时用 `nums[i] == nums[i - 1]` 而不是 `nums[i] == nums[i + 1]`;② 双指针移动时注意边界 `l < r`;③ 判断 `nums[i] > 0` 而不是 `>= 0`;④ 如果用 List 存结果并用 indexOf 去重,注意 Integer 比较必须用 equals,不能用 ==。

复杂度:O(n²) 时间,O(1) 额外空间(不含结果集)。

本题最容易翻车的不是算法,而是用 == 比较 Integer 导致的去重失效。

排序 双指针 去重

五、第 2 轮:技术二面

5. 1 面试形式与时长

| 项目 | 内容 |

| --- | --- |

| 轮次名称 | 技术二面 |

| 面试类型 | 技术面 |

| 时长 | 45 分钟 |

| 形式 | 视频面试 |

| 本轮题量 | 4 题 |

5. 2 重点考查维度

  • 项目经历 —— 本组 3 道题
  • 算法手撕 —— 本组 1 道题

纯项目,一条一条往下问,会追加限制条件看你怎么应变。八股一个都没问,直接进入手撕环节。反馈速度极快,面完十分钟内出结果。

5. 3 面试真题与参考答案

Q1 项目逐条深挖:你当时为什么这么实现?

项目经历 · 设计决策

参考答案

二面的问法是「顺着实现反推决策」,每一层都要有一句理由。

  • 先给约束:当时的并发量、数据量、交付时间、团队技术栈,理由必须挂在约束上。
  • 再给选择:为什么用这张表/这个缓存/这个中间件,而不是另一个。
  • 给代价:这个选择带来了什么问题(一致性变弱、运维复杂),后来怎么补的。
  • 给边界:什么情况下这个方案会失效,届时的演进方向是什么。

面试官常追问「如果当时选另一个会怎样」,准备时把每个关键决策做成 A/B 对比,是最有效的办法。

提醒:不要把所有决策都说成自己拍的板,团队约束讲清楚反而更可信。

Q2 有没有更好的优化方案?如果加一些限制条件,你会怎么做?

项目经历 · 方案演进

参考答案

这类问题考的是「方案的可延展性」,按三层答。

  • 现状层:当前方案的瓶颈在哪(数据库压力、接口串行、缓存命中率低)。
  • 优化层:给 2~3 个方向并排序——加缓存、异步化、读写分离、分库分表、批量化。每个方向说清收益与改动成本。
  • 限制层:面试官加的限制通常是「不能加机器」「不能改表结构」「延迟要压到 50ms」,这时答案要随之收敛,比如只能做本地缓存 + 结果合并。
  • 收尾:给出你的推荐方案与理由,说明为什么现在不做其他方案。

一个加分细节:主动提「优化的前提是先有度量」,说清你会先看哪些指标再动手。

Q3 项目中你遇到的最大困难是什么?怎么解决的?

项目经历 · 技术难点

参考答案

选「有约束、有取舍、有结果」的难点,避免纯知识型问题。

  • 难点举例:数据迁移不能停机、缓存与库不一致、消息重复消费导致对账不平。
  • 约束:数据量多大、可接受的不一致窗口、团队人力与时间。
  • 方案:先定位根因,再给分层防护(本地消息表 + 定时补偿 + 对账兜底),最后灰度验证。
  • 结果:问题率从多少降到多少,是否有返工。
  • 沉淀:把这次的经验做成了什么规范或工具。

结尾补一句「难的不是方案,是让所有人都按同一个约定实现」,把技术问题落到协作上,二面主管很吃这一套。

Q4 手撕:力扣 809. 情感丰富的文字

算法手撕 · 双指针分组

参考答案

题目:给定字符串 s 和单词数组 words,统计 words 中有多少个单词可以由 s「扩张」得到。扩张规则是:把连续相同字符的一组(长度 n)扩成不少于 3 个且不等于原长度的任意长度,即新长度 m 满足 m ≥ 3 且 m ≠ n;若 n = 3(可以扩到 b,且 b ≠ a 自动满足);a > b 不合法。

def expressiveWords(s, words):
 def group(t):
 res = []
 i = 0
 while i < len(t):
 j = i
 while j < len(t) and t[j] == t[i]:
 j += 1
 res.append((t[i], j - i))
 i = j
 return res

 gs = group(s)
 ans = 0
 for w in words:
 gw = group(w)
 if len(gw) != len(gs):
 continue
 ok = True
 for (a, ca), (b, cb) in zip(gs, gw):
 if a != b or ca < cb or (ca > cb and ca < 3):
 ok = False
 break
 ans += 1 if ok else 0
 return ans

验证:s 为 heeellooo、words 为 ["hello","hi","helo"] 时返回 1(只有 hello 能扩成 heeellooo)。

边界:空字符串、单字符组、group 数量不等直接判不合法。复杂度 O(len(s) + 所有单词长度之和)。

分组 双指针 字符串

六、注意事项

OD 的技术面难度中等,真正拉开差距的是八股的连贯性和手撕的语言选择。

01 手撕必须用岗位语言

投 Java 岗就用 Java 写。日常用 C++ 或 Python 刷题的人,现场用 Java 会写得很别扭,尤其在集合与类型比较上容易出错。

02 包装类型比较是高频坑

三数之和对撞去重、缓存 key 比较、结果去重,只要用到 == 比 Integer 就可能出错。统一改用 equals 或先拆箱,并在自测时专门验证一次。

03 八股要能连续答下来

一面每个方向都会问,答一句就卡住很伤。按「结构—原理—场景」三段背,保证每道题都有 2 分钟的存量。

04 负载因子这类「为什么」题别只背结论

0.75 要能扯到空间与冲突的折中、泊松分布、以及树化阈值取 8 的原因,只答「官方推荐」会被追问到没话说。

05 性格测试不要前后矛盾

HR 会提示倾向,但同一维度反复出现时答案要一致。选得过于极端或自相矛盾,可能在技术面之前就被筛掉。

06 二面别只讲实现

面试官一定会问「有没有更好的方案」「加个限制你会怎么做」。准备时把每个决策的替代方案与代价写下来,别临场编。

07 资面问题提前定口径

期望薪资、换城市、到岗时间这三件事在资面就会被问到,口径定了就不要在后续环节改口。

总结收尾

这份记录的价值在于把 OD 的完整链路摊开了:性格测试与资面是筛人,技术一面是刷八股广度,技术二面是验项目真实性。一面的八股看似零散,其实集中在五条线上——集合、并发、包装类型与 equals、缓存、数据库事务,按线准备比按题准备效率高得多。二面则要习惯「加限制条件」的追问方式,随时能给出退一步的可行方案。最后提醒一句:手撕选语言这件事,现场真的会决定成败。

以上内容整理自学员和应届同学反馈的真实面经,结合华为技术有限公司的最新考情整理,仅供参考学习。祝各位求职者顺利拿到心仪 offer!

看完面经后,回到自己的简历做一轮准备

用 HI简历整理项目经历、提炼可追问亮点,再按面经高频问题准备对应回答。

免费开始写简历

常见问题

华为OD Java开发面试有几轮?

流程为简历筛选、性格测试、资面、技术一面、技术二面、主管面到offer,其中技术面一般两轮,当天或隔天完成。

技术一面主要考什么?

以高密度八股为主,覆盖HashMap底层与负载因子、并发与锁、包装类型与equals/hashCode、缓存三大问题、MySQL事务与隔离级别,最后手撕三数之和。

技术二面如何准备?

二面围绕项目逐条追问为什么这么实现,并追加限制条件,建议为每个关键决策准备A/B方案对比,说明收益、代价与适用条件。