模面2
接口和抽象类的区别
接口是对行为的抽象,抽象类是对本质的抽象。一个类具有多种功能,可以实现多个方法,但它只能是“它”,只能继承一个类。使用习惯上,我们一般利用类实现代码的复用,利用接口实现代码的规范与解耦。
接口中的default 和 static方法有什么区别
default方法表示这是它的默认实现,如果你不重写这个方法,被调用时就按照默认的走;static表示这个方法归接口所有,你不需要实现。
final关键字的作用
final关键字可以修饰类(但不能修饰抽象类)、方法、变量。
如果修饰类,表示这个类无法继承。如果修饰方法,表示这个方法无法重写。修饰变量,表示这是一个常量。
不能修饰抽象类:抽象类是为了被继承,但是final会阻止他被继承
如何创建一个不可变对象
类声明为 final(不可继承);
所有成员变量声明为 private final;
不提供修改变量的 setter 方法;
通过构造器初始化时,如果是引用类型,要进行深拷贝(Deep Copy),防止外部修改原对象。
线程池对比直接new一个线程的优点
- 能复用线程,降低初始化线程的性能开销,提高线程的响应速度
- 能应对突发流量,如果1000个请求交给系统处理,线程池可以设置最大线程数量进行限流
threadlocal可能出现的问题
内存泄漏。threadlocal的底层基于hashmap,其key是threadlocal本身,是一个弱引用对象,其值为强引用对象,其生命周期就是线程的生命周期。但是如果我们使用的是线程池,线程不会被彻底销毁,当 ThreadLocal 实例被 GC 回收后,ThreadLocalMap 中会出现 Key 为 null 的 Entry。由于 Value 与当前 Thread 之间存在强引用链路,只要线程不销毁(如在线程池中),这部分 Value 就永远无法被回收,导致内存泄漏。
拒绝策略
- 直接抛出异常
- 调用主线程执行该任务
- 直接丢弃
- 丢弃最旧任务
spring创建bean的方式
- @component注解扫描
- 基于配置类的@confuguration和@bean
- 基于配置类的@import
aop的常用注解和其实现机制,什么情况会失效
| 注解 | 作用 | 执行时机 |
|---|---|---|
@Aspect |
声明切面 | 标注在类上,表示这是一个切面类。 |
@Pointcut |
定义切入点 | 规定哪些方法需要被拦截(如:execution(* com.xxx.service.*.*(..)))。 |
@Before |
前置通知 | 在目标方法执行之前运行。 |
@After |
后置通知 | 在目标方法执行之后运行(无论成功还是抛出异常)。 |
@AfterReturning |
返回通知 | 在目标方法正常执行完毕后运行。 |
@AfterThrowing |
异常通知 | 在目标方法抛出异常后运行。 |
@Around |
环绕通知 | 最强大。手动控制目标方法的执行(joinPoint.proceed()),可修改输入输出。 |
实现机制:当你为一个bean配置了切面,spring不会直接返回原对象,而是返回代理对象
- jdk代理: 通过
java.lang.reflect.Proxy动态创建一个实现类。 - cglib代理:通过字节码增强技术,动态创建一个目标类的子类并重写方法。
失效情况:
- 自调用现象:方法A调用方法B,且只有B有AOP注解,这个时候类内部还没有创建动态对象,就会失效。
- 方法修饰符不是
public final方法或static方法- 内部类或非 Spring Bean
redis的基本数据结构
-
String:基于SDS(简单动态字符串)实现。用于单值存储、计数器、分布式锁。
-
List:基于quicklist(将多个ziplist)像链表一样连接。推送最新内容、简单消息队列(头进尾出)
-
Hash:基于ziplist(使用连续内存块)或hashtable(不连续,通过hash函数映射)。存储结构化数据
-
Set:基于intset(整数集合)或hashtable实现。对比Hash结构,核心功能就是去重。
-
ZSet:基于skiplist+hashtable或ziplist。用于排行榜、范围查询等
skiplist:(22 封私信) SkipList跳表:高效查找的利器 - 知乎
zset数据量太大,可能导致什么问题?如何优化
问题:内存占用、读写效率、运营备份、集群失效
优化思路:限制大小、设置过期时间(value无法设置过期时间,需手动删除)、切分大set
redis为什么快
基于内存、IO多路复用(避免了单线程的IO阻塞这一致命问题)
具体来说,IO多路复用就是设置一个selector不断轮询socket,等某个socket就绪后才通知线程处理该请求,没有通知时该线程会空闲
mysql为什么选择b+树
其实是问b+树的优势
b+树的非叶子节点只存储索引,叶子节点存储数据的结构,而且一个非叶子节点可以有多个子树。这使得其与其他树相比,树高更低,增删改查的效率都会更高。
b+树的叶子节点按顺序排列,使其天然支持范围查找,对应了sql中频繁的范围查找。
深分页的优化思路
深分页为什么慢?以下面一条语句来举例
1 | -- 需求:查询第 100001-100010 条订单(按创建时间排序) |
mysql会先查询100010条数据,然后舍弃前100000条。所有我们优化的核心思路就是让他查快点。从查询速度的角度思考,我们可以添加索引;从查询量的角度思考,我们可以提前计算主键值。索引的细节就是最好创建联合索引,这样索引的性价比比较高;提前计算就是先算好某一页对应的主键值,比如第二页是100000到110000这样。






