Java面试问题记录(四)
四、设计模式
1、设计模式6大原则
1)单一职责(一个类和方法只做一件事)、
2)里氏替换(多态,子类可扩展父类)、
3)依赖倒置(细节依赖抽象,下层依赖上层)、
4)接口隔离(建立单一接口)、迪米特原则(最少知道,降低耦合)、
5)开闭原则(抽象架构,扩展实现)
2、创建型模式
这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。
3、结构型模式
这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。
4、行为模式
这类模式负责对象间的高效沟通和职责委派。
五、反射,代理
1、怎么实现反射调用方法
1)获取目标类的class对象
2)通过class对象获取要调用的Method对象
3)使用Method对象的invoke()方法调用目标方法
2、怎么代理一个类,有什么场景使用
1)静态代理:在编译期就确定代理类和被代理类之间的关系,代理类需要手动编写
2)JDK动态代理:在运行时动态生成代理类,需要借助Proxy和InvocationHandler
3)CGLIB动态代理:这是基于继承的动态代理,无需被代理类实现接口,通过生成被代理类的子类来实现代理。
代理方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
静态代理 | 简单直观,性能好 | 代码冗余,维护成本高 | 代理类少且固定的场景 |
JDK 动态代理 | 无需手动编写代理类,灵活 | 只能代理实现接口的类 | 基于接口的代理场景 |
CGLIB 动态代理 | 可代理没有实现接口的类 | 生成代理类耗时,性能略低 | 无接口的类代理场景 |
3、类代理的原理是什么
代理类型 | 核心原理 | 依赖条件 | 底层技术 |
---|---|---|---|
静态代理 | 编译期手动编写代理类,组合被代理对象 | 代理类与被代理类实现同一接口 | 硬编码组合 |
JDK 动态代理 | 运行时生成实现接口的代理类,转发调用 | 被代理类必须实现接口 | 反射 + 字节码生成 |
CGLIB 动态代理 | 运行时生成被代理类的子类,重写方法 | 被代理类不能是 final 类 | ASM 字节码操作 |
4、有什么框架可以做类代理
1)spring中的AOP
2)CGLIB
3)Javassist
4)mybatis(动态生成Mapper类的代理)
六、Redis
命令
1、计数命令
incr 和 decr
2、排序命令
sort
3、加锁命令
set nx
架构
1、常用的数据类型
hash,set,List,String,zset
数据类型 | 底层结构(简化) | 核心特点 | 常用命令 | 典型应用场景 |
---|---|---|---|---|
String(字符串) | 动态字符串(SDS) | 1. 最基础类型,value 可存字符串、整数、浮点数 2. 最大存储容量 512MB 3. 支持原子性增减操作 | SET /GET 、INCR /DECR 、APPEND 、EXPIRE 、MSET /MGET | 1. 缓存:存储用户信息、商品详情等 2. 计数器:文章阅读量、登录次数( INCR )3. 分布式锁: SET key value NX EX |
Hash(哈希) | 哈希表(数组 + 链表) | 1. 键值对的集合(类似 Java 的 HashMap) 2. 单个 Hash 可存储多个字段(field-value) 3. 操作单个字段时,无需加载整个 Hash | HSET /HGET 、HMSET /HMGET 、HGETALL 、HDEL 、HKEYS /HVALS | 1. 存储对象:用户信息(id 为 key,name/age/phone 为 field) 2. 购物车:用户 ID 为 key,商品 ID 为 field,数量为 value |
List(列表) | 双向链表(或压缩列表) | 1. 有序(按插入顺序)、可重复的元素集合 2. 支持首尾高效操作(时间复杂度 O (1)),中间操作低效(O (n)) 3. 可实现 “栈” 或 “队列” | LPUSH /RPUSH (增)、LPOP /RPOP (删)、LRANGE (查)、LLEN (长度) | 1. 消息队列:LPUSH 生产消息,RPOP 消费消息2. 排行榜:最新评论、最新订单(首尾插入 + 范围查询) 3. 栈 / 队列: LPUSH+LPOP (栈)、LPUSH+RPOP (队列) |
Set(集合) | 哈希表(或整数集合) | 1. 无序、不可重复的元素集合 2. 支持交集、并集、差集等集合运算(原子性) 3. 查找元素效率高(O (1)) | SADD /SREM (增删)、SMEMBERS (查所有)、SISMEMBER (判断存在)、SINTER (交集)、SUNION (并集) | 1. 标签:文章标签(一个文章多个标签,去重) 2. 社交:共同好友( SINTER )、好友推荐(SUNION )3. 去重:用户签到记录(避免重复签到) |
Sorted Set(有序集合) | 跳表(+ 哈希表) | 1. 有序(按 “分数” 排序)、不可重复的元素集合 2. 每个元素关联一个 “分数(score)”,按 score 升序排列 3. 支持按分数范围 / 排名快速查询 | ZADD (增)、ZRANGE /ZREVRANGE (按排名查)、ZSCORE (查分数)、ZREM (删)、ZCOUNT (按分数统计) | 1. 排行榜:游戏积分排名(ZADD 加分数,ZREVRANGE 查 Top N)2. 延时任务:score 设为时间戳, ZRANGEBYSCORE 取到期任务3. 范围统计:统计分数在 80-100 分的用户 |
2、数据淘汰策略
1)volatile-lru(least recently used):从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
2)volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
3)volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
4)allkeys-lru(least recently used):从数据集中移除最近最少使用的数据淘汰。
5)allkeys-random:从数据集中任意选择数据淘汰。
6)no-eviction(默认内存淘汰策略):禁止驱逐数据,当内存不足以容纳新写入数据时,新写入操作会报错。
7)volatile-lfu(least frequently used):从已设置过期时间的数据集中挑选最不经常使用的数据淘汰。
8)allkeys-lfu(least frequently used):从数据集中移除最不经常使用的数据淘汰。
3、单线程的为什么那么快
1)纯内存操作:这是最主要的原因,因为Redis数据读写都发生在内存中,访问速度很快
2)高效的IO模型:Redis使用单线程事件循环配置IO多路复用技术,让单线程可以同时处理多个IO请求,避免了多线程情况下的上下文切换和锁冲突问题。
3)优化的内部数据结构:Redis会根据数据大小和类型动态选择最合适的内部编码,在性能和空间效率之间选择最佳平衡
4)简洁高效的通信协议:Redis使用的是自己设计的RESP协议,这个协议实现简单,解析性能好,并且是二进制安全的
4、RDB和AOF的优缺点
RDB:Redis可以通过创建快照来存储内存里某个时间点的数据副本,并对其进行备份,还可以将快照复制到其他服务,用于数据同步或者数据恢复的作用。
AOF:Redis会记录每一条更改Redis数据的命令,并将其记录在AOF缓冲区中,然后写入AOF文件。
对比维度 | RDB(快照式) | AOF(日志式) |
---|---|---|
1. 数据安全性 | 劣势:存在数据丢失风险。 原因:RDB 是 “定时快照”(如每 5 分钟生成一次),若在两次快照间隔内宕机,期间新增 / 修改的数据会全部丢失。 | 优势:数据安全性更高,丢失数据可控制。 原因:AOF 支持三种刷盘策略(见下文),默认 fsync everysec (每秒刷盘一次),最多丢失 1 秒内的数据。 |
2. 文件大小 | 优势:文件体积小。 原因:RDB 是二进制压缩文件,仅保存数据最终状态,不记录中间过程(如多次修改同一键,仅存最终值)。 | 劣势:文件体积大。 原因:AOF 记录每一条写命令,即使多次修改同一键,也会保留所有历史命令(需通过 AOF 重写优化)。 |
3. 恢复速度 | 优势:恢复速度快。 原因:加载时直接读取二进制快照,无需执行命令,适合大数据量场景(如 GB 级数据恢复仅需几秒)。 | 劣势:恢复速度慢。 原因:恢复时需逐条执行 AOF 文件中的所有命令,命令越多、数据量越大,恢复时间越长(GB 级数据可能需要分钟级)。 |
4. 性能影响(写操作) | 优势:对写性能影响小。 原因:RDB 仅在 “生成快照” 时消耗资源(fork 子进程、遍历内存),平时的写操作无额外开销;但 fork 子进程可能导致短暂阻塞(尤其是内存大时,fork 耗时增加)。 | 劣势:对写性能有一定影响。 原因:每一条写命令都需要追加到 AOF 文件,且刷盘( fsync )操作会涉及磁盘 I/O;但可通过调整刷盘策略平衡性能与安全性。 |
5. 持久化策略灵活性 | 劣势:策略单一,仅支持 “定时快照”(如 save 900 1 表示 900 秒内至少 1 次写操作则触发快照),无法实现 “实时持久化”。 | 优势:策略灵活,支持三种刷盘策略(通过 appendfsync 配置):- always :每写一条命令就刷盘(最安全,性能最差);- everysec :每秒刷盘一次(默认,平衡安全与性能);- no :由操作系统决定何时刷盘(性能最好,安全性最差)。 |
6. 兼容性 | 优势:兼容性强。 原因:RDB 文件是二进制格式,几乎支持所有 Redis 版本(旧版本可加载新版本的 RDB 文件,反之亦然,需注意大版本差异)。 | 劣势:兼容性较弱。 原因:AOF 文件记录的是 Redis 命令,不同版本的命令语法可能变化(如旧版本不支持新版本的命令),可能导致恢复失败。 |
7. 主从复制支持 | 优势:主从复制初始化时,主节点会生成 RDB 文件发送给从节点,快照文件体积小,传输效率高,适合大规模集群部署。 | 劣势:主从复制初始化不依赖 AOF(默认用 RDB),若强制用 AOF 传输,文件体积大,传输耗时久,影响集群同步效率。 |
8. 运维成本 | 优势:运维简单。 原因:仅需管理一个 RDB 文件,适合定时备份(如每天凌晨生成快照并归档)。 | 劣势:运维稍复杂。 原因:需处理 AOF 重写(避免文件过大),且可能出现 AOF 文件损坏(需用 redis-check-aof 工具修复)。 |
5、持久化策略选择
1)如果Redis里存储的数据丢失一些也没有影响的话,可以考虑使用RDB
2)不建议单独使用AOF,因为时不时创建一个RDB快照,恢复速度也比AOF快,只是需要较大的内存
3)如果保存的数据安全性较高,可以将AOF和RDB混合使用
应用
1、缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级
1)缓存雪崩:大量缓存的key在同一时间段内全部失效,导致原本由缓存支撑的高并发请求全部到了数据库(随机设置过期时间)
2)缓存穿透:请求的数据在缓存和数据库中都不存在,导致每次请求都会穿透缓存到达数据库(在缓存中设置空值,布隆过滤器)
3)缓存预热:主动将热点数据提前存入缓存中,一般使用场景是在秒杀前将商品信息存到缓存
4)缓存更新:当数据库中的数据发生更新后,要同步到缓存中
5)缓存降级:在系统面临巨大压力下,主动放弃非核心数据的缓存能力,返回简化数据的策略
2、Pipeline有什么好处,为什么要用pipeline
好处
1)一次可以向客户端发送多条命令,减少往返次数,提升通信效率
2)提升批量操作的吞吐量
3)简化批量命令的编程模型
为什么要用它?
首先Redis执行命令的速度非常快,那么性能瓶颈就在网络通信上,通过Pipeline 同时请求多条命令,这样就可以提升Redis处理数据的速度
3、是否使用过Redis集群,集群的原理是什么
主从架构:由1个主节点+N个从节点组成,主节点负责写操作,从节点负责读操作,从节点通过复制同步主节点数据,实现数据备份,缺点是主节点宕机后,需手动切换从节点为主节点,无法自动故障转移
哨兵架构:由主从架构+哨兵节点(通常3个)组成,可以监控主从节点状态,当主节点宕机时,自动选举从节点升级为主节点,并通知客户端新的主节点地址,缺点是无法解决数据分片问题,单主节点内存受限
Cluster集群架构:由多个主从节点组成,主节点之间通过Gossip协议通信,支持数据分片及故障转移,适用于海量数据,高并发读写的分布式场景
4、Redis的同步机制了解么
Redis的同步机制核心是主从复制,实现目标是主节点向从节点单向同步数据。
全量同步
1)从节点发起同步请求
2)主节点生成RDB快照
3)主节点发送RDB快照给从节点
4)从节点加载RDB快照
5)主节点发送复制缓冲区命令
增量同步
1)从节点连接主节点,将自身数据的偏移量传给主节点
2)主节点从偏移量开始,将缓冲区的命令同步给从节点
七、MySQL
锁
1、全局锁
作用于整个MySQL服务器实例的锁机制,其核心作用是阻塞所有的写操作和部分读操作,确保在获得锁的期间,整个MySQL处于静止状态,不会发生任何数据变更
2、表锁
MySQL 中锁定粒度最大的一种锁(全局锁除外),是针对非索引字段加的锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁
3、行锁
MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁 ,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁
4、乐观锁、悲观锁
乐观锁是指对任何请求保持乐观,认为它们相互不会造成影响
悲观锁是指对任何请求保持悲观,认为它们每次请求都会修改数据
5、排他锁
事务在修改记录的时候获取排他锁,不允许多个事务同时获取
6、锁优化
1)优化索引,避免锁升级
2)优化事务设计,缩短锁持有时间
3)优化sql语句,减少锁竞争
事务
1、事务特征
事务是指对数据库的一次操作,要么全部成功,要么全部不成功,主要有四大特征:
1)原子性:事务是最小的执行单位,不允许被分割
2)一致性:执行事务前后,数据保持一致
3)隔离性:并发访问数据库时,线程之间互不干扰
4)持久性:一次事务对数据库的操作永久有效
2、脏读
一个事务读取数据后,且对数据产生了修改,这个修改对其他事务可见,即使当前事务没有提交,这时另一个事务读取这条数据,但是上一个事务因为某些原因被回滚,导致数据没有同步到数据库,那么另一个事务读到的就是脏数据。
3、不可重复读
一个事务读取多次同一条数据,但是这条数据被别的事务修改了,这就导致两次读取到的值不一样
4、幻读
现象与不可重复读类似,多次读取数据库时,由于别的事务插入了几条数据,导致两次读到的数据行数不一样,像是出现了幻觉。
5、事务隔离
MySQL有四种隔离级别
1)读未提交
2)读已提交
3)可重复读
4)序列化读
6、事务实现原理
实现核心主要依赖于INNDB引擎,底层是通过日志系统,锁机制和MVCC三大组件协同工作。
日志系统
undo log回滚日志,redo log重做日志,binlog归档日志
MVCC
MVCC 是一种并发控制机制,用于在多个并发事务同时读写数据库时保持数据的一致性和隔离性。它是通过在每个数据行上维护多个版本的数据来实现的。当一个事务要对数据库中的数据进行修改时,MVCC 会为该事务创建一个数据快照,而不是直接修改实际的数据行。
索引
1、聚簇索引 VS 非聚簇索引
概念 | 核心定义 | 关键特征 |
---|---|---|
聚簇索引 | 索引的 B+ 树结构与数据行的物理存储完全重合,即 索引的叶子节点就是数据行本身。 | 索引即数据,数据即索引 |
非聚簇索引 | 索引的 B+ 树结构与数据行物理存储分离,叶子节点仅存储 “索引键 + 聚簇索引键”(而非完整数据)。 | 索引与数据独立,需 “回表” 查询 |
2、最左匹配原则
在使用联合索引时,MySQL会根据索引的字段顺序,从左到右依次匹配查询条件中的字段
3、前缀索引
针对字符串类型字段(如 VARCHAR
、CHAR
、TEXT
等)的索引优化方式,它不索引字段的完整值,而是仅截取字段的前 N 个字符(前缀)创建索引。
八、框架
Spring
1、Bean的生命周期
创建 -> 赋值 -> 初始化 -> 销毁
首先,实例化一个bean对象,为bean设置相关属性和依赖,调用beanNameAware中的setBeanName方法取设置bean的名字,然后调用setBeanFactory方法设置bean对应的工厂信息。然后调用bean前置处理器中的方法,处理一些前置操作,调用初始化接口的方法,处理一些bean初始化是的操作,调用init方法,然后调用bean的后置处理器中的方法,处理一些后置操作。此时,bean已经可以被使用了,当需要销毁时,调用destroy方法
2、Bean的定义都包括什么信息
1)bean的唯一标识:beanID或者beanName
2)bean的类型信息,属于哪个class
3)bean的作用域
4)bean的构造参数
5)bean的属性
6)bean的初始化和销毁方法
7)bean的依赖关系
3、Spring 事务中的隔离级别有哪几种
DEFAULT(默认值):默认值为READ_COMMITTED。
READ_UNCOMMITTED(读未提交):允许事务读取其他事务未提交的数据,可能出现脏读
READ_COMMITTED(读已提交):事务只能读取其他事务已经提交的数据,可以避免脏读,但是可能出现不可重复读
REPEATABLE_READ(可重复读):保证同一事务内多次读取同一数据的结果一致,可以避免脏读和不可重复读,但是会出现幻读
SERIALIZABLE(序列化读):最高隔离级别,强制事务串行执行,避免所有并发问题。
4、schedule 使用
cron表达式
5、如何解决循环依赖
通过三级缓存来解决的。一级缓存存最终形态的bean,二级缓存存尚未填充属性的bean,三级缓存存最原始的bean
当Spring创建beanA后,发现A依赖于B,又去创建B,而B又依赖A,又去创建A;此时在创建的时候会发现发生了循环依赖,因为A还没初始化完成,所以一二级缓存中并没有它,然后创建一个A最原始的对象放到三级缓存中,提前暴露给B,那么B就可以创建一个未填充属性的bean放在二级缓存中,此时A可以从二级缓存中获取到B,然后初始化成功。
6、SpringMVC的工作流程
客户端向服务端发起一次请求,这个请求会先到中央处理器中,中央处理器接收到请求后会调用handlerMapping处理器映射器,在handlerMapping找到对应的controller处理,并将链路返回给中央处理器,中央处理器将链路信息传给handleradapter处理器适配器,在这里会去执行对应的controller并得到modelAndView,然后返回给中央处理器,中央处理器将modelAndView传给视图解析器,视图解析器将模型填充到view视图中,返回给中央处理器,中央处理器将view返回给客户端。
7、如何理解AOP和IOC
IOC:控制反转,就是由容器来负责控制对象的生命周期和对象之间的关系,以前是我们想要什么样的对象就自己创建,现在是想要什么对象,就去找容器拿
AOP:面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
8、Springboot的自动装配
开启自动装配的注解是enableAutoConfiguration,但是一般启动类上的注解是springbootAplication,这是一个复合注解,它里面包含了enableAutoConfiguration
自动装配的核心逻辑实际上是通过一个AutoConfigrationImportSelector类去实现的,它实现了ImportSelector接口,这个接口的作用就是收集需要导入的配置类,配合import注解就可以将相应的类导入到spring容器中。获取注入类的方法是selectImports,主要流程是,先获取注解的属性,用于后面的排除,然后获取所有需要自动装配的的配置类的路径,这一步是最关键的,然后去掉重复的配置类和需要排除的重复类,把需要自动加载的配置类路径存储起来,后面再按照路径依次加载。
9、spring cloud 的核心组件有哪些
Eureka:服务注册于发现。
Feign:基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求。
Ribbon:实现负载均衡,从一个服务的多台机器中选择一台。
Hystrix:提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
Zuul:网关管理,由 Zuul 网关转发请求给对应的服务。
10、spring ioc容器启动时会干什么?
分为两个阶段:容器启动阶段和bean实例化阶段
在容器启动阶段中先去加载配置,然后分析配置信息,将其装配到beanDefinetion中,然后还有一些后置处理,bean实例化阶段中先去实例化对象,然后装配依赖,然后是对象的其他处理,最后再注册回调接口
Dubbo
1、通信模型是什么样的
层级 | 作用 | 核心组件 / 实现 |
---|---|---|
传输层(Transport) | 负责底层数据的物理传输(字节流),处理 TCP 连接、数据读写等。 | 基于 Netty(默认)、Mina、Grizzly 等 NIO 框架 |
协议层(Protocol) | 定义数据传输的格式(协议头、序列化方式等),实现 “请求 - 响应” 语义。 | Dubbo 协议(默认)、HTTP、Hessian、gRPC 等 |
代理层(Proxy) | 对服务接口生成代理,屏蔽远程调用细节(如网络通信、序列化 / 反序列化)。 | 基于 JDK 动态代理或 CGLIB 生成代理类 |
服务层(Service) | 封装服务的注册、发现、负载均衡、容错等治理能力。 | 注册中心(ZooKeeper 等)、负载均衡策略等 |
2、Dubbo 和 Spring Cloud 有什么区别
功能模块 | Dubbo 实现 | Spring Cloud 实现 |
---|---|---|
服务注册发现 | 内置注册中心适配(支持 ZooKeeper、Nacos、Redis 等),客户端主动拉取服务列表。 | 通过 Eureka、Consul、Nacos、ZooKeeper 等组件实现,支持服务健康检查、自动剔除。 |
远程通信 | 基于 RPC 协议(默认 Dubbo 协议,支持 Hessian、gRPC 等),二进制传输,效率高。 | 早期默认基于 REST API(HTTP/JSON),后期支持 gRPC、Spring Cloud Stream 等,文本 / 二进制均可。 |
负载均衡 | 提供多种内置策略(随机、轮询、一致性哈希、最小活跃数等)。 | 集成 Ribbon(已淘汰)、Spring Cloud LoadBalancer,支持类似策略。 |
容错机制 | 支持重试、降级、熔断(基于自身机制或整合 Sentinel)。 | 依赖 Hystrix(已淘汰)、Resilience4j 等组件,提供熔断、限流、降级完整能力。 |
配置中心 | 无内置实现,需集成 Nacos、Apollo 等第三方组件。 | 提供 Spring Cloud Config、Spring Cloud Alibaba Nacos Config 等原生支持。 |
API 网关 | 无内置网关,需集成 Spring Cloud Gateway、Zuul 等。 | 提供 Spring Cloud Gateway(主流)、Zuul 等网关组件,支持路由、过滤、限流。 |
链路追踪 | 需集成 SkyWalking、Zipkin 等(无原生支持)。 | 集成 Spring Cloud Sleuth + Zipkin,原生支持分布式链路追踪。 |
服务监控 | 提供 Dubbo Admin 监控服务调用情况。 | 集成 Spring Boot Actuator + Spring Cloud Admin,监控服务健康、指标。 |
3、dubbo都支持什么协议,推荐用哪种
dubbo://(推荐)
rmi://
hessian://
http://
webservice://
thrift://
memcached://
redis://
rest://
4、Dubbo里面有哪几种节点角色
1)服务者
2)消费者
3)注册中心
4)监控中心
5)容器
5、Dubbo中怎么处理的超时断开
Dubbo 的超时控制基于 “客户端主动超时检测” 实现:客户端发起调用后,会启动一个定时器,若在指定时间内未收到服务端响应,则触发超时机制,断开当前连接(或标记请求失败),并执行预设的容错策略(如重试、降级等)。
elasticsearch
1、elasticsearch 的倒排索引是什么
倒排索引是 Elasticsearch 实现高效全文检索的核心,通过 “关键词→文档” 的映射关系,将复杂的文本查询转化为快速的关键词查找。其结构由 “词典(Term Dictionary)” 和 “postings list” 组成,配合分词、标准化、分段索引等优化机制,让 Elasticsearch 能在海量数据中实现毫秒级的全文检索。