前言
今年还没做过一个需求迭代,每天浑浑噩噩的🧔。最近又不知道写什么文章,刚好周日给掘金上的一个老哥做了模拟面试,今天突然起了我以前每次面试也会有记录的习惯。
今天就看看我以前在成都面试的公司都问哪些面试问题呢❓
主要是分享一下面试问题,答案就简单写了! 建议大家看❤目录❤就行了
面试合集
借贷宝
1. lock锁的底层原理,sychonized底层原理
markdown
- Lock` 锁基于 AQS 实现,提供了更灵活的锁操作,像可中断锁、公平锁等。
- `synchronized` 关键字基于对象头和 Monitor 实现,使用起来更为简便,由 JVM 自动管理锁的获取和释放。
2. redis怎么去存对象的;多个客户端,链接redis 如果一个key比较大,会对其他的客户端造成什么影响。
vbnet
- 用hash存储或者json字符串
- Redis 是单线程处理命令的,当处理大 key 的操作(如删除、修改)时,同时频繁的对大Key 进行修改删除操作 会造成更多的内存碎片,同时也会占用较多的 CPU 时间,并且传输的过程中大key可能会长时间占用IO, 这些问题都会降低redis性能,甚至引发网络阻塞等。
3. redis和客户端建立连接的过程
diff
- 客户端发起连接请求:客户端使用 TCP 协议向 Redis 服务器的指定 IP 地址和端口号发起连接请求。
- 服务器监听端口:Redis 服务器在启动时会监听指定的端口(默认是 6379),等待客户端的连接请求。
- 建立 TCP 连接:服务器接收到客户端的连接请求后,会与客户端建立 TCP 连接。
- 身份验证(可选):如果 Redis 服务器设置了密码,客户端需要发送 AUTH 命令进行身份验证。
- 开始通信:连接建立并验证通过后,客户端和服务器就可以开始进行命令的发送和响应的接收。
4. redis的io模型
css
Redis 使用了单线程的 I/O 多路复用模型:
1. I/O 多路复用:Redis 使用了操作系统提供的 I/O 多路复用机制(如 select、poll、epoll、kqueue 等),可以同时监听多个客户端的连接和事件。通过 I/O 多路复用,Redis 可以在一个线程中高效地处理多个客户端的请求。
2. 单线程处理:Redis 采用单线程处理命令,避免了多线程带来的锁竞争和上下文切换的开销。单线程可以顺序地处理每个客户端的请求,保证了数据的一致性和操作的原子性。
3. 事件驱动:Redis 基于事件驱动的方式处理客户端的请求。当有新的客户端连接、数据可读或可写等事件发生时,Redis 会触发相应的事件处理函数来处理这些事件。
这种 I/O 模型使得 Redis 在处理大量并发连接时具有很高的性能。
6. 通过数据库实现乐观锁和悲观锁、innodb 索引,采用B+树结构,那么数据库利用b+树结构 ,怎么做的数据库锁;
bash
- where条件通过版本号的方式实现乐观锁,利用版本号解决ABA等问题。
8. 事务的隔离级别
diff
- 读未提交、读已提交、可重复读、串行化,mysql默认可重复读
7. 加密算法之间的区别,信封加密你为什么要采用这几种加密算法
diff
- 对称AES对内容加密,非对称Ras加密对 AES 密钥加密。因为RSA加密内容有长度限制
8. redis持久化方式
diff
- RDB 持久化是把 Redis 在某个时间点的数据快照保存到磁盘上的二进制文件。这就像是给数据拍了一张照片,记录了当时的状态
- AOF 持久化是将 Redis 执行的写命令追加到文件末尾。就好像是记录了所有对数据的操作步骤。
9. redis用来存用户信息的话,怎么来保证数据的一致性问题
diff
- 关键信息,直接删除缓存,重新登录;非关键信息 直接更新数据库然后更新缓存
火花思维
1.先来手写简单的算法题
2.向List<Integer>
中插入String有哪些方法
通过反射:
java
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
// 获取 add 方法
Method addMethod = integerList.getClass().getMethod("add", Object.class);
// 插入 String 类型的元素
addMethod.invoke(integerList, "Hello");
3.如何通过AOP实现注解拦截
创建切面类,将注解作为切点,使用前置通知 或者 后置通知 或者环绕通知
4.元注解有哪些
less
@Retention,@Target,@Documented, @Inherited,@Repeatable(Java 8 引入)
5.explain 有哪关键参数,怎么通过这些参数去做SQL优化
markdown
`type`:表示查询的访问类型,即 MySQL 如何查找表中的行。常见的访问类型从好到坏依次为:
- `system`:表中只有一行记录,是 `const` 类型的特例。
- `const`:通过索引一次就可以找到匹配的记录,通常用于 `PRIMARY KEY` 或 `UNIQUE` 索引的等值查询。
- `eq_ref`:对于每个来自前面表的记录,从该表中使用唯一索引查找匹配的记录,通常用于连接查询。
- `ref`:使用非唯一索引查找匹配的记录,返回匹配某个单独值的所有行。
- `range`:使用索引进行范围查询,如 `WHERE` 子句中的 `BETWEEN`、`>`、`<` 等操作。
- `index`:扫描整个索引树来查找匹配的记录。
- `ALL`:全表扫描,即遍历表中的所有行来查找匹配的记录。
6.B+树的优势
-
磁盘 I/O 性能高效
- 高度较低:B + 树通常具有较低的树高。由于其非叶子节点存储索引信息,叶子节点存储数据,且所有叶子节点通过链表相连,这种结构使得在查找数据时,能以较少的磁盘 I/O 操作快速定位到目标数据。一般来说,B + 树的高度通常在 3 到 5 层左右,相比其他树结构,大大减少了磁盘 I/O 次数。
- 节点扇出大:B + 树的每个节点可以包含多个子节点,即扇出较大。这意味着在一次磁盘 I/O 操作中,可以读取到较多的索引信息,从而减少了查找过程中需要访问的节点数量,提高了查询效率。
-
范围查询支持好
- 叶子节点有序:B + 树的叶子节点是按照键值有序排列的,并且通过双向链表连接。这种结构使得范围查询变得非常高效,只需要从范围的起始值开始,沿着链表依次读取满足条件的节点,直到达到范围的结束值。例如,在一个存储学生成绩的数据库中,要查询成绩在 80 到 90 分之间的学生信息,B + 树可以快速定位到成绩为 80 分的叶子节点,然后沿着链表顺序查找,直到找到成绩为 90 分的节点,无需像其他无序结构那样进行全表扫描或逐个比较。
- 索引覆盖:在进行范围查询时,B + 树可以利用索引覆盖的特性,直接从索引中获取所需的数据,而无需访问数据行。如果查询的列都包含在索引中,那么只需要在 B + 树的索引结构中进行查找和遍历,避免了额外的磁盘 I/O 操作来读取数据页,进一步提高了范围查询的性能。
-
插入和删除操作稳定
- 分裂与合并规则:B + 树在插入和删除节点时,通过特定的分裂和合并规则来保持树的平衡。当一个节点插入数据导致节点满时,会进行分裂操作,将节点分成两个新的节点,并将中间键值上移到父节点;删除操作同理,当节点数据不足时,会尝试与兄弟节点合并或调整节点内容,以保持树的结构平衡。这种机制使得 B + 树在频繁的插入和删除操作下,依然能保持较好的性能,不会出现树结构的剧烈变化导致查询性能下降。
- 局部性原理:B + 树的插入和删除操作通常只影响到局部的节点,不会导致整个树结构的大规模调整。由于磁盘的读写是以页为单位的,这种局部性原理使得 B + 树在进行插入和删除操作时,能够尽量减少对磁盘其他页面的影响,降低了 I/O 开销,提高了操作的稳定性和效率。
-
数据存储紧凑
- 节点空间利用率高:B + 树的节点通常能够充分利用存储空间,因为其节点可以包含多个键值和指针。相比一些其他树结构,B + 树的节点填充率较高,一般可以达到 70% 到 90% 左右,这意味着在相同的存储空间内,可以存储更多的索引信息,从而减少了树的高度,提高了查询性能。
- 数据冗余度低:B + 树中除了叶子节点存储数据外,非叶子节点只存储索引信息,不存储实际数据,避免了数据的冗余存储。同时,叶子节点中的数据也可以通过合理的组织方式,减少不必要的空间浪费。例如,可以对数据进行压缩存储,或者采用合适的数据结构来存储重复的数据值,提高了数据存储的紧凑性和空间利用率。
7.单例模式有哪些实现方式,最优实现方式
饿汉式、懒汉式(线程安全/非安全)、双检锁、静态类部类、枚举,最优的方式枚举和静态类部类
8.volatile 、synchronized 底层如何实现得
markdown
- `volatile` 关键字的底层是通过内存屏障(Memory Barrier)来实现的。内存屏障是一种特殊的指令,用于控制指令的执行顺序和保证内存的可见性。
- synchronized在编译后的字节码中添加 `monitorenter` 和 `monitorexit` 指令
9.eureka 架构图说一下呢
10.说一下分布式可重入锁呢
看过spring源码么
自定义springboot 中的start
redis zset 底层数据结构
sql
使用了跳跃表(Skip List)和哈希表(Hash Table)两种数据结构
什么时候会执行full gc
sql
Full GC触发机制:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC之前,检查是否老年代连续空间是否大于新生代对象总空间总和,如果不成立,是否允许担保失败,如果不允许fullgc,如果允许检查老年代空间是否大于历次晋升到老年代对象的平均大小,如果小于则full gc。(担保机制1.6之前)
5.担保机制失败,发生full gc。(1.6之前) 6.1.6之后,minorGc 之前,老年代连续空间小于当前新生代 并且小于历次晋升老年代对象总和
(5)由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
1.7和1.8的jvm内存最大的区别
1.7的方法区由永久代实现(jvm),1.8由元空间实现(本地内存)
说一下类加载过程
类加载过程主要分为以下几个阶段:
- 加载(Loading)
- 查找并加载类的二进制数据 :类加载器(Class Loader)负责根据类的全限定名(如
java.lang.String
)来查找并加载对应的字节码文件。类加载器可以从不同的来源加载字节码文件,常见的来源包括本地文件系统、网络、数据库等。 - 生成 Class 对象 :将加载的字节码文件转换为
java.lang.Class
对象,这个对象是类在 JVM 中的唯一表示,后续对该类的各种操作(如创建实例、调用方法等)都需要通过这个Class
对象来进行。
- 验证(Verification)
- 文件格式验证 :验证字节码文件是否符合 JVM 规定的格式,例如是否以
0xCAFEBABE
开头,主次版本号是否在当前 JVM 支持的范围内等。 - 元数据验证 :对类的元数据(如类的继承关系、方法的签名等)进行语义分析,确保其符合 Java 语言的规范。例如,检查一个类是否继承了不允许被继承的类(如
final
类),方法的参数和返回值类型是否匹配等。 - 字节码验证:对字节码指令进行验证,确保指令的执行不会危害 JVM 的安全。例如,检查指令的操作数类型是否正确,是否会出现栈溢出等异常情况。
- 符号引用验证:在解析阶段,会将符号引用转换为直接引用,符号引用验证就是在这个过程中对符号引用的合法性进行验证,确保引用的类、方法、字段等确实存在,并且访问权限是合法的。
- 准备(Preparation)
- 为类的静态变量分配内存 :在这个阶段,JVM 会为类的静态变量(被
static
修饰的变量)分配内存空间,并将其初始化为默认值。例如,int
类型的静态变量会被初始化为 0,boolean
类型的静态变量会被初始化为false
,引用类型的静态变量会被初始化为null
等。 - 不进行显式初始化 :需要注意的是,在准备阶段只是为静态变量分配内存并设置默认值,并不会执行静态变量的显式初始化代码(如
static int a = 10;
中的= 10
部分),显式初始化代码会在初始化阶段执行。
- 解析(Resolution)
- 将符号引用转换为直接引用:在字节码文件中,类、方法、字段等的引用通常是以符号引用的形式存在的,符号引用只是一个字符串,它描述了被引用的目标,但并没有直接指向目标的内存地址。在解析阶段,JVM 会将这些符号引用转换为直接引用,即直接指向目标的内存地址,这样在后续的调用过程中就可以直接访问目标。
- 解析的时机:解析阶段可以在类加载时进行,也可以在第一次使用某个符号引用时进行,具体取决于 JVM 的实现和配置。
- 初始化(Initialization)
-
执行类的初始化代码 :在这个阶段,JVM 会执行类的初始化代码,包括静态变量的显式初始化和静态代码块(被
static
修饰的代码块)中的代码。这些代码会按照它们在类中出现的顺序依次执行。 -
类初始化的触发条件 :类的初始化是类加载过程中的最后一个阶段,只有在特定的情况下才会触发。常见的触发条件包括:创建类的实例(使用
new
关键字)、访问类的静态变量或调用类的静态方法、使用反射机制创建类的实例或访问类的成员、初始化一个类的子类等。
亚信科技
1. 基本的算法题
2. map的遍历方式有哪些
scss
keySet() 、values()、entrySet、Iterator、foreach、
3. 分布式事务
常见解决方案:
-
两阶段提交(2PC)
- 原理:将事务的提交过程分为两个阶段,即准备阶段和提交阶段。协调者(Coordinator)负责协调各个参与者(Participant)的操作。在准备阶段,协调者向参与者发送准备请求,参与者执行事务操作并将结果反馈给协调者;在提交阶段,协调者根据所有参与者的反馈决定是提交还是回滚事务,并将决定通知给各个参与者。
- 优点:实现简单,能够保证强一致性。
- 缺点:存在单点故障问题(协调者崩溃会导致整个事务无法完成)、性能较低(需要多次网络通信)、同步阻塞(参与者在等待协调者决策时会被阻塞)。
-
三阶段提交(3PC)
- 原理:在两阶段提交的基础上增加了一个预准备阶段,缓解了 2PC 的同步阻塞问题。三阶段分别是预准备阶段、准备阶段和提交阶段。在预准备阶段,协调者会询问参与者是否有能力执行事务;在准备阶段和提交阶段与 2PC 类似。
- 优点:减少了参与者的阻塞时间,提高了系统的并发性能。
- 缺点:仍然存在单点故障问题,并且在网络分区等极端情况下,可能会出现数据不一致的问题。
-
TCC(Try - Confirm - Cancel)
- 原理:将一个业务操作拆分为三个阶段,即 Try 阶段、Confirm 阶段和 Cancel 阶段。Try 阶段主要进行资源的检查和预留;Confirm 阶段对 Try 阶段预留的资源进行确认操作;Cancel 阶段对 Try 阶段预留的资源进行释放。各个阶段由业务代码自行实现。
- 优点:可以实现柔性事务,性能较高,适用于对性能要求较高的场景。
- 缺点:开发成本较高,需要编写大量的业务代码来实现三个阶段的逻辑。
-
消息最终一致性
- 原理:借助消息队列来保障事务的最终一致性。当一个业务操作完成后,发送一条消息到消息队列,其他相关服务从消息队列中消费消息并执行相应的操作。如果消费失败,可以进行重试,直到操作成功。
- 优点:实现简单,对业务代码的侵入性较小,性能较高。
- 缺点:只能保证最终一致性,在一定时间内可能会出现数据不一致的情况。
4. 常用的注解
5. 怎么将List变成线程安全的
scss
使用Collections.synchronizedList()、CopyOnWriteArrayList、自定义加锁
神策数据
1. SQL 排名有哪些函数可以实现,以及这些函数之间的区别
scss
`ROW_NUMBER()`:每一行分配一个唯一的、连续的整数排名
`RANK()`:有并列的行,它们会被赋予相同的排名,并且下一个排名会跳过相应的数量
`DENSE_RANK()`:有并列的行时,它们会被赋予相同的排名,并且下一个排名会连续递增,不会跳过
2. 说一下final关键字呢
3. bean的生命周期
4. new Object的过程
5. list排序,去重怎么做
6. 如何处理kafka消息丢失的问题
7. 分布式锁的实现
8. 函数式接口有哪些呢
其他公司(蓉笙、卡拉卡尔、九方互动、云方工程、跨客、时代新城...)
当然面试问题不止下面这些,博主只是列出了比较典型的问题,一些项目上的问题就不写了。
1. 怎么去对mysql优化
2. lock 和 synchronized区别
arduino
lock 可以更好的控制锁粒度,需要手动释放锁;synchronized关键字用到方法上代码块,使用方便
3. 单例的实现方式有哪些,最优的方式是哪种
4. bootstrap.properties 和 application.properties 的区别
5. redis 底层的数据结构
6. 类加载机制
类加载过程主要分为三个阶段:加载、链接和初始化,其中链接阶段又可细分为验证、准备和解析三个步骤
7. 用到过juc下面的哪些工具类呢
CountDownLatch、ConcurrentHashMap、Executor、AtomicInteger
8. redis集群模式的插槽有了解么
go
Redis 集群采用哈希槽(hash slot)来实现数据的分片存储,整个集群有 16384 个哈希槽(编号从 0 到 16383)。当你向 Redis 集群写入数据时,Redis 会根据键(key)计算出一个哈希值,然后通过取模运算将这个哈希值映射到 0 - 16383 范围内的一个插槽上,公式为:`slot = CRC16(key) % 16384`,其中`CRC16`是一种循环冗余校验算法,用于计算键的哈希值。集群中的每个节点负责处理一部分插槽,通过这种方式将数据均匀地分布到各个节点上。
9. mysql 索引为什么要使用B+树结构💥
不说了,这个问题10次面试8次都要问
10. 如何实现接口幂等
前端生成token 业务类型+时间搓,存到redis进行校验 是否重复提交等。
11. oauth协议授权流程
OAuth协议有多种授权流程,常见的有授权码授权流程、隐式授权流程、密码凭证授权流程和客户端凭证授权流程,以下是它们的具体介绍:
-
授权码授权流程:
- 客户端向授权服务器请求授权:客户端重定向用户到授权服务器并请求授权,请求中包含客户端标识、请求的范围等信息。
- 用户授权:资源所有者登录并授权客户端访问其受保护资源。
- 授权服务器颁发授权码:如果资源所有者同意授权,授权服务器将重定向用户到客户端,并附带一个授权码。
- 客户端通过授权码获取 AccessToken:客户端使用授权码向授权服务器请求 AccessToken,这通常通过云对云的 API 调用来完成。
- 授权服务器颁发 AccessToken:授权服务器验证授权码的有效性,若有效则颁发 AccessToken 给客户端。
- 客户端使用 AccessToken 访问资源服务器:客户端使用 AccessToken 向资源服务器请求访问受保护资源。
-
隐式授权流程:
- 客户端向授权服务器请求授权:客户端重定向用户到授权服务器并请求授权,包括客户端标识、请求的范围等信息。
- 用户授权:资源所有者登录并授权客户端访问其受保护资源。
- 授权服务器颁发 AccessToken:如果资源所有者同意授权,授权服务器将重定向用户到客户端,并在重定向的 URL 中附带 AccessToken。客户端直接从 URL 中获取 AccessToken。
- 客户端使用 AccessToken 访问资源服务器:客户端使用 AccessToken 向资源服务器请求访问受保护资源。
-
密码凭证授权流程:
- 用户提供凭证:用户直接向客户端提供自己的用户名和密码。
- 客户端请求 AccessToken:客户端使用用户提供的凭证直接向授权服务器请求 AccessToken。
- 授权服务器验证凭证并颁发 AccessToken:授权服务器验证凭证的有效性,如果验证成功,授权服务器向客户端返回 AccessToken。
-
客户端凭证授权流程:
- 客户端发送凭证:客户端直接向授权服务器发送客户端 ID 和客户端密钥。
- 授权服务器验证并颁发 AccessToken:授权服务器验证客户端的身份,如果验证成功,授权服务器向客户端返回 AccessToken
12. tcp 三次握手 四次挥手 why?
三次捂手,保证双方都知道网络互通
13.对称加密和非对称加密的效率
对称加解密效率快
14.金额用什么字段类型存储,用什么类型计算
javascript
String 存储,`BigDecimal` 计算
总结
java 的核心技术就那些,虽然好多在实际工作几乎用不到。但是没办法呀,就看谁理解得更透彻