⼋股
华为 Java ⾯试
早在 9-10 ⽉份,华为就开展校园招聘了,⾯试⾯完之后, 就会进⼊池⼦,等待开 奖,这⼀等就是
2 个⽉,很多同学纠结 是等华为,还是先签别的,担⼼华为开不出来。
不过,华为的池⼦确实还是⽐较深的,基本上学历对味,⾯试不要太拉跨,基本上都能⾯试通过
之后进⼊池⼦。
泡池⼦的 ,中途可能还会收到 hr 的保温电话,所谓保温⾏为,是指通过⾯试后,到最后发offer 之
前,HR 会不时地给通过了⾯试的求职者 打电话,⽰意可能有offer ,先不要签其他家,你排名很还
不错,拿到华为 offer 的希望很⼤。
现在来到了 11 ⽉底,华为最近开始陆陆 续续 开奖了,不少同学跟我爆料了华为的开奖情况。
我在这⾥也给⼤家整理了 25 届华为校招开发岗的薪资情况:

13 级别:19-21k * (14-16 ),年包 30 〜33w
14 级别:22-25k * (14-16 ),年包 35 〜40w
15 级别:26-31k * (14-16 ),年包 41 〜50w
华为的薪资主要根据职级来定的,校招开发岗位职级⼀般是 13 级、14 级、15 级,再细节⼀点,
还会分 14a ,14b ,14c 。14 a 是 14 级别⾥⾯薪资最⾼的⽔平,14c 则是 14 级别⾥⾯的普通档的
薪资⽔平。
本科⽣基本是 13 级别,硕⼠⽣是 14 级别居多,个别⽐较优秀的硕⼠能拿到 15 级别。
华为⾯试流程总共是 2 轮技术⾯+1 轮 hr ⾯,在约⾯之前,还得先进⾏机试,基本都是算法题,
达到150 分就算机试通过,然后就进⾏后⾯的技术⾯试。

华为的⾯试难度相⽐互联⽹公司会简单⼀点,不会问太深的技术原理,问的题⽬也不 会很多,⼤
概都是 10 -20 个问题,相⽐互联⽹⼤⼚⼀场⾯试动不动就问 30 个问题,确实压⼒相对⼩⼀点。
今天给⼤家分享⼀位校招同学华为⼆⾯的⾯经,⾯试者的技术栈是Java ,主要问了Spring 、Java 集
合、并发、⽹络、mysql ⽅⾯的问题,并且还有⼿撕算法的过程。

⼋股
Spring 中的事务的隔离级别有哪些?
Sping 中的事务隔离级别有 5 种,它们分别 是:
DEFAULT :Spring 中默认的事务隔离级别,以连接的数据库的事务隔离级别为准;
READ_UNCOMMITTED :读未提交,也叫未提交读,该隔离级别的事务可以看到其他事 务中未
提交的数据。该隔离级别因为可以读取到其他事 务中未提交的数据,⽽未提交的数据可能会发
⽣回滚,因此我们把该级别读取到的数据称之为 脏数据,把这个问题称之为 脏读;
- READ_COMMITTED :读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事 务的数
据,因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事 务提交的结果,所以在
不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读;
- REPEATABLE_READ :可重复读,它能确保同⼀事务多次查询的结果⼀致。但也会有新的问题,
⽐如此级别的事务正在执⾏时,另⼀个事 务成功的插⼊了某条 数据,但因为它每次 查询的结果
都是⼀样的,所以会 导致查询不到这条数据,⾃⼰重复插⼊时⼜失败(因为唯⼀约束的原因)。
明明 在事务中查询不到这条信息,但⾃⼰就是插⼊不进去,这就叫幻读 (Phantom Read );
- SERIALIZABLE :串⾏化,最⾼的事务隔离级别,它会强制事务排序,使之不 会发⽣冲突,从⽽
解决了脏读、不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多。
所以,相⽐于 MySQL 的事务隔离级别,Spring 中多了⼀种 DEFAULT 的事务隔离级别。针对不同
的隔离级别,并发事务时可能发⽣的现象也会不同。

