2026/6/9
嚼嚼嚼。
事务![]()
拦截器
MySQL关键字
where和having
数据库索引和常见类型
还是隔离级别
hashmap底层原理,扩容机制
jdk1.7数组+链表
jdk1.8之后用数组+链表+红黑树
hashmap为啥重写equals时必须重写hashcode
一个hashcode用来找桶,equals用来判断同一桶里多个元素是否为同一个key。
反正就是hashcode用来找桶,equals用来桶内判等。
如果只改equals不改hashcode,相等对象进不同桶,存取全部错乱。
多线程之线程创建
啊吧啊吧,闹心了。这回直接写吧。
线程一共三种创建方式:
1.继承thread类(并重写run方法)
这种就是很简单啊,直接调start就启动了。如果在run方法内可以直接用this获取线程对象,不然你就嘚用Thread.currentThread。Thread.currentThread.getName这个直接获取名字啦,到时候输出调试的时候也能用上。不过这种创建方式的缺点呢,就是java是单继承的,继承Thread后不能继承其他父类了,扩展性很差;当任务和线程耦合在一起的时候,多个线程执行同一任务无法复用。
2.实现Runnable接口(并重写run方法)
这个是接口实现的,接口可以多接口,任务和线程是分离的,同一个Runnable对象可以交给多个线程执行,资源共享。适合多线程处理同一任务。他有个缺点,就是没有返回值、不能抛出受检异常。
3.实现Callable+FutureTask(有返回、抛异常)
优点是线程任务有返回值,任务内可以抛出checked受检异常。同样解耦任务与线程,支持多线程复用任务。缺点是写法很繁琐,需要FutureTask包装,get()获取子线程执行结束后的返回值后会阻塞主线程。就是那个FutureTask.get()。
sleep()和wait()的区别
sleep()和wait()所属的类是不同的。sleep()是Thread类静态方法;wait()是Object类实例方法,所有对象都有。
sleep()和wait()的锁行为不一样。sleep休眠时不释放持有的同步锁,其他线程无法进入同步块;wait调用后立刻释放当前对象锁,让其他线程竞争该锁。
sleep()和wait()唤醒条件也不一样。👇
sleep时间到自动苏醒;中途不能被其他线程唤醒。
抛出InterruptedException可打断休眠
wait有三种唤醒方式:
1.等待时长结束自动醒wait(long)
2.其他线程调用notify()随机唤醒一个处于WATING的线程
3.其他线程调用notifyAll()唤醒全部无超时参数wait()会无限阻塞
使用场景限制
wait()/notify()/notifyAll()必须写在synchronized同步代码块/同步方法内,否则抛出异常。
sleep()无同步锁要求,任意地方都能调用。
状态
sleep:线程进入TIMED_WAITING
wait:线程进入WAITNG/TIMED_WAITING
设计目的
sleep:单纯让当前线程暂停一段时间,不涉及线程间通信
wait:多线程协作、生产者消费者模型,线程间等待通知
还是索引创建
join
SQL – 连接(内连接,左连接,右连接和全连接)|极客教程
Mybatis一级缓存二级缓存
我将用最直白最简洁的方式来说说mybatis一级缓存二级缓存。
一级缓存是本地缓存,默认自动开启的。只在同一个SqlSession里失效。简单说,在一次数据库会话中,重复查相同条件的数据,不会重复访问数据库。(一次会话里,在第一次查的时候结果就存进一级缓存了,再查的时候直接从缓存里拿数据,不访问库。但是会话间是不共享的,a会话查的东西,b会话查不到)
缓存清空/失效
1.同一张表执行insert/update/delete
2.手动调用sqlSession.clearCache()清空缓存
3.关闭/重新创建会话
二级缓存是Mapper缓存,默认关闭,需要手动打开。同一个Mapper映射文件,跨SqlSession共享缓存。就是同一个Mapper,所有会话共享。
缓存清空/失效
1.都这个mapper下任意数据做增删改,缓存全部清空
二级缓存开启
1.全局配置开启二级缓存总开关
2.在对应Mapper.xml里加<cache/>
3.实体类实现Serializable序列化接口
执行顺序
先看二级缓存有没有数据->没有再看一级缓存->还没有就查数据库
java反射
正常咱们进行操作,嘚知道用的哪个类里的代码,知道哪个类,就创建这个类的对象,直接调用方法。
java反射你不提前知道这个类是啥,但是在程序运行的时候,它能反向找着那个类,创建那个类的对象,调用方法,读写属性。
是怎么实现的?
Java的每个类都自带一个class对象,程序一运行的时候,JVM自动给它生成一个唯一的户口本。这里面包含着这个以下信息,这个类叫啥名儿;里头有啥变量;有哪些方法;构造器是谁。这个户口本儿就是反射的根本
第一步是要先拿到这个类的户口本:使用类名.class直接拿;然后第2种就是new类名().get class通过这个类的实例然后来拿户口本;第3种就是class.forName后头接上接收一个完整类名,输入它的类的名字,加载类拿到户口本。平时大多数用的都是第3种方式
然后第二步呢就是拿着你这个户口本调取里边所有的信息,爱咋操作咋操作,这个里头有所有的字段,所有的方法,还有构造方法。你如果是想调取构造器的话就可以凭空的造出来这个类对象;如果想要调用成员方法就可以执行这个方法;如果调取成员变量就读取修改变量的值,就算你哪怕是私有private的内容,加一句setAccessible(true)就可以跳过权限锁,照样能访问
嗯也就是说你普通的代码,你在编译阶段的时候就把类方法都绑定死了。而这个反射呢,在运行期间依靠class户口本动态地调取读取类的全部的元数据,动态给执行操作
索引失效
嗯首先什么是索引失效?索引失效就是你建了索引,但是你Mysql没去用索引,是用的全表扫描。这就是索引失效,会导致你查询速度很慢
索引失效的条件
第一个就是索引字段他参与了计算,调用了函数。这种情况肯定会失效的。因为索引你存的是原始字段值,如果加工的话索引匹配不到了,那个值它变了
第2种就是隐式类型转换嗯。字符串索引字段,查询值不能加单引号,数据库强制转换字段整体数据,索引会作废。反正就是你不加单引号是不行的,你如果不加单引号那就失效了,你加单引号才有效
第3种就是like通配符%出现在了最左侧。出现在最左侧,左模糊的话索引就会失效。如果是右模糊的话,索引还是可以用的。name LIKE '%老王',这个就是左模糊它就失效了。name LIKE '老王%',这个是有模糊还是可以用的
第4就是联合索引他违反了最左前缀原则。复合索引(a,b,c)查询条件没有最左列的a,整个索引他就会失效,你少了一个最左边的a它就失效了!
第5种就是OR条件一侧没有索引。一个字段有索引,另外一个字段是没索引,你还用all拼接的,这个整体索引它就失效了。他就全表扫描了
第6种是使用了否定运算符(比如!=,<>,NOT IN,IS NOT NULL)。数据库判定符合条件数据越多,优化器他就直接放弃索引了
第7种是MySQL优化器主动放弃索引。查询结果占了整张表数据的20%到30%以上,全表扫描的速度比索引速度更快,所以它就直接放弃用索引了
第8种就是使用order by字段没有遵循联合索引顺序。联合索引(a,b),查询a等于1,排序b以外的字段,索引是无法支持排序的
第9种是字段设置成null,大量的数据为空。索引对null支持差,筛选is not null,大概率失效
如何自己检查索引是否失效
EXPLAIN SELECT * FROM 表 WHERE 条件;
type列显示ALL 索引失效
ref/range 索引正常