了解 AOP 的原理吗?
"AOP 的底层核心其实就是动态代理。
Spring 在 Bean 生命周期的后置处理阶段,会通过 BeanPostProcessor 判断当前 Bean 是否需要增强,比如有没有 @Transactional、切面类等。
如果发现需要增强,Spring 就不会直接返回原始 Bean,而是会基于动态代理生成一个代理对象,最后放入 Spring 容器。
后续业务调用的其实是代理对象,而不是原对象。
Spring AOP 主要有两种代理方式:
JDK 动态代理
如果 Bean 实现了接口,Spring 会基于 java.lang.reflect.Proxy 生成接口代理对象。
CGLIB 动态代理
如果 Bean 没有实现接口,Spring 会使用 CGLIB 生成当前类的子类,通过重写方法实现增强。
而真正调用目标方法时,本质还是通过反射完成的。
比如 JDK 动态代理底层会进入:
java
InvocationHandler.invoke()
然后通过:
java
method.invoke(target,args)
反射调用目标方法。
java
业务代码调用方法
↓
实际调用的是代理对象的方法
↓
代理对象拦截请求
↓
进入 InvocationHandler.invoke()
↓
执行前置增强,比如开启事务/打印日志
↓
通过 method.invoke(target, args) 调用原始目标对象方法
↓
执行后置增强,比如提交事务/统计耗时
↓
如果异常,执行异常增强,比如回滚事务
↓
返回结果
数据库 SQL 优化是怎么做的?
这个前面的博客有讲具体如何去定位慢查询,大概就是数据库sql优化
MySQL 的索引有几种类型?
这个前面的博客有具体讲
索引使用的数据结构是什么?
这个前面的博客有具体讲
接口跟抽象类有什么区别?
接口:
- 支持多实现
- JDK8前只能有抽象方法,之后可以有default方法
抽象类:
- 可以有成员变量
- 可以有普通方法
Java 类只能继承一个抽象类,但可以实现多个接口
重载跟重写有什么区别?
重载:
- 发生在同一个类中
- 方法名相同
- 参数不同
- 返回类型可以不同
- 编译期决定
重写:
- 发生在父子类
- 方法必须一样
- 子类重新实现
- 运行期动态绑定
final 关键字有什么作用?
final 可以修饰:
类、方法、变量
- 修饰类:不能被继承
- 修饰变量:值不能被修改,只能在初始化时候被定义
- 如果修饰引用类型:不能重新指向,但对象内容可以改
- 修饰方法:不能被重写
重写(Override)的底层原理
重写本质上是:JVM 的动态绑定
底层核心:虚方法表
JVM会为每个类维护虚方法表
方法名→ 实际方法地址
Animal 虚方法表
speak() -> Animal.speak()
Dog 虚方法表
java
speak() -> Dog.speak()
第一步
找到:
java
a 实际引用对象Dog
第二步
找到:
Dog 虚方法表
第三步
定位:
speak()
对应的方法地址
第四步
执行:
Dog.speak()
finally 关键字的作用是什么?
finally 一般用于:
try-catch-finally
无论是否异常finally 都会执行
一般用于:
- 关闭连接
- 释放资源
- 解锁
反射有哪些作用?
反射可以:
运行时动态获取:
- 类
- 方法
- 字段
并动态调用对应的方法
Spring 大量用了反射。
比如:
- IOC
- AOP
- 注解解析
Java 怎么实现多线程编程?
- 第一:继承 Thread。
- 第二:实现 Runnable。
- 第三:实现 Callable + Future
- 第四:使用线程池创建线程
你实现过多线程相关功能吗?具体是什么?
1.我五子棋项目中,使用阻塞队列进行一个匹配机制的实现使用的多线程相关功能
2.我做过 RabbitMQ 异步消费、文档异步索引
定时任务用什么实现?
我项目里主要用:
XXL-JOB
它支持:
- 分布式调度
- 失败重试
- 日志监控
实现线程时如何保证线程安全?
1.使用synchronized/ReentrantLock
2.使用原子类(AtomicInteger这种)
3.使用volatile避免指令重排序和实现内存可见性
4.使用CAS这种轻量级锁机制
5.使用线程安全类,比如hashtable,ConcurrentMap
6.使用ThreadLocal保存每个线程的私有变量
Java 的集合有哪些?集合对应的底层数据结构是什么?
- List
- Set
- Map
- Queue
ArrayList:
动态数组
LinkedList:
双向链表
HashMap:
数组 + 链表 + 红黑树
TreeMap:
红黑树
内存里的堆跟栈有什么区别?
栈存:
- 局部变量
- 方法参数
- 方法调用信息
线程私有,速度快,自动回收
堆:
- 存对象实例,
- 线程共享
- GC 主要回收堆
JVM 内存主要分为哪几个区域?
java
1. 程序计数器(记录当前线程执行到哪条字节码指令)
2. Java 虚拟机栈(存方法运行时的数据)
3. 本地方法栈(给 Native 方法服务)
4. 堆(存对象实例)
5. 方法区(存类相关信息)
线程跟进程有什么区别?
- 进程是操作系统进行资源分配的最小单位,线程是资源调度的最小单位。
- 一个进程可以包含多个线程,同一个进程中的线程会共享
- 相比进程,线程创建和切换成本更低,通信也更方便。