也就是说:
在「读未提交」隔离级别下,可能发⽣脏读、不可重复读和幻读现象;
在「读提交」隔离级别下,可能发⽣不可重复读和幻读现象,但是不可能发⽣脏读现象;
在「可重复读」隔离级别下,可能发⽣幻读现象,但是不可能脏 读和不可重复读现象;
在「串⾏化」隔离级别下,脏读、不可重复读和幻读现象都不可能会发⽣。
Spring 事务的传播⾏为有哪些?
在Spring 中对于事 务的传播⾏为定义了七 种类型分别 是:REQUIRED 、SUPPORTS 、
MANDATORY 、REQUIRES_NEW 、NOT_SUPPORTED 、NEVER 、NESTED 。
⽀持当前事务的:REQUIRED 、SUPPORTS 、MANDATORY ;不⽀持当前事务的:
REQUIRES_NEW 、NOT_SUPPORTED 、NEVER ,以及嵌套事务 NESTED ,其中 REQUIRED 是默认的事务传播级别。

多态解决了什么 问题?
多态是指⼦类可以替换⽗类,在实际的代码运⾏过程中,调⽤⼦类的⽅法实现。多态这种特性也
需要编程语⾔提供特殊的语法机制来实现,⽐如继承、接⼝类。
多态可以提⾼代码的扩展性和复⽤性,是很多设计 模式、设计 原则、编程技巧的代码实现基础。
⽐如策略模式、基于接⼝⽽⾮实现编程、依赖倒置原则、⾥式替换原则、利⽤多态去掉冗⻓的 if-else 语句等等
HashMap 是线程安全的吗?
hashmap 不是线程安全的,hashmap 在多线程会存在下⾯的问题:
JDK 1.7 HashMap 采⽤数组 + 链表的数据结构,多线程背景下,在数组扩容的时候,存在 Entry
链死循环和数据丢失问题。
JDK 1.8 HashMap 采⽤数组 + 链表 + 红⿊⼆叉树的数据结构,优化了 1.7 中数组扩容的⽅案,
解决了 Entry 链死循环和数据丢失问题。但是多线程背景下,put ⽅法存在数据覆盖的 问题。
如果要保证线程安全,可以通过这 些⽅法来保证:
多线程环境可以使 ⽤Collections.synchronizedMap 同步加锁的⽅式,还可以使 ⽤HashTable ,但
是同步的⽅式显然性能不达标,⽽ConurrentHashMap 更适合⾼并发场景使⽤。
ConcurrentHashmap 在JDK1.7 和1.8 的版本改动⽐较⼤,1.7 使⽤Segment+HashEntry 分段锁的⽅式实现,1.8 则抛弃了Segment ,改为使⽤CAS+synchronized+Node 实现,同样也加⼊了红⿊树,避免链表过⻓导致性能的问题。
Java 中的线程安全的集合是什么 ?
在 java.util 包中的线程安全的类主要 2 个,其他都是⾮线程安全的。
Vector :线程安全的动态数组,其内 部⽅法基本都经过synchronized 修饰,如果不需要线程安
全,并不建议选择,毕竟同步是有额外开销的。Vector 内部是使⽤对象数组来保存数据,可以
根据需要⾃动的增加容量,当数组已满时,会创建新的数组,并拷⻉原有数组数据。
Hashtable :线程安全的哈希表,HashTable 的加锁⽅法是给每个⽅法加上 synchronized 关键
字,这样锁住的是整个 Table 对象,不⽀持 null 键和值,由于同步导致的性能开销,所以已经
很少被推荐使⽤,如果要保证线程安全的哈希表,可以⽤ConcurrentHashMap 。
java.util.concurrent 包提供的都是线程安全的集合:
并发Map :
ConcurrentHashMap :它与 HashTable 的主要区别是⼆者加锁粒度的不同,在JDK1.7 ,
ConcurrentHashMap 加的是分段锁,也就是Segment 锁,每个Segment 含有整个 table 的⼀部
分,这样不同分段之间的并发操作就互不 影响。在JDK 1.8 ,它取消了Segment 字段,直接在
table 元素上加锁,实现对每⼀⾏进⾏加锁,进⼀步减⼩了并发冲突的概率。对于put 操作,如果
Key 对应的数组元素为null ,则通过CAS 操作(Compare and Swap )将其设置为当前值。如果
Key 对应的数组元素(也即链表表 头或者树的根元素)不为 null ,则对该元素使⽤ synchronized
关键字申请锁,然后进⾏操作。如果该 put 操作使 得当 前链表⻓度超过⼀定阈值,则将该链表
转换为红⿊树,从⽽提⾼寻址效率。
ConcurrentSkipListMap :实现了⼀个基于SkipList (跳表)算法的可排序的并发集合,SkipList
是⼀种可以在对数预期时间内完成搜索、插⼊、删除等操作的数据结构,通过维护多个指向其
他元素的“跳跃”链接来实现⾼效查找。
并发Set :
ConcurrentSkipListSet :是线程安全的有序的集合。底层是使⽤ConcurrentSkipListMap 实现。
CopyOnWriteArraySet :是线程安全的Set 实现,它是线程安全的⽆序的集合,可以将它理解成
线程安全的HashSet 。有意思的是,CopyOnWriteArraySet 和HashSet 虽然都继承于共同的⽗类
AbstractSet ;但是,HashSet 是通过“散列表”实现的,⽽CopyOnWriteArraySet 则是通过“动态数
组(CopyOnWriteArrayList)” 实现的,并不是散列表。并发List :
CopyOnWriteArrayList :它是 ArrayList 的线程安全的变体,其中所有写操作(add ,set 等)都
通过对底层数组进⾏全新复制来实现,允许存储 null 元素。即当对象进⾏写操作时,使⽤了
Lock 锁做同步处理,内部拷⻉了原数组,并在新数 组上进⾏添加操作,最后将新数 组替换掉 旧数组;若进⾏的读操作,则直接返回结果,操作过程中不 需要进⾏同步。
并发 Queue :
ConcurrentLinkedQueue :是⼀个适⽤于⾼并发场景下的队列,它通过⽆锁的⽅式(CAS) ,实现了⾼并发状态下的⾼性能。通常,ConcurrentLinkedQueue 的性能要好于 BlockingQueue 。
BlockingQueue :与 ConcurrentLinkedQueue 的使⽤场景不同,BlockingQueue 的主要功能并
不是在于提升⾼并发时的队列性能,⽽在于简化多线程间的数据共享。BlockingQueue 提供⼀
种读写阻塞等待的机制,即如果消费者速度较快,则 BlockingQueue 则可能被清空,此时消费
线程再试图从 BlockingQueue 读取数据时就会被阻塞。反之,如果⽣产线程较快,则
BlockingQueue 可能会被装 满,此时,⽣产线程再试图向 BlockingQueue 队列装⼊数据时,便会被阻塞等待。
并发 Deque :
LinkedBlockingDeque :是⼀个线程安全的双端队列实现。它的内部使⽤链表结构,每⼀个节
点都维护了⼀个前驱节点和⼀个后驱节点。LinkedBlockingDeque 没有进⾏读写锁的分离,因
此同⼀时间只能有⼀个线程对其进⾏操作
ConcurrentLinkedDeque :ConcurrentLinkedDeque 是⼀种基于链接节点的⽆限并发链表。可以
安全地并发执⾏插⼊、删除和访问操作。当许多线程同时访问⼀个公共 集合时,
ConcurrentLinkedDeque 是⼀个合适的选择。
ConcurrentHashMap ⽤了悲观锁还是乐观锁?
悲观锁和乐观锁都有⽤到。
添加元素时⾸先会判断容器是否为空:
如果为空则使⽤ volatile 加 CAS (乐观锁) 来初始化。
如果容器不为 空,则根据存储的元素计算该位置是否为空。
如果根据存储的元素计算结果为空,则利 ⽤ CAS (乐观锁) 设置该节点;
如果根据存储的元素计算结果不为 空,则使⽤ synchronized (悲观锁) ,然后,遍历桶中的数
据,并替换或新增节点到桶中,最后再判断是否需要转为红⿊树,这样就能保证并发访问时的
线程安全了。
乐观锁是怎样实现的?
乐观锁假设多个事 务之间很少发⽣冲突,因此在读取数据时不会加锁,⽽是在更新数 据时检查数
据的版本(如使⽤版本号或时间戳),如果版本匹配则执⾏更新操作,否则认为发⽣了冲突。
乐观锁适⽤于读多写少的场景,可以减少锁的竞争,提⾼并发性能。例如,数据库中的乐观锁机
制可以⽤于处理并发更新同⼀⾏数据的情况。
乐观锁实现⽅式可以通过 CAS 来实现。
CAS 叫做CompareAndSwap ,⽐较并交换,主要是通过处理器的指令来保证操作的原⼦性,它包
含三个 操作数:
变量内存地址 ,V表⽰
旧的预期值,A表⽰
准备设置的新值,B表⽰
当执⾏CAS 指令时,只有当V等于A时,才会⽤B去更新V的值,否则就不会执⾏更新操作。
Http1.0 和2.0 的区别是什么 ?
协议版本:Http/1.0 是较早的版本,采⽤⽂本格式进⾏通信,虽然⽀持⻓连接,但是默认是使
⽤短连接。从 http/1.1 版本开始,默认是⽤了⻓连接,Http/2.0 是较新的版本,引⼊了⼆进制格
式,以及多路复⽤等新特性。
性能:Http/1.0 每次 请求只能响应⼀个资源,多个资源需要多次请求,存在队头阻塞问题。
Http/2.0 ⽀持多路复⽤,可以在单个连接上同时传输多个请求和响 应,提⾼性能。
头部压缩:Http/1.0 每次 请求和响 应都需要携带完整的头部信息,存在较⼤的开销。Http/2.0
引⼊了头部压缩机制,减少了重复头 部信息的传输,提⾼了效率。
服务器推送:Http/1.0 需要等待客⼾端请求后才能发送响应,⽆法主动推送资源。Http/2.0 ⽀
持服务器推送,服务器可以在客⼾端请求之前将相关资源推送给客⼾端,减少等待时间。
MySQL 中的bin log 的作⽤是什么 ?
binlog 是 MySQL 的 Server 层实现的⽇志,⽤于备份恢复、主从 复制。
binlog 有 3 种格式类型,分别 是 STATEMENT (默认格式)、ROW 、 MIXED ,区别如下:
STATEMENT :每⼀条修改数 据的 SQL 都会被记录到 binlog 中(相当于记录了逻辑操作,所以
针对这种格式, binlog 可以称为逻辑⽇志),主从 复制中 slave 端再根据 SQL 语句重现。但
STATEMENT 有动态函数的问题,⽐如你⽤了 uuid 或者 now 这些函数,你在主库上执⾏的结果
并不是你在从库执⾏的结果,这种随时在变的函数会导致复制的数据不⼀致;
ROW :记录⾏数据最终被修改成什么 样了(这种格式的⽇志,就不能称为逻辑⽇志了),不会
出现 STATEMENT 下动态函数的问题。但 ROW 的缺点是每⾏数据的变化 结果都会被记录,⽐如
执⾏批量 update 语句,更新多少⾏数据就会产⽣多少条记录,使 binlog ⽂件过⼤,⽽在
STATEMENT 格式下只会记录⼀个 update 语句⽽已;
MIXED :包含了 STATEMENT 和 ROW 模式,它会根据不同的情况⾃动使⽤ ROW 模式和
STATEMENT 模式;
算法
⼀个⼆叉树,给⼀个target ,找出⼤于这个树中的节点的最⼤深度。
