8大基本数据类型11
| 类型 | 字节 | 范围 |
|---|---|---|
| byte | 1 | -128~127 |
| short | 2 | -32768~32767 |
| int | 4 | 常用整型 |
| long | 8 | 长整型,后缀 L |
| float | 4 | 单精度,后缀 F |
| double | 8 | 双精度,默认小数 |
| char | 2 | 单个字符 |
| boolean | 1 | true/flase |
== 和 equals 区别
==:基本比数值,引用比内存地址
equals:重写后对比对象内容
面向对象 OOP 三大特性
封装
私有化成员,提供 get/set,隐藏内部细节。
权限修饰符:private < 默认 < protected < public
java
class Person {
// 私有属性,外部无法直接访问
private String name;
private int age;
// 公开方法,对外提供访问入口
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
// 校验数据,保证合法
public void setAge(int age) {
if(age > 0){
this.age = age;
}
}
}
解释:属性私有化隐藏细节,仅通过方法操作数据,可做数据校验,避免非法赋值。
继承extends
子类复用父类代码,Java单继承。
this:当前对象
super:父类对象、父构造
java
// 父类
class Animal {
public void eat(){
System.out.println("动物进食");
}
}
// 子类继承父类,复用父类方法
class Dog extends Animal{
}
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
// 直接调用继承来的方法
dog.eat();
}
}
解释:子类继承父类属性方法,不用重复编写代码,实现代码复用。
多态
父类引用指向子类对象,编译看父类,运行看子类。
java
// 父类
class Animal {
public void shout(){
System.out.println("动物叫声");
}
}
// 子类重写方法
class Cat extends Animal{
@Override
public void shout() {
System.out.println("喵喵喵");
}
}
class Dog extends Animal{
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
public class Test {
public static void main(String[] args) {
// 父类引用指向子类对象
Animal animal1 = new Cat();
Animal animal2 = new Dog();
// 执行子类重写后的方法
animal1.shout();
animal2.shout();
}
}
解释:同一个父类行为,不同子类展现不同效果,运行时执行子类逻辑,提升程序拓展性。
重写和重载
重载 Overload同类中,方法名相同,参数个数 / 类型 / 顺序不同,返回值无关。
java
public class Demo {
// 重载方法
public void say(){}
public void say(String name){}
public void say(int age){}
}
重写 Override子类重写父类方法,方法名、参数、返回值一致;权限不能缩小。
java
class Father{
public void run(){
System.out.println("父类跑步");
}
}
class Son extends Father{
// 重写
@Override
public void run(){
System.out.println("子类快跑");
}
}
三大关键字
final
修饰类:不能被继承
修饰方法:不能重写
修饰变量:常量,只能赋值一次
static
静态变量 / 方法:属于类,所有对象共享
静态代码块:类加载只执行一次
静态不能直接调用非静态
abstract 抽象
抽象类:不能实例化,可含抽象 / 普通方法
抽象方法:无方法体,子类必须重写
final、finally、finalize 区别
final关键字,修饰类、方法、变量,表不可变、不可继承、不可重写。
finally用在 try-catch 里,无论是否异常,finally 代码一定执行。用于关闭资源。
finalizeObject 里的方法,对象被 GC 回收前调用一次,不推荐使用。
String、StringBuilder、StringBuffer
String:底层 final char[] 不可变字符串,每次修改都会生成新对象,效率低、线程安全
StringBuilder:底层 char[] 可变字符串,不生成新对象,效率最高、线程不安全
StringBuffer:底层 char[] 可变字符串,线程安全(加了锁),效率中等
性能速度
StringBuilder > StringBuffer > String
使用场景
少量字符串操作 → 用 String
单线程大量拼接、修改 → 用 StringBuilder
多线程环境大量操作 → 用 StringBuffer
synchronized 与 Lock
| 对比项 | synchronized | Lock |
|---|---|---|
| 实现层面 | JVM原生内置 | Java代码API实现 |
| 锁释放 | 代码执行完 / 异常自动释放 | 必须手动 unlock 释放 |
| 中断等待 | 不可中断 | 可中断等待线程 |
| 锁类型 | 默认非公平锁 | 支持公平 / 非公平锁切换 |
| 等待队列 | 单个队列 | 可多个条件队列 |
| 性能 | 低版本较差,新版本优化提升 | 竞争激烈时性能更优 |
关键点
异常处理
synchronized 抛异常自动释锁;Lock 必须 finally 手动解锁,死锁风险更高
公平锁
synchronized 非公平;ReentrantLock 构造传 true 开启公平锁
唤醒机制
synchronized 搭配 wait/notify;Lock 搭配 Condition 精准唤醒
适用场景
简单同步、少量并发用 synchronized;高并发、需灵活控制锁逻辑用 Lock
线程创建方式
继承 Thread、实现 Runnable、实现 Callable、线程池
继承 Thread 类
1、继承 Thread
2、重写 run()
3、调用 start() 启动
java
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程1运行");
}
}
// 使用
new MyThread().start();
实现 Runnable 接口(无返回值)
1、实现 Runnable
2、重写 run()
3、传入 Thread 启动
java
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程2运行");
}
}
// 使用
new Thread(new MyRunnable()).start();
实现 Callable 接口(有返回值)
1、实现 Callable
2、重写 call()
3、可以返回结果、抛出异常
java
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "线程3返回结果";
}
}
// 使用
FutureTask<String> task = new FutureTask<>(new MyCallable());
new Thread(task).start();
String result = task.get(); // 获取返回值
使用 线程池
通过 ExecutorService 创建线程,复用线程、控制并发通过 ExecutorService 创建线程,复用线程、控制并发。
java
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.submit(() -> {
System.out.println("线程池执行");
});
ThreadPoolExecutor vs Executors
Executors
Executors.newFixedThreadPool(3);
封装好的,不用写 7 个参数
内部用了无界队列 new LinkedBlockingQueue<>()
任务无限堆积 → 内存爆掉 → OOM 宕机
ThreadPoolExecutor
7 大参数完全自定义
队列容量自己设 → 不会无限堆积任务
拒绝策略自己配 → 高并发下不会崩
线程池七大参数
corePoolSize(核心线程数)
maximumPoolSize(最大线程数)
keepAliveTime(空闲线程存活时间)
unit(时间单位)
workQueue(阻塞队列)
threadFactory(线程工厂)
handler(拒绝策略)
java
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2, // 核心线程
5, // 最大线程
3L, // 空闲时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(10), // 队列容量10
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
拒绝策略
AbortPolicy:直接抛出RejectedExecutionException异常,中断任务提交。(默认)
CallerRunsPolicy:把任务退回给调用者线程执行,减缓提交速度,削峰限流。
DiscardPolicy:默默丢弃当前新来任务,无任何提示。
DiscardOldestPolicy:丢弃队列里等待最久的任务,放入新任务。
业务核心、不能丢任务 → CallerRunsPolicy
允许报错、及时感知过载 → AbortPolicy(默认)
实时数据、旧数据无用 → DiscardOldestPolicy
线程生命周期
新建(NEW)→就绪(RUNNABLE)→运行(RUNNING)→阻塞(BLOCKED)→死亡(TERMINATED)
常用切换方法
start():新建→就绪
sleep():运行→阻塞
wait():运行→阻塞
notify():阻塞→就绪
join():进入阻塞等待其他线程
JVM
Java Virtual Machine,Java 虚拟机是一台虚拟的计算机,运行 Java 字节码文件,屏蔽软硬件差异,实现一次编写,到处运行。
核心作用
1、执行.class字节码,不直接运行源码
2、内存管理、垃圾回收、权限校验、代码编译
3、跨平台:Windows/Linux/Mac 都能跑同一套 Java 程序
内存模型
SpringBoot自动装配实现原理
自动装配,其实本质上就是约定大于配置。当你导入了SpringBoot的某一个starter依赖时,这个依赖会有提前写好的一些关于整合到Spring中的配置信息。
回答方式:
注解的形式
- 启动类上有一个@SpringBootApplication的注解,这个注解是一个组合注解
- 在上述注解中包含了@EnableAutoConfiguration的注解,这又一个组合注解
- 在上述注解中,又包含了一个@Import的注解,这个注解导入了AutoConfigurationImportSelector类
- 在上述导入的类中,就加载了META-INF/spring.factories文件 大致流程如下
- getAutoConfigurationEntry->getCandidateConfigurations->SpringFactoriesLoader.loadFactoryNames->classLoader.getResources("META-INF/spring.factories")
main方法启动形式
- main方法的形式。依然要关注到启动类上的注解
- 在启动类的main方法执行时,优先启动run方法,run方法往里找会找到Spring非常核心的refresh方法
- 在refresh方法中,会找到执行BeanFactoryPostProcessor的位置
- 在上述位置会发现加载了一个ConfigurationClassPostProcessor的实例
- 上述实例会加载被@Configuration修饰的类,启动类本质也是被@Configuration修饰的
- 根据上述的方式,会基于ConfigurationClassParser的去加载@Import注解,如此依赖前面的启动类中的@Import引入的的示例 AutoConfigurationImportSelector 会被加载到,并且执行它的process的方法,将所有的AutoConfiguration加载上
- 最好也会走到这个流程
- getAutoConfigurationEntry->getCandidateConfigurations->SpringFactoriesLoader.loadFactoryNames->classLoader.getResources("META-INF/spring.factories")
Spring创建Bean的过程
- 首先Spring会根据反射,将对象实例化好
- 其次将对象进行初始化,将属性赋值
- 会执行各种Aware接口
- 会执行BeanPostProcessor的before方法
- 会执行基于InitializingBean提供的afterPropertiesSet方法
- 会执行提供很好的init-method方法
- 会执行BeanPostProcessor的after方法
Spring IOC
IOC 控制反转
原本开发者手动new创建对象、维护依赖,反转为由 Spring 容器全权负责对象实例化、依赖装配、生命周期管理,程序只需直接使用对象。
完整执行流程
- 资源加载定位
读取配置源:XML 文件、注解、JavaConfig 类,扫描项目标注组件 - 解析封装 BeanDefinition
扫描到 @Component、@Bean、XML 标签等,解析类信息,统一封装为 BeanDefinition,存入注册表 - 后置处理器处理配置
BeanFactory 后置处理器修改、完善 BeanDefinition 数据 - 实例化 Bean
容器启动或首次获取 Bean 时,根据 BeanDefinition 反射创建对象实例 - 依赖注入
自动解析属性依赖,通过构造器、setter、字段三种方式完成依赖装配 - Bean 初始化
执行 @PostConstruct 初始化方法
调用 InitializingBean 接口初始化方法
执行自定义 init-method - 后置处理加工
Bean 后置处理器对初始化后的 Bean 做代理、属性修改等增强 - 放入单例缓存池
成品 Bean 存入一级缓存,后续直接复用 - 容器销毁
关闭容器时,执行 @PreDestroy、DisposableBean、destroy-method 销毁逻辑
优势
1、彻底解耦,类无需关心对象创建
1、统一管控对象生命周期
1、依赖自动注入,简化开发
1、便于扩展、测试、事务与 AOP 整合
Spring AOP
依靠动态代理生成代理对象,调用时走代理执行增强逻辑
核心专业术语
切面 Aspect:存放通用增强逻辑的类
连接点 JoinPoint:可以被拦截到的方法
切点 Pointcut:具体要拦截哪些方法
通知 Advice:拦截后执行的增强代码
目标对象 Target:被增强的原始业务类
代理对象 Proxy:AOP 生成的增强后的对象
织入 Weaving:把增强逻辑嵌入目标方法的过程
五种通知类型
@Before 前置通知:方法执行前触发
@AfterReturning 返回通知:方法正常执行完毕触发
@AfterThrowing 异常通知:方法抛出异常触发
@After 后置通知:方法结束必触发(正常 / 异常都走)
@Around 环绕通知:包裹整个方法,可控制执行、拦截、放行
AOP 执行顺序
环绕前置 → 前置通知 → 目标方法 → 返回 / 异常通知 → 后置通知 → 环绕后置
Spirng 事务失效场景
1、同类内部方法调用
java
@Service
public class UserService {
// 没有 @Transactional
public void methodA() {
// 内部调用 methodB
methodB();
}
@Transactional
public void methodB() {
// 数据库操作
}
}
Spring 事务是基于 AOP 动态代理实现的!只有代理对象调用才会走事务增强。
同类内部调用 → 是 this 对象调用,不是代理对象 → 事务不生效!
解决方案
自己注入自己
把 @Transactional 加到外层方法
2、方法不是 public
java
@Transactional
private void method(){ } // 事务失效
Spring 事务代理只支持 public 方法,非 public 方法会被 Spring 直接忽略事务。
解决方案
改成 public
3、异常被 try-catch 吃掉了
java
@Transactional
public void test(){
try{
// 数据库操作
}catch(Exception e){
// 异常被捕获,没有抛出去 → 事务不回滚!
}
}
Spring 事务默认只对 运行时异常(RuntimeException)和 Error 回滚。
异常被你 catch 住了,Spring 不知道出错了 → 不会回滚。
解决方案
不要吞异常
catch 后重新抛出 throw new RuntimeException(e);
4、抛出的异常类型不对
java
@Transactional
public void test() throws Exception {
throw new Exception(); // 事务不回滚
}
Spring 默认只回滚 RuntimeException,你抛了 Exception(受检异常) → 不回滚
解决方案
@Transactional(rollbackFor = Exception.class)
5、多线程调用(新线程不受 Spring 管理)
java
@Transactional
public void test(){
new Thread(() -> {
// 数据库操作
}).start();
}
事务存在当前线程 ThreadLocal 里,新开线程拿不到事务 → 独立连接 → 事务失效。
解决方案
把 @Transactional 加到子线程方法上。
6、Bean 没有被 Spring 管理
没被 Spring 管理 → 不会生成代理 → 事务无效。
7、开启了新的事务 / 传播机制错误
java
@Transactional(propagation = Propagation.NOT_SUPPORTED)
传播属性设置错误,导致以非事务方式执行。
8、final /static 方法
java
@Transactional
public final void test(){}
无法被代理重写 → 事务失效。
Redis 数据
结构
常用 5 种基础类型
String、List、Set、Hash、Sorted Set(ZSet)
各自适用场景
String:缓存、计数器、验证码、分布式 ID
List:消息队列、栈、排行榜列表
Hash:存储对象、购物车
Set:去重、交集并集、好友共同关注
ZSet:有序排行榜、延时任务
Redis 持久化
两种持久化方式
RDB 快照、AOF 日志,4.0 后支持混合持久化
RDB持久化
定时把整个内存数据生成二进制快照文件dump.rdb,保存到磁盘。
触发方式
自动触发
save 秒数 改动次数:满足条件自动快照
默认配置:save 900 1、save 300 10、save 60 10000
手动触发
save:阻塞主线程,直到快照完成
bgsave:fork 子线程后台生成快照,不阻塞业务
优点
文件体积小,备份、迁移速度快
重启恢复数据速度最快
占用 CPU 低,适合冷备份
缺点
两次快照间新增数据会丢失,无法做到实时持久化
大数据量 fork 子进程,瞬间消耗内存
恢复流程
重启 Redis,自动加载dump.rdb文件还原数据
AOF 持久化
记录每一条写操作命令,追加到日志文件appendonly.aof,重启重放命令恢复数据。
三种刷盘策略
always:每条命令立刻刷盘,数据最安全,性能最差
everysec:每秒刷一次盘,默认策略,丢最多 1 秒数据,均衡性能安全
no:交由系统自动刷盘,性能最高,丢失数据最多
AOF 重写
日志文件会越来越大,Redis 自动精简合并重复命令,压缩文件体积,不丢失实际数据。
优点
数据安全性极高,最多丢失秒级数据
日志可读,误操作可找回命令
缺点
文件体积远大于 RDB
数据恢复速度比 RDB 慢
频繁刷盘轻微损耗性能
| 对比项 | RDB | AOF |
|---|---|---|
| 存储形式 | 二进制快照 | 文本命令日志 |
| 数据完整性 | 易丢失最后时段数据 | 基本不丢失数据 |
| 恢复速度 | 快 | 慢 |
| 文件大小 | 小 | 慢 |
| 性能消耗 | 低 | 偏高 |
混合持久化
先以 RDB 格式保存全量快照,后续增量写命令用 AOF 追加。
兼顾 RDB 快速恢复 + AOF 高数据安全,生产环境主流配置。
Redis 缓存穿透 缓存击穿 缓存雪崩
缓存穿透
查询数据库不存在的数据,缓存查不到,请求直接打到数据库,恶意空查询可压垮库。
解决方式
空值缓存:查询为空也存入缓存,设置短过期时间
布隆过滤器:提前过滤不存在 key,拦截非法请求
接口参数校验:非法参数直接拦截,不访问缓存与数据库
互斥锁:少量并发兜底防护
缓存击穿
热点高频 Key刚好过期,海量并发瞬间全部直达数据库。
解决方式
永不过期:业务层面手动更新,不设置自动过期
互斥锁:单个线程查库写缓存,其他线程等待
热点数据常驻内存,规避过期临界点
缓存雪崩
大量缓存 key同一时间过期,或 Redis 服务宕机,所有请求全部冲击数据库。
解决方式
过期时间加随机偏移,错开批量过期
Redis 集群高可用,主从 + 哨兵避免单点宕机
服务限流、熔断、降级,挡住过量请求
多级缓存:本地缓存 + Redis 双层防护
数据库索引失效场景
违背最左匹配
字段做运算、函数操作
使用!=、<>、not in
字符串不加引号
or 左右字段无索引
like % 前缀模糊查询
事务四大特性
原子性:事务要么全执行,要么全回滚
一致性:前后数据状态合法统一
隔离性:事务间互不干扰
持久性:提交后数据永久保存
事务隔离级别
读未提交:可查未提交数据,脏读、不可重复读、幻读都存在
读已提交:默认级别,解决脏读,存在不可重复读、幻读
可重复读:InnoDB 默认,解决脏读、不可重复读,存在幻读
串行化:最高级别,全部问题解决,并发性能最低
幂等执行
前端生成唯一请求 ID(requestId)
请求进入后端
后端先查 Redis/DB:该 requestId 是否处理过
已处理 → 直接返回结果,不执行业务
未处理 → 执行业务
执行成功 → 记录 requestId
返回成功