# 荣耀 Java 面试
大家好,我是小林。
荣耀泡了好久,这几天终于开奖了!
不少同学看到开奖后的薪资还是比较满意的,给的不算低。
我从网上爆料的信息,整理了荣耀 25 届「通用开发岗」的校招薪资:
- 29k x 12 + 10w(年终奖)=44.8w ,同学 bg 未知,base 北京
- 28k x 12 + 8w(年终奖) = 41.6w,同学 bg 硕士 985,base 北京
- 27k x 12 + 10w(年终奖)= 42.4w,同学 bg 硕士 985,base 西安
- 27k x 12 + 6w(年终奖)= 38.4w,同学 bg 硕士 985,base 南京
- 26k x 12 + 8w(年终奖)= 37.2ww,同学 bg 硕士 985,base 上海
- 24k x 12 + 6w(年终奖)= 34.8w,同学 bg 硕士海龟,base 南京
- 22k x (14~16)= 30w~35w,同学 bg 本科 985,base 深圳
- 21k x (14~16)= 29w~33w,同学 bg 硕士211,base 西安
- 20k x (14~16)= 28w~32w,同学 bg 本科 985,base 深圳
如果按照 offer 档次划分的话,大概情况如下表格(PS:下面档次区分是我自己划分的,并不一定完全准确,仅供参考)
整体来看,荣耀的薪资还是不错的,跟互联网大厂薪资水平差不多。
如果在南京拿到 35w 年薪 offer,那就是真的香,等同于二线城市拿一线城市的薪资了,相比在一线城市生活,除去日常开销,可存下来的钱会更多。
那话说回来,荣耀的面试难度如何?
其实荣耀面试跟华为差不多,毕竟华为跟荣耀曾经是母品牌跟子品牌的关系,虽然现在两家公司都是独立运营,但是面试风格还是相似的。
荣耀的校招面试流程:笔试->一面->测评->二面->进入录用状态。
面试共有两轮,一面会问技术八股多一些,但是问的不会特别多,一场面试大概是 10 个左右的技术问题,
。二面的话,基本就是聊天局,聊学校和个人的经历,偶尔可能会问几个技术问题。
这次,跟大家分享今年荣耀通用开发岗位的校招面经,这是一面,主要以技术八股拷打为主,大家觉得难度如何呢?
# 荣耀一面
# Java和C 语言的区别是什么?
- 首先,Java是一种面向对象的语言,强调通过对象来组织代码和数据,重点在于对事物的建模,关注“是什么”和“如何互动”,通过继承、多态等机制实现代码复用和扩展性,支持封装、继承和多态等面向对象的特性。C是一门面向过程的语言,代码的结构主要是通过功能模块(函数)来组织的,程序的执行是通过调用这些函数来实现的,重点在于对过程的描述,即如何完成某个任务。
- 其次对于不同的平台来说,Java只需要一次编译,就可以在不同的平台上面运行,对于c来说,如果在window上面编译生成的文件只能在window上运行,如果想要在Linux系统下运行,需要重新在Linux下面进行编译,原因是由于Java有自己的一套虚拟机体制,Java编译完成之后,会生成class文件,然后将生成的文件移到不同的平台时,Java虚拟机会帮助我们将class文件翻译成计算机所能识别的信息,这样就可以在不同平台运行了。
- 第三从语法结构上来说,Java去掉了c里面比较重要的指针的概念,对于指针的概念就是我们可以直接去操作计算机的物理地址,所以使用c的时候,我们需要经常去自己申请一些内存空间,用完之后还需要自己手动释放,Java的垃圾回收机制较好的解决了这一点,我们不用再去关心申请的内存释放释放,Java虚拟机会帮助我们去清理一些我们用不到的空间,从而降低了很多出错的可能。
# Java垃圾回收算法有哪些?
- 标记-清除算法:标记-清除算法分为“标记”和“清除”两个阶段,首先通过可达性分析,标记出所有需要回收的对象,然后统一回收所有被标记的对象。标记-清除算法有两个缺陷,一个是效率问题,标记和清除的过程效率都不高,另外一个就是,清除结束后会造成大量的碎片空间。有可能会造成在申请大块内存的时候因为没有足够的连续空间导致再次 GC。
- 复制算法:为了解决碎片空间的问题,出现了“复制算法”。复制算法的原理是,将内存分成两块,每次申请内存时都使用其中的一块,当内存不够时,将这一块内存中所有存活的复制到另一块上。然后将然后再把已使用的内存整个清理掉。复制算法解决了空间碎片的问题。但是也带来了新的问题。因为每次在申请内存时,都只能使用一半的内存空间。内存利用率严重不足。
- 标记-整理算法:复制算法在 GC 之后存活对象较少的情况下效率比较高,但如果存活对象比较多时,会执行较多的复制操作,效率就会下降。而老年代的对象在 GC 之后的存活率就比较高,所以就有人提出了“标记-整理算法”。标记-整理算法的“标记”过程与“标记-清除算法”的标记过程一致,但标记之后不会直接清理。而是将所有存活对象都移动到内存的一端。移动结束后直接清理掉剩余部分。
- 分代回收算法:分代收集是将内存划分成了新生代和老年代。分配的依据是对象的生存周期,或者说经历过的 GC 次数。对象创建时,一般在新生代申请内存,当经历一次 GC 之后如果对还存活,那么对象的年龄 +1。当年龄超过一定值(默认是 15,可以通过参数 -XX:MaxTenuringThreshold 来设定)后,如果对象还存活,那么该对象会进入老年代。
# Synchorized锁的具体锁的范围?
- 作用于非静态方法,锁住的是对象实例(this),每一个对象实例有一个锁。
public synchronized void method() {}
- 作用于静态方法,锁住的是类的Class对象,因为Class的相关数据存储在永久代元空间,元空间是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程。
public static synchronized void method() {}
- 作用于 Lock.class,锁住的是 Lock 的Class对象,也是全局只有一个。
synchronized (Lock.class) {}
- 作用于 this,锁住的是对象实例,每一个对象实例有一个锁。
synchronized (this) {}
- 作用于静态成员变量,锁住的是该静态成员变量对象,由于是静态变量,因此全局只有一个。
public static Object monitor = new Object();
synchronized (monitor) {}
# http和https的区别?
区别主要有以下四点:
- HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
- HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
- 两者的默认端口不一样,HTTP 默认端口号是 80,HTTPS 默认端口号是 443。
- HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。
# http是怎么建立的?
是先进行三次握手之后,http 就可以传输请求和响应了,然后如果要断开连接,就会进行四次挥手。
不过 http 1.1 开始,默认的连接方式就是长连接,一个 tcp 连接可以进行多次 http 请求和响应,减少了握手和挥手的开销。
# https又是怎么建立的?
传统的 TLS 握手基本都是使用 RSA 算法来实现密钥交换的,在将 TLS 证书部署服务端时,证书文件其实就是服务端的公钥,会在 TLS 握手阶段传递给客户端,而服务端的私钥则一直留在服务端,一定要确保私钥不能被窃取。
在 RSA 密钥协商算法中,客户端会生成随机密钥,并使用服务端的公钥加密后再传给服务端。根据非对称加密算法,公钥加密的消息仅能通过私钥解密,这样服务端解密后,双方就得到了相同的密钥,再用它加密应用消息。
我用 Wireshark 工具抓了用 RSA 密钥交换的 TLS 握手过程,你可以从下面看到,一共经历了四次握手:
TLS 第一次握手
首先,由客户端向服务器发起加密通信请求,也就是 ClientHello 请求。在这一步,客户端主要向服务器发送以下信息:
- (1)客户端支持的 TLS 协议版本,如 TLS 1.2 版本。
- (2)客户端生产的随机数(Client Random),后面用于生成「会话秘钥」条件之一。
- (3)客户端支持的密码套件列表,如 RSA 加密算法。
TLS 第二次握手
服务器收到客户端请求后,向客户端发出响应,也就是 SeverHello。服务器回应的内容有如下内容:
- (1)确认 TLS 协议版本,如果浏览器不支持,则关闭加密通信。
- (2)服务器生产的随机数(Server Random),也是后面用于生产「会话秘钥」条件之一。
- (3)确认的密码套件列表,如 RSA 加密算法。(4)服务器的数字证书。
TLS 第三次握手
客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。
如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:
- (1)一个随机数(pre-master key)。该随机数会被服务器公钥加密。
- (2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
- (3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。
上面第一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。
服务器和客户端有了这三个随机数(Client Random、Server Random、pre-master key),接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」。
TLS 第四次握手
服务器收到客户端的第三个随机数(pre-master key)之后,通过协商的加密算法,计算出本次通信的「会话秘钥」。
然后,向客户端发送最后的信息:
- (1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
- (2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。
至此,整个 TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。
# Redis除了缓存外还有哪些功能?
还可以用 Redis 实现消息队列和分布式锁。
# Redis实现分布式锁方式是什么?
分布式锁是用于分布式环境下并发控制的一种机制,用于控制某个资源在同一时刻只能被一个应用所使用。如下图所示:Redis 本身可以被多个客户端共享访问,正好就是一个共享存储系统,可以用来保存分布式锁,而且 Redis 的读写性能高,可以应对高并发的锁操作场景。Redis 的 SET 命令有个 NX 参数可以实现「key不存在才插入」,所以可以用它来实现分布式锁:
- 如果 key 不存在,则显示插入成功,可以用来表示加锁成功;
- 如果 key 存在,则会显示插入失败,可以用来表示加锁失败。
基于 Redis 节点实现分布式锁时,对于加锁操作,我们需要满足三个条件。
- 加锁包括了读取锁变量、检查锁变量值和设置锁变量值三个操作,但需要以原子操作的方式完成,所以,我们使用 SET 命令带上 NX 选项来实现加锁;
- 锁变量需要设置过期时间,以免客户端拿到锁后发生异常,导致锁一直无法释放,所以,我们在 SET 命令执行时加上 EX/PX 选项,设置其过期时间;
- 锁变量的值需要能区分来自不同客户端的加锁操作,以免在释放锁时,出现误释放操作,所以,我们使用 SET 命令设置锁变量值时,每个客户端设置的值是一个唯一值,用于标识客户端;
满足这三个条件的分布式命令如下:
SET lock_key unique_value NX PX 10000
- lock_key 就是 key 键;
- unique_value 是客户端生成的唯一的标识,区分来自不同客户端的锁操作;
- NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
- PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。
而解锁的过程就是将 lock_key 键删除(del lock_key),但不能乱删,要保证执行操作的客户端就是加锁的客户端。所以,解锁的时候,我们要先判断锁的 unique_value 是否为加锁客户端,是的话,才将 lock_key 键删除。
可以看到,解锁是有两个操作,这时就需要 Lua 脚本来保证解锁的原子性,因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,保证了锁释放操作的原子性。
// 释放锁时,先比较 unique_value 是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
这样一来,就通过使用 SET 命令和 Lua 脚本在 Redis 单节点上完成了分布式锁的加锁和解锁。
# 红黑树和B+树区别?
- **磁盘I/O次数:**对于有 N 个叶子节点的 B+Tree,其搜索复杂度为O(logdN),其中 d 表示节点允许的最大子节点个数为 d 个。在实际的应用当中, d 值是大于100的,这样就保证了,即使数据达到千万级别时,B+Tree 的高度依然维持在 3~4 次的磁盘 I/O 操作就能查询到目标数据。而红黑树是二叉树,每个父节点的儿子节点个数只能是 2 个,意味着其搜索复杂度为 O(logN),这已经比 B+Tree 高出不少,因此红黑树检索到目标数据所经历的磁盘 I/O 次数要更多。
- **数据量处理:**B+树适合处理大量数据,尤其是无法全部装入内存的情况,而红黑树更适合内存中的数据操作。
- **顺序访问:**B+树的叶子节点形成链表,使得对数据的顺序访问更为高效。而红黑树作为二叉树,顺序访问效率较低。
- **应用场景:**B+树在处理大量数据并频繁进行磁盘I/O操作的场景下更有优势,特别是在需要高效的范围查询和顺序访问时。而红黑树在内存中的数据结构和算法实现中更为常见,适用于响应时间要求严格的场合,比如 hashmap 有用红黑树,以及 epoll 底层也用了红黑树。
对了,最新的互联网大厂后端面经都会在公众号首发,别忘记关注哦!!如果你想加入百人技术交流群,扫码下方二维码回复「加群」。