接口和抽象类的区别

接口是对行为的抽象,抽象类是对本质的抽象。一个类具有多种功能,可以实现多个方法,但它只能是“它”,只能继承一个类。使用习惯上,我们一般利用类实现代码的复用,利用接口实现代码的规范与解耦。

接口中的default 和 static方法有什么区别

default方法表示这是它的默认实现,如果你不重写这个方法,被调用时就按照默认的走;static表示这个方法归接口所有,你不需要实现。

final关键字的作用

final关键字可以修饰类(但不能修饰抽象类)、方法、变量。

如果修饰类,表示这个类无法继承。如果修饰方法,表示这个方法无法重写。修饰变量,表示这是一个常量。

不能修饰抽象类:抽象类是为了被继承,但是final会阻止他被继承

如何创建一个不可变对象

类声明为 final(不可继承);

所有成员变量声明为 private final

不提供修改变量的 setter 方法;

通过构造器初始化时,如果是引用类型,要进行深拷贝(Deep Copy),防止外部修改原对象。

线程池对比直接new一个线程的优点

  1. 能复用线程,降低初始化线程的性能开销,提高线程的响应速度
  2. 能应对突发流量,如果1000个请求交给系统处理,线程池可以设置最大线程数量进行限流

threadlocal可能出现的问题

内存泄漏。threadlocal的底层基于hashmap,其key是threadlocal本身,是一个弱引用对象,其值为强引用对象,其生命周期就是线程的生命周期。但是如果我们使用的是线程池,线程不会被彻底销毁,当 ThreadLocal 实例被 GC 回收后,ThreadLocalMap 中会出现 Key 为 null 的 Entry。由于 Value 与当前 Thread 之间存在强引用链路,只要线程不销毁(如在线程池中),这部分 Value 就永远无法被回收,导致内存泄漏。

拒绝策略

  1. 直接抛出异常
  2. 调用主线程执行该任务
  3. 直接丢弃
  4. 丢弃最旧任务

spring创建bean的方式

  1. @component注解扫描
  2. 基于配置类的@confuguration和@bean
  3. 基于配置类的@import

aop的常用注解和其实现机制,什么情况会失效

注解 作用 执行时机
@Aspect 声明切面 标注在类上,表示这是一个切面类。
@Pointcut 定义切入点 规定哪些方法需要被拦截(如:execution(* com.xxx.service.*.*(..)))。
@Before 前置通知 在目标方法执行之前运行。
@After 后置通知 在目标方法执行之后运行(无论成功还是抛出异常)。
@AfterReturning 返回通知 在目标方法正常执行完毕后运行。
@AfterThrowing 异常通知 在目标方法抛出异常后运行。
@Around 环绕通知 最强大。手动控制目标方法的执行(joinPoint.proceed()),可修改输入输出。

实现机制:当你为一个bean配置了切面,spring不会直接返回原对象,而是返回代理对象

  1. jdk代理: 通过 java.lang.reflect.Proxy 动态创建一个实现类。
  2. cglib代理:通过字节码增强技术,动态创建一个目标类的子类并重写方法。

失效情况:

  1. 自调用现象:方法A调用方法B,且只有B有AOP注解,这个时候类内部还没有创建动态对象,就会失效。
  2. 方法修饰符不是 public
  3. final 方法或 static 方法
  4. 内部类或非 Spring Bean

redis的基本数据结构

  1. String:基于SDS(简单动态字符串)实现。用于单值存储、计数器、分布式锁。

  2. List:基于quicklist(将多个ziplist)像链表一样连接。推送最新内容、简单消息队列(头进尾出)

  3. Hash:基于ziplist(使用连续内存块)或hashtable(不连续,通过hash函数映射)。存储结构化数据

  4. Set:基于intset(整数集合)或hashtable实现。对比Hash结构,核心功能就是去重。

  5. ZSet:基于skiplist+hashtable或ziplist。用于排行榜、范围查询等

skiplist:(22 封私信) SkipList跳表:高效查找的利器 - 知乎

zset数据量太大,可能导致什么问题?如何优化

问题:内存占用、读写效率、运营备份、集群失效

优化思路:限制大小、设置过期时间(value无法设置过期时间,需手动删除)、切分大set

redis为什么快

基于内存、IO多路复用(避免了单线程的IO阻塞这一致命问题)

具体来说,IO多路复用就是设置一个selector不断轮询socket,等某个socket就绪后才通知线程处理该请求,没有通知时该线程会空闲

mysql为什么选择b+树

其实是问b+树的优势

b+树的非叶子节点只存储索引,叶子节点存储数据的结构,而且一个非叶子节点可以有多个子树。这使得其与其他树相比,树高更低,增删改查的效率都会更高。

b+树的叶子节点按顺序排列,使其天然支持范围查找,对应了sql中频繁的范围查找。

深分页的优化思路

深分页为什么慢?以下面一条语句来举例

1
2
3
4
5
6
-- 需求:查询第 100001-100010 条订单(按创建时间排序)
SELECT id, order_no, create_time
FROM orders
WHERE status = 1
ORDER BY create_time DESC
LIMIT 100000, 10;

mysql会先查询100010条数据,然后舍弃前100000条。所有我们优化的核心思路就是让他查快点。从查询速度的角度思考,我们可以添加索引;从查询量的角度思考,我们可以提前计算主键值。索引的细节就是最好创建联合索引,这样索引的性价比比较高;提前计算就是先算好某一页对应的主键值,比如第二页是100000到110000这样。