1.Java中的多态是什么?如何实现?
大类型引用的多种赋值形态, 具体表述为大类型引用指向不同的小类型对象
通过方法的重载和重写
重载:在同一类中, 方法名相同, 参数列表不同, 与其他部分无关
重写:发生在继承|实现关系之上;返回值类型、方法名、形参列表必须与父类一致;访问修饰符必须与父类相同|更宽;不允许抛出比父类更大|更多的异常
2.八大基本类型默认值和引用类型默认值
整型 : 0,浮点型 :0.0,布尔型:false,字符型:空,引用类型:null
两者区别:基本类型直接存储值在栈内存中,传递时复制值本身;引用类型存储的是对象在堆内存中的地址,传递时复制地址而非对象本身。
3.如何比较两个对象是否相等
- 通过==来比较对象是否相等,但不推荐因为==比较的是两个对象的地址
- 通过重写equals()方法和hashCode()方法来比较对象是否相等
equals():该方法默认比较对象地址, 但是实际开发中, 更多的场景需要比较的为对象内容, 所以需要重写
hashCode():该方法默认根据对象地址获取哈希码值, 但是实际开发中, 更多关注的是对象内容, 内容一致则哈希码值理应一致, 所以需要重写
4.finalize,final,finally的区别
final是一个修饰符, 都能修饰类不能被继承;修饰方法不能被重写;修饰变量变成常量值不可改(
(1)局部常量
对赋值时机无要求, 未二次赋值即可
(2) 常量属性
1. 没有默认值
2. 必须在声明创建时赋值, 保证空间分配. 赋值时机:
- 声明时直接赋值
- 在所有构造中提供赋值操作
)
finally是一个代码块, 通常与try-catch结合使用, 作用为执行关闭|释放资源的代码
finalize是Object中的一个方法, 作用为在垃圾回收机制回收对象之前执行清理操作
5.string, stringBuffer,stringBuilder的区别
string与可变长字符串:string值不可改,可以使用“”,可以使用+=,可以使用串池
stringBuffer与stringBuilder:
stringBuffer:JDK1.0 线程安全, 效率低
stringBuilder:JDK5.0 线程不安全, 效率高
6.短路机制&&,||与&,|
短路机制:当执行到可以奠定最终结果的表达式时, 后续表达式将不再执行
短路时机:
&&: 执行到false时
||: 执行到true时
非短路运算符: &和|特点为无论如何所有表达式都会执行
7.JDK,JVM,JRE的区别
JDK是java开发工具包
JRE是java运行环境
JVM是java虚拟机
其中JDK包含JRE和一系列开发工具,JRE包含JVM和核心类库,JVM是java运行的基础
8.JDK常用的包
java.unti、java.sql、java.lang、java.io
9.静态变量和实例变量的区别
存储位置:
- 实例内容存储在对象空间中
- 静态内容存储在方法区中
存储个数:
- 实例内容数量与对象数量一致
- 静态内容以类为单位只有一份
存储时机:
- 实例内容在创建对象时出现在内存中 (较晚)
- 静态内容在类加载时出现在内存中 (较早)
访问方式:
- 实例内容必须通过`对象引用`访问
- 静态内容可以通过`类名`直接访问
执行关注:
- 实例内容执行关注实际对象类型
- 静态内容执行关注引用类型
10.Integer和int的区别
int :基本数据类型、不能作为泛型使用、不能存储空值、默认值为0
Integer:引用数据类型、可以作为泛型使用、可以存储空值、默认值为null
11.构造器是否可以被重写
不能
原因:构造器的名称必须和类名一致,这就决定了子类不能定义出与父类构造器名称、参数列表、返回类型都相同且符合重写规则的构造器
12.面向对象什么特性
继承,多态,封装,抽象
13.线程和进程的区别
维度 | 进程 | 线程 |
---|---|---|
定义 | 资源分配和调度的基本单位 | CPU调度和分派的基本单位 |
资源占用 | 独立的内存空间 | 共享进程的内存空间 |
创建/销毁 | 开销大,需分配新的地址空间 | 开销小,仅需分配栈空间 |
调度 | 操作系统调度,开销大 | 操作系统调度,开销小 |
14.堆和栈的区别
栈:主要存储局部变量与方法调用信息、由系统自动完成内存的分配与回收、内存空间相对较小,不过访问速度快
堆:用于存放对象实例与数组、内存的分配和回收由垃圾回收器负责、内存空间较大,但访问速度慢
15.JVM是什么?谈谈你对JVM的认识
JVM即Java虚拟机(Java Virtual Machine),它是一个抽象的计算机,是Java程序的运行基础。JVM可以在不同的操作系统上提供统一的运行环境,使得Java程序能够实现“一次编写,到处运行”的特性。
JVM内存模型:
程序计数器:程序计数器是一块较小的内存空间,是当前线程正在执行的那条字节码指令的地址。
当在多线程情况下,记录的是当前线程执行的位置,进行线程切换时就可以清楚知道上次线程执行到哪里,线程私有
java虚拟机栈:
Java 虚拟机栈会为每一个即将运行的 Java 方法创建一块叫做“栈帧”的区域,用于存放该方法运行过程中的一些信息
本地方法栈:本地方法栈是为 JVM 运行 Native 方法准备的空间,它与 Java 虚拟机栈实现的功能类似,只不过本地方法栈是描述本地方法运行过程的内存模型。
堆:线程共享,是垃圾回收的主要场所,
堆可分为新生代、老年代。
new的对象会放到新生代,当新生代满了之后会触发垃圾回收机制,把不会再被引用的对象回收,将新生代中剩余对象放入幸存区中,默认经过15次回收后,将幸存区中对象放入老年区中
方法区:方法区也是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
16.多线程的创建方式
- 创建一个类继承自
Thread
类,重写run()
方法,该方法中定义线程要执行的任务。然后创建该类的实例并调用start()
方法启动线程。- 创建一个类实现
Runnable
接口,实现run()
方法,将该类的实例作为参数传递给Thread
类的构造函数,然后调用start()
方法启动线程。- 创建一个类实现
Callable
接口,实现call()
方法,该方法可以有返回值。将该类的实例作为参数传递给FutureTask
类的构造函数,再将FutureTask
实例作为参数传递给Th
read
类的构造函数,最后调用start()
方法启动线程。可以通过FutureTask
的get()
方法获取线程执行的返回结果。- 使用
ExecutorService
接口及其实现类(如ThreadPoolExecutor
、Executors
提供的工厂方法)创建线程池,将任务(实现Runnable
或Callable
接口的类的实例)提交给线程池执行。线程池基本原理:
1、任务提交到线程池以后,如果工作线程数小于 coreSize,直接交给核心线程执行任务2、核心线程数都满了,任务放到队列中3、队列满了,创建核心线程数以外的线程执行任务4、达到最大线程数,且队列仍然是满的状态,使用拒绝策略拒绝策略
- ThreadPoolExecutor.AbortPolicy(系统默认): 丢弃任务并抛出RejectedExecutionException 异常,让你感知 到任务被拒绝了,我们可以根据业务逻辑选择重试或者放弃提交等策略
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常,相对而言存在一定 的风险,因为我们提交的时候根本不知道这个 任务会被丢弃,可能造成数据丢失。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程),通常是存活时间最长的任务,它也存在一定的数据丢失风险
- ThreadPoolExecutor.CallerRunsPolicy:既不抛弃任务也不抛出异常,而是将某些任务回退到调用者,让调用者去执行它。
17.Runnable和callable的区别
java.lang.Runnable: run()
特点: 无返回值, 无参数, 不能上抛异常
java.util.concurrent.Callable<返回值泛型>: call()
特点: 有返回值, 无参数, 可以上抛异常(默认上抛Exception)
18.什么是泛型,泛型的作用是什么,泛型怎么定义
泛型是Java 5引入的类型参数化机制,本质是编译期的类型安全检查工具
用于集合时, 可以约束集合存储的数据类型
19.java中异常体系是如何分类的
Throwable: 不正常情况的总父类
Error : 错误
特点: 无法解决也无法提前避免
触发原因: 通常由硬件问题|内存问题导致
Exception:异常
特点: 可以解决也可以提前避免
触发原因: 通常由代码导致
分类:
RuntimeException: 运行时异常, 也称为未检查异常|未检异常等`
特点: 编译成功, 运行报错, 可以选择性处理
常见运行时异常:
java.lang.ArithmeticException: 数学运算异常
java.lang.ArrayIndexOutOfBoundsException: 数组下标越界异常
java.lang.StringIndexOutOfBoundsException: 字符串下标越界异常
java.lang.IndexOutOfBoundsException: 下标越界异常
java.lang.NullPointerException: 空指针异常
java.lang.ClassCastException: 类型转换异常
java.lang.NumberFormatException: 数据类型转换异常
20.Java集合框架分几类,核心区别是什么
list:有序,有下标,元素可以重复,通过索引访问
ArrayList
JDK1.2 底层数组实现 查询快, 增删慢 线程不安全, 效率高
LinkedList
JDK1.2 底层链表实现 查询慢, 增删快 线程不安全, 效率高
Vector
JDK1.0 底层数组实现 都慢 线程安全, 效率低set:无序,无下标,元素不能重复,通过for循环
HashSet
JDK1.2 底层哈希表(数组+链表)实现 线程不安全, 效率高
LinkedHashSet
JDK1.2 是HashSet的子类, 底层哈希表实现 线程不安全, 效率高
TreeSet
JDK1.2 是`SortedSet`的实现类, 底层红黑树实现 线程不安全, 效率高map:通过键值对存储,键:无序,无下标,不能重复,值:无序,无下标,可以重复,
通过键访问值
HashMap
JDK1.2 底层哈希表实现 线程不安全, 效率高
LinkedHashMap
JDK1.2 是HashMap的子类, 底层哈希表实现 线程不安全, 效率高
TreeMap
JDK1.2 是SortedMap的实现类, 底层红黑树实现 线程不安全, 效率高
Hashtable
JDK1.0 底层哈希表实现 线程安全, 效率低
Properties
JDK1.0 是Hashtable的子类, 底层哈希表实现 线程安全, 效率低基本特性键值对存储:HashMap以键值对形式存储数据,每个键都是唯一的,一个键对应一个值。无序性:存储的键值对是无序的,不会按照插入顺序或其他特定顺序排列。允许 null 值和 null 键:可以有一个 null 键也能有多个 null 值。非线程安全:(这个特点可以在介绍完hashmap后引出线程安全的实现)在多线程环境下,若多个线程同时访问HashMap,并且至 少有一个线程对其结构进行修改,那么它必须在外部进行同步。底层:加载因子 0.75 16*0.75=12 当数组中元素的数量大于12时,扩容为原来的1倍 16-->322. 结构: 数组+链表 +红黑树 在第一次使用时初始化(第一次调用put方法存值时)3. threshold 阈值 = capacity * load factor4.树化的条件1. 链表长度达到阈值:链表的长度达到 8 时,会触发树化的检查。2. 桶数组的容量达到一定值:HashMap 的桶数组(即 table 数组)长度达到 64。5. 反树化的条件当红黑树中的节点数量减少到 6 个时,红黑树会转换为链表6. HashMap的重要参数HashMap 的实例有两个参数影响其性能:初始容量和加载因子。初始容量容量默认为 16加载因子: 0.75 扩容的倍数是:2
21.lambda表达式及其特性
简化了匿名内部类的使用,并提高代码的可读性和性能。Lambda表达式的基本语 法:接口/父类 引用名=(参数)->表达式Lambda表达式的特性:简洁:将冗长的代码变的简单易读。灵活:传递参数和返回值。高效:提高代码性能,减少创建过多的中间对象。
22.线程的五种基本状态
新建状态:新建一个线程,但并未执行start()
就绪状态:线程执行start()方法进入就绪状态,等待CPU分配时间片
运行状态:线程拿到CPU所分配的时间片,开始执行
阻塞状态:通过sleep或join方法wait方法使线程进入等待状态,
终止状态:线程执行完全部代码后,结束运行
23.事务的四大特征
事务(Transaction)是数据库管理系统执行过程中的一 个逻辑单位,它由一个或多个SQL语句组成,这些语句作为一个整体一起向系统提交,要么全部执行,要么全 部不执行,即事务具有原子性(Atomicity)。1.原子性(Atomicity):事务是数据库中的最小工作 单元,事务中的所有操作要么全部完成,要么全部不 做,事务在执行过程中发生错误会被回滚 到事务开始前的状态,就像这个事务从 未执行过一样。2. 一致性(Consistency):事务必须使数据库从一个 一致性状态变换到另一个一致性状态。一致性与原子 性是密切相关的。3. 隔离性(Isolation):数据库系统提供一定的隔离级别,保证事务在不受外部并发操作影响的“独立”环境 执行。这意味着事务处理过程中的中间状态对外部是 不可见的,或者说,事务处理过程中的数据变化只有 在这个事务提交时才对其他事务可见。4. 持久性(Durability):一旦事务被提交,它对数据 库的修改就是永久性的,接下来的其他操作和数据库 故障不应该对其有任何影响隔离级别1. 读未提交:最低级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读或幻读。2.读已提交:允许读取已经提交的数据,可以阻止脏读,但不可重复读和幻读仍有可能发生。3. 可重复读:MySQL InnoDB的默认事务隔离级别。确保在同一个事务中多次读取同样记录的结果是一致的,但幻读仍有可能发生。4. 可串行化:最高的隔离级别,通过强制事务串行执行,避免了脏读、不可重复读和幻读,但这也将大大降低数据库的并发性能。
24.sleep和wait的区别
资源释放: sleep()只会释放时间片, wait()会同时释放时间片和锁标记
2. 来源和调用者: sleep()是来自Thread类的静态方法, 通过Thread类调用. wait()是来自于Object类的实例方法, 通过临界资源对象调用
3. 进入状态: sleep()使线程进入有限期等待状态, wait()是线程进入的是无限期等待状态
25.什么是死锁?如何产生?如何避免?
定义:当存在多个临界资源对象时, 如果多个线程之间拥有的锁标记不同, 此时任意线程都无法获取完整锁标记进入运行状态, 就会形成死锁现象. 通常是由多个临界资源时休眠不当导致
产生原因:
1.互斥执行: 多个线程间的运行及对某个锁标记的拥有一定互斥
2. 不可抢占: 其他线程无法抢占拥有时间片和锁标记的线程的资源
3. 请求保持: 线程会一直保持对未拥有的锁标记的获取
4. 循环等待: 多个线程间互相等待对方所持有的锁标记资源如何避免:
1.尽量避免使用多个锁,尽量使用一个锁或者使用更加高级的锁2. 确保同步代码块的执行时间尽可能短,这样可以减少线程等待时间,从而避免死锁的产生。3. 破坏不剥夺条件,在Java中,可以使用可重入锁ReentrantLock
的tryLock()
方法。如果线程无法获取到所有需要的锁,就释放已经获取的锁4. 避免嵌套锁,如果需要使用多个锁,请确保它们的获取顺序是一致的,这样可以避免死锁。
26.面向对象的五大基本原则
单一职责原则(Single Responsibility Principle,SRP)
含义:一个类或者模块应该有且只有一个改变的原因。也就是说,一个类只负责一项职责。
好处:降低类的复杂度,提高代码的可读性和可维护性,当某一职责发生变化时,不会影响到其他职责。
开闭原则(Open Closed Principle,OCP)
含义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。即可以通过扩展来实现新功能,而不是修改已有的代码。
好处:增强软件的可扩展性和稳定性,减少引入新问题的风险。
里氏替换原则(Liskov Substitution Principle,LSP)
含义:子类可以替换其父类并且出现在父类能够出现的任何地方,而不会影响系统的正确性。也就是说,子类必须能够完全实现父类的功能。
好处:保证系统的继承体系稳定,提高代码的复用性和可维护性。
接口隔离原则(Interface Segregation Principle,ISP)
含义:客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。
好处:降低类之间的耦合度,提高系统的灵活性和可维护性。
依赖倒置原则(Dependency Inversion Principle,DIP)
含义:高层模块不应该依赖低层模块,二者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。简单来说,就是要面向接口编程,而不是面向实现编程。
好处:降低模块间的耦合度,提高系统的可扩展性和可维护性。
27.线程锁的类型
悲观锁:悲观锁假设最坏的情况,即认为冲突一定会发生,因此在操作数据前就进行加锁处理,确保数据加锁期间不会被其他线程修改。
乐观锁:乐观锁假设最好的情况,即认为冲突不会经常发生,因此在数据提交更新时,才会真正对数据加锁。乐观锁通常是通过版本号(version)或时间戳(timestamp)来实现的,在更新数据时,会检查版本号或时间戳是否发生了变化,从而判断是否发生了并发修改。
公平锁:公平锁是一种按照请求顺序授予锁的机制。即先请求锁的线程会先获得锁,后请求锁的线程会后获得锁。这种锁通过维护一个队列来管理等待的线程,确保每个线程都能公平地获取到锁。
非公平锁:非公平锁是一种不按照请求顺序授予锁的机制。即任何线程都有可能在任何时候获得锁,而不考虑请求顺序。这种锁通常会优先考虑当前已经持有锁的线程,以提高系统的吞吐量。
可重入锁:可重入锁(Reentrant Lock),也称为递归锁,是一种支持同一个线程多次获取同一个锁的机制。即同一个线程可以重复获取一个锁n次,同样的,在释放时也需要释放n次。这种锁在Java中非常重要,因为它允许一个线程在持有锁的情况下再次获取该锁,而不会导致死锁。
读写锁:读写锁(Read-Write Lock)是一种并发控制机制,用于在读多写少的场景下提供更好的性能。它允许多个线程同时读取共享数据,但在写操作时需要互斥地独占访问,从而在保证数据一致性的同时提高系统的并发性。
互斥锁:互斥锁是独占锁的一种常规实现,是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性
自旋锁:自旋锁(Spinlock)是一种用于多线程编程中的同步机制,它采用循环等待的方式尝试获取锁,直到锁被释放为止。
28.synchronized的原理
它会使 用对象的内置锁(也称为监视器锁)来实现同步,每个对象都有一把内置锁,当一个线程访问一个同步代码块 时,它会尝试获取这个锁,如果锁被其他线程持有,则 该线程将被阻塞,直到锁被释放。Synchronized 有以下几个特点:互斥性:Synchronized 保证同一时刻只有一个线程可 以获取锁,并且只有该线程可以执行同步代码块中的 代码。可重入性:同一个线程可以多次获取同步锁而不会被 阻塞,这样可以避免死锁的发生。独占性: 如果一个线程获得了对象的锁,则其他线程 必须等待该线程释放锁之后才能获取锁。缺点:非公平锁 ,当锁被释放后,任何一个线程都有 机会竞争得到锁,这样做的目的是提高效率,但缺点 是可能产生线程饥饿现象。
29.java获取反射的三种方法
1.通过new对象实现反射机制2.通过路径实现反射机制3.通过类名实现反射机制
30. concurrentHashMap的认识
jdk1.7中ConcurrentHashMap 是一个 Segment数组,Segment 通过继承 ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 Segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。
JDK1.8 中的 ConcurrentHashMap 选择了与 HashMap 相同的数组+链表+红黑树结构;在锁的实现上,抛弃了原有的 Segment 分段锁,采用 CAS + synchronized 实现更加细粒度的锁。
31.java四大引用
强引用(Strong Reference)定义:强引用是最常见的引用类型,我们平时使用的普通对象引用就是强引用。特点:只要对象具有强引用,垃圾回收器就永远不会回收它。即使内存不足,也会导致程序抛出OutOfMemoryError异常。2. 软引用(Soft Reference)定义:软引用是一种比强引用稍微弱一些的引用类型。特点:只有在系统将要发生内存溢出之前,软引用指向的对象才会被垃圾回收器回收。如果内存足够,垃圾回收器就不会回收它。3. 弱引用(Weak Reference)定义:弱引用是一种比软引用更弱的引用类型。特点:弱引用关联的对象只能生存到下一次垃圾收集发生为止,无论当前内存是否足够,只要垃圾收集器开始工作,都会回收掉只被弱引用关联的对象。用途:主要用于实现规范的映射关系,可以自动清理不再使用的对象。实现:需要使用java.lang.ref.WeakReference类来实现。4. 虚引用(Phantom Reference)定义:虚引用是四种引用类型中最弱的一种。特点:虚引用与垃圾回收过程没有直接关系,主要用于跟踪对象被垃圾回收器回收的活动。虚引用必须和引用队列(ReferenceQueue)一起使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的引用队列中。用途:主要用于跟踪对象的回收情况。实现:需要使用java.lang.ref.PhantomReference类来实现。访问对象:虚引用无法直接访问对象,其get()方法总是返回null。
32.简述一下你了解的设计模式
Builder(建造者模式)Factory Method(工厂方法模式)Singleton(单例模式)实现条件 :1. 构造方法私有化 (避免在外部创建对象)2. 提供一个静态方法 返回本类的实例实例创建的方式 :1. 调用的时候创建 (懒汉式)2. 类加载的时候创建 (静态代码块)Adapter(适配器模式)Decorator(装饰模式)Proxy(代理模式)
33.volatile关键字的作用
保证变量的可见性volatile关键字的作用就是保证共享变量的可见性。保证赋值操作的原子性原子性就是不能被线程调度打断的操作,是线程安全的操作,对于原子性操作,即使在多线程环境下,也不用担心线程安全问题或者数据不一致的问题。禁止指令重排正常情况下,虚拟机会对指令进行重排,当然是在不影响程序结果的正确性的前提下。
34.什么是JVM调优
JVM调优,即Java虚拟机(Java Virtual Machine)调优,是指对Java虚拟机的运行参数进行调整,以优化Java应用程序的性能。
35.多线程如何获取异步执行的结果
使用Future接口Future接口代表异步计算的结果。你可以使用ExecutorService提交任务并获取Future对象,之后通过该对象获取任务的执行结果使用CompletableFuture类CompletableFuture是 Java 8 引入的一个强大的异步编程工具,它可以更方便地处理异步任务和结果。Future接口适合简单的异步任务,而CompletableFuture类则更适合复杂的异步操作和组合任务。
36.synchronized和lock的区别
类别
|
synchronized
|
lock
|
存在层次
|
Java关键字,jvm层
面
|
是一个类
|
锁的释放
|
JVM 将确保锁会获得自动释放
|
lock 必须在 finally 块中释放
|
锁状态
|
无法判断
|
可以判断
|
锁类型
|
可重入,不可中断、非公平
|
可重入、可判断、可 公平(两者皆可默认非公平)
|
性能
|
少量同步
|
大量同步
|
36.drop、delete、truncate的区别
比较
|
Delete
|
Truncate
|
Drop
|
类型
|
属于DML
|
属于DDL
|
属于DDL
|
回滚
|
可回滚
|
不可回滚,
|
不可回滚
|
删除内容
|
表结构还在,删除表的全部或者一部分数据行
|
表结构还在, 删除表中的所有数据
|
从数据库中删除表,所有的数据行,索引
和权限也会被删除
|
删除速度
|
删除速度慢,需要逐行删除
|
删除速度快
|
删除速度最快
|
37.mysql中in和exists的区别
in关键字确定给定的值是否与子查询或列表中的值相匹配。in在查询的时候,首先查询子查询的表,然后将内表和外表做一个笛卡尔积,然后按照条件进行筛选。所以相对内表比较小的时候,in的速度较快。exists关键字指定一个子查询,检测行的存在。遍历循环外表,然后看外表中的记录有没有和内表的数据一样的。匹配上就将结果放入结果集中。in 与 exists 的区别mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。1. 如果查询的两个表大小相当,那么用in和exists差别不大。2. 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。3. not in 和not exists:如果查询语句使用了not in(索引失效),那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。
38.sql语句的执行顺序
FROM子句:首先,SQL会执行FROM子句,以确定查询中涉及的所有表和视图。如果存在多表连接(如JOIN操作),则在这一步也会处理表之间的连接关系。FROM子句还负责数据的组装,即将来自不同数据源的数据合并成一个结果集。2. JOIN:如果有多个表参与查询,则指定它们之间的连接类型和条件。3. ON:在执行JOIN操作时,应用ON子句中的条件来筛选连接的记录。4. WHERE子句:接下来,SQL会执行WHERE子句,对FROM子句产生的结果集进行筛选。WHERE子句中的条件用于过滤掉不满足条件的行,只保留满足条件的行进入下一步处理。5. GROUP BY子句:如果查询中包含了GROUP BY子句,则SQL会在WHERE子句之后执行GROUP BY子句。GROUP BY子句将结果集中的行按照一个或多个列的值进行分组,每个分组包含具有相同列值的行。6. HAVING子句:HAVING子句通常与GROUP BY子句一起使用,用于对分组后的结果进行过滤。与WHERE子句不同的是,HAVING子句可以对聚合函数的结果进行过滤。如果查询中没有GROUP BY子句,HAVING子句也可以单独使用,但此时它会将整个结果集视为一个大的分组。7. SELECT子句:在WHERE、GROUP BY和HAVING子句处理完毕后,SQL会执SELECT子句。SELECT子句指定了要从结果集中检索的列或表达式。如果查询中包含了聚合函数(如SUM、AVG、COUNT等),则这些函数会在这一步进行计算。8. DISTINCT关键字(如果使用了):如果SELECT子句中包含了DISTINCT关键字,则SQL会在返回最终结果之前去除重复的行。9. ORDER BY子句:最后,如果查询中包含了ORDER BY子句,则SQL会根据ORDER BY子句中的列或表达式对结果集进行排序。排序可以是升序(ASC)或降序(DESC),默认为升序。10. LIMIT/OFFSET子句(或等效的):在某些数据库系统中,可能还允许使用LIMIT/OFFSET子句(或其等效物,如FETCH FIRST、OFFSET ROWS FETCH NEXT等)来限制返回的记录数或跳过某些记录。这些子句通常在查询的最后阶段执行。
39.表连接方式
表连接是数据库中结合两个表或多个表数据的操作
内连接是一种数据库操作,用于从多个表中返回满足连接条件的记录。它只返回两个表中匹配的行,丢弃不满足条件的行。主要特点1. 匹配条件:内连接主要依据 ON 子句中设定的条件来匹配两个表的记录。2. 结果集:最终返回的结果集仅包含同时满足连接条件的记录。3. 关键字:在使用内连接时,INNER JOIN 关键字是可以省略的,直接用 JOIN 效果相同外连接(OUTER JOIN)外连接分为左外连接(LEFT OUTER JOIN 或 LEFT JOIN)、右外连接(RIGHT OUTER JOIN 或 RIGHT JOIN)和全外连接(FULL OUTER JOIN)。这些连接类 型返回至少在一个表中满足连接条件的行,并且还包括那些不满足连接条件的行,不过这些行的对应部分将包含NULL。左外连接(LEFT JOIN):返回左表(LEFT JOIN左侧的表)的所有行,即使右表中没有匹配。如果右表中没有匹配,则结果中右表的部分将包含NULL。右外连接(RIGHT JOIN):与左外连接相反,它返回右表(RIGHT JOIN右侧的表)的所有行,即使左表中没有匹配。全外连接(FULL OUTER JOIN):返回左表和右表中的所有行。当某行在另一个表中没有匹配时,则另一个表的部分将包含NULL。需要注意的是,并非所有数据库系统都直接支持全外连接,可能需要通过其他方式(如UNION)来实现。交叉连接(CROSS JOIN)交叉连接返回第一个表中的每一行与第二个表中的每一行组合的结果。这通常会产生一个非常大的结果集,特别是当两个表都有很多行时。交叉连接的结果是一个笛卡尔积。笛卡尔积(Cartesian Product)笛卡尔积是两个集合中所有元素的组合。在数据库查询的上下文中,当两个表进行交叉连接时,得到的结果集就是这两个表的笛卡尔积。每个来自第一个表的行都会与第二个表的每一行组合。
40.什么是索引
索引是数据库中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的类型:单列索引:针对表中单个列的索引,适用于该列的等值查询、范围查询和排序操作。多列索引(复合索引):针对表中多个列的联合索引,适用于这些列的复合查询。唯一索引:确保索引列的值在整个表中是唯一的,可以加速唯一值的查询。用户名唯一 、订单单号 这类的数据如果经常查,建议创建唯一索 引主键索引:一种特殊的唯一索引,用于唯一标识表中的每一行,每个表只能有一个主键索引。全文索引:用于对文本类型的列进行全文搜索,能高效处理大文本字段的查找。空间索引:用于地理空间数据类型的列,例如Point、LineString和Polygon,可以加速对地理空间数据的搜索和分析索引的优点:1.提高检索速度2. 加速排序和分组操作3. 提高连接性能4. 保证数据完整性5. 支持快速查找索引的缺点1. 占用存储空间2. 维护成本高3. 增加写操作的时间4. 不适用于所有查询5. 索引失效
41.常用的聚合函数
AVG(expression):返回某列的平均值。COUNT(expression):返回某列/某组/整表的行数。MAX(expression):返回某列的最大值。MIN(expression):返回某列的最小值。SUM(expression):返回指定字段的总和
42.Threadlocal
ThreadLocal为每个使用该变量的线程都提供一个独立的变量副本,每个线程都可以访问自己内部的副本变量,而不会影响到其他线程的副本。用途1. 线程上下文数据存储:在多线程应用程序中安全地存储和访问与线程相关的数据。2. 线程上下文传递:在异步编程场景中,将数据从一个线程传递到另一个线程。3. 线程局部状态维护:每个线程维护自己的局部状态信息,避免线程间的干扰。4. 性能统计和追踪:存储每个线程的计时器或统计器,用于性能分析和瓶颈定位。5. 临时资源分配:在需要临时分配资源的场景中,避免资源的竞争和冲突。6. 日志记录和调试:存储当前线程的标识或调试信息,帮助排查多线程环境下的问题。7. 测试场景模拟:在测试场景中模拟特定的线程环境。实现原理:ThreadLocalMap:ThreadLocal使用内部类ThreadLocalMap来管理每个线程的数据。每个ThreadLocal对象在ThreadLocalMap中都有一个唯一的键值对,键为ThreadLocal对象自身,值为线程特定的数据。存储和访问当调用ThreadLocal的set方法时,会先获取当前线程的ThreadLocalMap,然后将ThreadLocal对象作为键,要保存的值作为值,存储到ThreadLocalMap中。当线程需要获取ThreadLocal对象的值时,调用 ThreadLocal的get方法,通过当前线程的ThreadLocalMap获取对应的值。内存泄漏问题:ThreadLocal在保存时会将自己作为弱引用(WeakReference)的键存储在ThreadLocalMap中。如果ThreadLocal对象没有外部 强引用,那么在垃圾回收时可能会被回收,但其对应的value仍然存在于ThreadLocalMap中,导致内存泄漏。因此,在使用完ThreadLocal后,应该调用remove方法清除数据
43.数据库三大范式是什么
第一范式(1 NF):字段不可再拆分。例如对数据“销售部张三”来说就不遵循第一范式,但并不代表遵循第一范式就一定是好的,例如大多数地址数据都可以拆分,但拆分后表就显得复杂第二范式(2 NF):表中任意一个主键或任意一组联合主键,可以确定除该主键外的所有的非主键值。也就是根据主键可以得到其他非主键的唯一值第三范式(3 NF):在任一主键都可以确定所有非主键字段值的情况下,不能存在某非主键字段 A 可以获取 某非主键字段 B。
44.Mysql B+tree索引和hash索引的区别
特性
|
Hash 索引
|
B+Tree 索引
|
---|---|---|
数据结构
| 哈希表实现通过哈希函数将键值映射到哈希表中的位置。 | 使用 B + 树结构所有数据都存储在叶子节点,非叶子节点仅用于索引导航。 |
查询性能
| 低 | 高 |
排序支持
|
不支持排序,因为哈希表中的数据是无
序的。
|
支持排序,因为 B + 树的叶子节点是有序的。
|
索引键值
|
仅支持等值比较
|
支持各种比较操作
|
存储空间
|
比 B+Tree 索引更紧凑
|
空间占用较大。
|
哈希冲突处理
|
通过链表或开放寻址法处理冲突
|
不存在哈希冲突问题。
|
适用场景
|
内存表、哈希分区
|
大多数场景
|
45.io流分几种
按流向分类1.输入流数据源(如文件、网络等)读取数据。2. 输出流(OutputStream/Writer)用于向目标(如文件、网络等)写入数据。二、按数据类型分类1. 字节流(Byte Stream)以字节为单位进行读写操作的流。字节流通常用于处理非文本数据,如图片、音频、视频等媒体文件。2. 字符流(Character Stream)以字符为单位进行读写操作的流。字符流通常用于处理文本数据,如文本文件、程序代码等。三、按功能分类1. 缓冲流(Buffered Stream)提供缓冲功能,能够减少IO操作的次数,提高IO操作的性能。2. 转换流(Conversion Stream)用于字符流和字节流之间的转换。3. 文件流(File Stream)专门用于对文件进行读写操作的流4. 内存流(Memory Stream)将数据保存在内存中进行读写操作的流。5. 对象流(Object Stream)用于序列化和反序列化对象的流。
46.MyISAM与InnoDB区别
存储结构与文件类型MyISAM使用非聚集索引,索引和数据文件是分离的。在磁盘上存储为三个文件:.frm(表定义)、.MYD(数据文件)、.MYI(索引文件)。支持静态表、动态表和压缩表三种存储格式。InnoDB使用聚集索引,索引和数据是关联在一起的,表数据文件本身就是按B+Tree组织的一个索引结构。有两种存储方式:共享表空间存储和多表空间存储。表结构和MyISAM一样,以表名开头,扩展名是.frm。如果使用共享表空间,所有表的数据文件和索引文件保存在一个表空间里;如果使用多表空间,每个表都有一个表空间文(.ibd)。2. 事务与外键(重点)MyISAM不支持事务处理,也不支持外键。InnoDB支持事务处理,是事务安全的存储引擎。支持外键,可以确保数据的参照完整性。3. 锁机制(重点)MyISAM只支持表级锁,无论是读操作还是写操作都会锁定 整个表。表级锁开销小,加锁快,但并发度低。InnoDB支持表级锁和行级锁,默认情况下使用行级锁。行级锁开销大,但并发度高,可以有效减少锁冲突。InnoDB的行锁是通过索引实现的,如果没有命中索引,则会退化为表锁。4. 性能与功能MyISAM强调性能,特别是读操作的性能,因为表级锁在读取时不会阻塞其他读操作。适用于读多写少的场景,如Web应用中的静态内容表。支持全文索引(FULLTEXT),在全文搜索方面性 能较好。InnoDB提供了更丰富的数据库功能,如事务、外键等。适用于需要高并发写入和复杂查询的场景。不直接支持全文索引,但可以通过插件(如Sphinx)实现。5. 计数与数据维护MyISAM保存了表的总行数,执行SELECT COUNT(*) FROM table;时直接返回该值,速度很快。InnoDB不保存表的总行数,执行SELECT COUNT(*) FROM table;时需要全表扫描,速度较慢。在处理大量数据时,InnoDB的自动增长列和索引管理更为复杂。6. 备份与恢复MyISAM数据以文件形式存储,备份和恢复时可以单独针对某个表进行操作。InnoDB备份和恢复相对复杂,需要拷贝数据文件、备份binlog或使用mysqldump等工具。在数据量较大时,备份和恢复操作可能较为耗时。7. 适用场景MyISAM适用于读操作远多于写操作的场景,如Web应用的静态内容表。适用于不需要事务和外键支持的应用。InnoDB适用于需要事务处理、外键支持以及高并发写入的应用。适用于对数据完整性和一致性要求较高的场景。
47.Gc垃圾回收
是自动管理程序内存的机制,它帮助程序员自动释放那些不再被程序所使用的内存对象,从而避免内存泄漏等问题。垃圾对象的判断算法引用计数算法:给对象增加一个引用计数器,每当有一个地方引用它时,计数器就+1;当引用失效时,计数器就-1。任何时刻计数器为0的对象就是不能再被使用的,即对象已“死”。然而,这种算法存在内存空间浪费严重和循环引用的问题,因此Java并不采用此算法。2. 可达性分析算法:通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时(即GC Roots到这个对象不可达),证明此对象是不可用的。在Java中,可作为GC Roots的对象包括虚拟机栈(栈帧中的本地变量表)中引用的对象、本地方法栈中JNI(Native方法)引用的对象等。垃圾回收算法标记-清除算法:首先标记出所有需要回收的对象,然后在标记完成后统一回收所有被标记的对象。这种算法的缺点是效率问题和空间问题,即标记和清除的效率都不高,且标记清除后会产生大量不连续的内存碎片。2. 复制算法:将内存空间分为两部分,每次只使用其中的一部分。当这部分内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另一部分上面,然后再把已经使用过的内存区域一次清理掉。这种算法的优点是每次都是对整个半区进行内存回收,内存分配时也就不需要考虑内存碎片等复杂情况,但缺点是空间利用率低,且存活对象较多时复制开销大。3. 标记-整理算法:标记过程与标记-清除算法一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。这种算法解决了空间利用率低的问题,但仍然存在复制搬运元素开销大的问题。4. 分代收集算法:根据对象存活的不同生命周期将内存划分为不同的域,如老年代和新生代。老年代的特点是每次垃圾回收时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量垃圾需要被回收。因此,可以根据不同区域选择不同的算法,如新生代使用复制算法,老年代使用标记-整理算法。
48.数据库如何优化
索引优化合理创建索引:分析查询语句中用于条件过滤、连接操作和排序的列,并在这些列上创建索引。但索引并非越多越好,过多的索引会增加存储成本和数据更新的开销。覆盖索引:尽量使用覆盖索引,即索引包含了查询所需的所有列,这样查询时可以直接从索引中获取数据,而无需回表查询,提高查询效率。查询语句优化避免全表扫描:确保查询语句能够利用索引来过滤数据,避免使用没有索引支持的条件进行查询,导致全表扫描。优化连接查询:在连接多个表时,确保连接条件正确且使用了合适的索引,以减少连接操作的成本。使用合适的聚合函数:在进行聚合查询时,选择合适的聚合函数,并确保查询语句能够高效地计算聚合结果。数据类型优化选择合适的数据类型:根据数据的实际范围和用途,选择占用空间小、查询效率高的数据类型。避免使用 TEXT 和 BLOB 类型:如果可能,尽量避免 在经常查询的列中使用 TEXT 和 BLOB 类型,因为这些类型的数据存储和查询效率相对较低。数据库设计优化范式化设计:遵循数据库设计的范式,以减少数据冗余,提高数据的一致性和完整性。但在某些情况下,为了提高查询性能,可以适当违反范式,进行反范式化设计。合理分表:根据数据的特点和查询需求,将大表拆分成多个小表,以提高查询效率和维护性。例如,可以按照数据的时间范围、业务模块等进行分表。服务器配置优化调整内存配置:根据服务器的物理内存大小和数据库的使用情况,合理配置数据库的内存参数,如缓冲池大小、排序缓冲区大小等,以提高数据库的性能。优化磁盘 I/O:使用高速的磁盘设备,如固态硬盘(SSD),可以显著提高数据库的读写性能。同时,可以通过调整磁盘缓存、优化磁盘分区等方式来进一步优化磁盘 I/O。定期维护数据库清理无用数据:定期删除不再使用的数据,以减少数据库的存储空间和查询数据量。优化表结构:定期对表进行分析和优化,以更新统计信息、整理碎片,提高数据库的性能。备份数据库:定期备份数据库,以防止数据丢失,并在必要时能够快速恢复数据库。
49.mysql什么是死锁,怎么解决
死锁的定义在 MySQL 里,死锁指的是两个或多个事务在执行过程中,因争夺锁资源而造成的一种互相等待的局面。若没有外力的介入,这些事务都无法继续推进。解决死锁的方法1. 优化事务逻辑减少事务持有锁的时间:把不必要的操作移出事务,让事务尽快提交或回滚,以此减少锁的持有时间,降低死锁发生的概率。按相同顺序访问资源:在多个事务里,都按照相同的顺序来访问表或者行,避免循环等待的情况。2. 调整隔离级别降低隔离级别:不同的隔离级别对锁的使用和持有时间是不一样的。可以把隔离级别从较高的(如可串行化)调整为较低的(如读已提交),减少锁的竞争。不过要注意,降低隔离级别可能会引发其他并发问题,像脏读、不可重复读等。3. 设置锁超时时间设置 innodb_lock_wait_timeout 参数:该参数规定了事务等待锁的最长时间。当事务等待锁的时间超过这个设定值时,就会自动回滚,从而解除死锁。可以通过以下语句设置该参数:上述语句把全局的锁等待超时时间设定为 60 秒。4. 死锁检测和回滚机制InnoDB 存储引擎的死锁检测:InnoDB 会自动检测死锁,一旦发现死锁,就会选择一个事务进行回滚,以解除死锁。被回滚的事务通常是开销较小的事务。可以通过以下语句查看死锁信息:该语句会显示 InnoDB 存储引擎的详细状态信息,其中包含最近一次死锁的详细情况,有助于分析死锁产生的原因。5. 数据库设计优化索引优化:合理地创建索引能够减少锁的范围,提高查询效率,进而降低死锁的发生概率。表结构优化:避免在一张表中存储过多的数据,可根据业务需求进行表的拆分,减少锁的竞争。
50.谈谈你对mybatis的认识
MyBatis 是一款优秀的持久层框架,它对 JDBC 操作数据库的过程进行了封装,使得开发者只需关注 SQL 本身,而无需处理如注册驱动、创建连接、关闭资源等繁杂的过程。
主要特点
1. SQL 与代码分离:MyBatis 采用 XML 或注解的方式将 SQL 语句与 Java 代码分离,这样便于对SQL 进行统一管理和优化。
2. 灵活的 SQL 编写:它支持编写动态 SQL,借助OGNL 表达式能够实现如 if、choose、 where 等动态元素,极大地提升了 SQL 编写的 灵活性。
3. 强大的映射能力:可以将结果集灵活地映射为Java 对象,不仅支持基本类型的映射,还支持关联对象和集合的映射。
4. 轻量级框架:MyBatis 框架本身不依赖过多外部库,使用起来较为轻便,学习成本也相对较低。
介绍mybatis的优缺点 适合场景
优缺点
优点:
能够完全掌控 SQL,便于进行性能优化。可以与传统项目无缝集成,尤其适合对 SQL 优
化有较高要求的场景。支持多种数据库,具备良好的数据库移植性。
缺点:
需要手动编写 SQL,在复杂查询场景下开发效率可能会受到一定影响。对复杂的对象关系映射支持不够友好,可能需要编写大量的映射配置。
适用场景
适用于对 SQL 性能要求较高,需要精细控制SQL 执行的项目。适合传统的 CRUD 应用以及数据访问层相对稳定的项目。 可用于与其他框架(如 Spring、Spring Boot)集成,构建企业级应用。
51.Hash碰撞是什么?如何解决?
hash碰撞指的是,两个不同的值(比如张三、李四的 id)经过hash计算后,得到的hash值相同,后来的李四 要放到原来的张三的位置,但是数组的位置已经被张三占了,导致冲突。开放寻址法开放定址法通过探测哈希表中的空闲位置来解决哈希碰撞。当发生碰撞时,哈希表会寻找下一个空闲的槽位来存储元素。常见的探测策略有:线性探测:如果当前位置发生碰撞,检查下一个位置,直到找到空位。二次探测:采用平方的方式逐步探测空闲位置。双重哈希:使用第二个哈希函数来计算探测步长。拉链法链地址法是通过为每个哈希表的槽位创建一个链表来存 储所有发生碰撞的元素。当发生碰撞时,新的元素会被加入到该位置的链表中。再哈希法当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。建立公共溢出区将哈希表分为基本表和溢出表。凡是与基本表发生冲突 的元素,一律填入溢出表。查找时,先在基本表中查 找,若找不到再到溢出表中查找。这种方法实现相对简 单,但可能增加查找的时间开销。
52.java中深拷贝和浅拷贝
浅拷贝:就是只复制某一个对象的引用地址,而不复制这个对象本身,这种复制方式意味着两个引用指针,指 向被复制对象的同一块内存地址。深拷贝:两个引用对象分别指向两个不同的对象,且两 个引用对象的引用分别指向两个不同的对象。就是说完 全创建一个一模一样的新对象,新对象和老对象之前不 共享任何内存,也就意味着对深对象的修改,不会影响 老对象的一个值。实现方式:在java里面无论是深拷贝还是浅拷贝,我们 都需要去通过实现Cloneable这样一个接口,并且实现 clone()方法,然后我们可以在clone()方法里面去实现浅 拷贝与深拷贝的业务逻辑,不同的是深拷贝需要对其内 的引用类型的变量,再进行一次 clone()。如何选择拷贝方式:如果对象有引用属性,那就要基于 具体的需求来选择浅拷贝还是深拷贝。意思是如果对象 引用任何时候都不会被改变,那么没必要使用深拷贝, 只需要使用浅拷贝就行了。如果对象引用经常改变,那 么就要使用深拷贝。
53.Mysql中常用的函数有那些?
数字函数CEIL(x)/CEILING(x):返回大于或等于x的最小整数。FLOOR(x):返回小于或等于x的最大整数。ROUND(x):返回离x最近的整数;ROUND(x,y)对x进行四舍五入的操作,返回值保留小数点后面指定的y位。二、字符串函数CONCAT(s1,s2,...):将s1,s2等多个字符串合并为一个字符串,分隔符默认是 逗号CONCAT_WS(x,s1,s2,...):同CONCAT(s1,s2,...),但是每个字符串之间要加上x,x可以是分隔符。GROUP_CONCAT(expr) 将多条记录合并为一个LENGTH(str)/CHAR_LENGTH(s)/CHARACTER_LENGTH(s):返回字符串s的字符数(不是字节长度)。LOWER(s)/LCASE(s):将字符串s的所有字母变成小写字母。UPPER(s)/UCASE(s):将字符串s的所有字母变成大写字母。TRIM(s):去掉字符串s开始和结尾处的空格。LTRIM(s):去掉字符串s开始处的空格。RTRIM(s):去掉字符串s结尾处的空格。SUBSTR(s, start, length)/SUBSTRING(s, start,length):从字符串s的start位置截取长度为length的子字符串。REPLACE(s,from_str,to_str):把字符串s中的from_str内容替换为to_str。POSITION(s1 IN s)/LOCATE(s1,s):从字符串s中获取s1的开始位置。三、日期函数CURDATE()/CURRENT_DATE():返回当前日期。CURRENT_TIME()/CURTIME():返回当前时间。NOW():返回当前日期和时间。DATE_ADD(date, INTERVAL expr type):计算起始日期date加上一个时间段后的日期。DATEDIFF(d1,d2):计算日期d1和d2之间相隔的天数。DATE_FORMAT(date,format):按表达式format的要求显示日期date。四、聚合函数AVG(expression):返回某列的平均值。COUNT(expression):返回某列/某组/整表的行数。MAX(expression):返回某列的最大值。MIN(expression):返回某列的最小值。SUM(expression):返回指定字段的总和。五、流程控制函数IF(test,v1,v2):如果表达式test成立,返回结果v1;否则,返回结果v2。ifnull(exp1,exp2) 如果表达式exp1不是null 返回 exp1,否则返回exp2
54.datetime 、timestamp的区别?
存储范围(知道)DATETIME:可以表示的日期时间范围从1000-01-01 00:00:00到9999-12-31 23:59:59。这个范围非常广泛,适用于需要存储历史久远数据的应用场景。TIMESTAMP:其标准范围是从1970-01-01 00:00:01 UTC到2038-01-19 03:14:07 UTC(即所 谓的“2038年问题”)。然而,值得注意的是,MySQL 5.6.5及以后的版本支持一个扩展的TIMESTAMP范 围,从1970-01-01 00:00:00 UTC到2106-02-07 06:28:15 UTC。尽管如此,TIMESTAMP的范围仍比 DATETIME小。2. 存储方式DATETIME:存储日期和时间时,不进行时区转换。它直接存储日期和时间值,因此在不同的时区查询时,显示的值是固定的,不会自动转换为当前会话的时区。TIMESTAMP:在存储时,MySQL会自动将日期和时间从当前时区转换为UTC(协调世界时)进行存储。在查询时,又会将其转换回客户端的时区进行显示。这使得TIMESTAMP在跨时区的应用中更为方便。3. 默认值与NULL值DATETIME:列默认可以为NULL,即如果不指定值,则默认为NULL。TIMESTAMP:列默认为NOT NULL,如果尝试创建一个TIMESTAMP列并设置其默认值为NULL,MySQL会自动将其默认值设置为0000-00-00 00:00:00(但在严格模式下,这可能会导致错误)。此外,一个表中可以有多个TIMESTAMP列,但只有一个 TIMESTAMP列可以设置为自动更新为当前时间戳 (ON UPDATE CURRENT_TIMESTAMP)。4. 存储空间(知道)DATETIME:通常需要8个字节的存储空间。TIMESTAMP:通常只需要4个字节的存储空间(尽管 在某些情况下,如MySQL 5.6.4及以前版本,TIMESTAMP的存储空间可能因存储的分数秒精度而有所不同)。5. 使用场景DATETIME:适用于需要大范围日期时间值且不需要自动时区转换的场景。TIMESTAMP:适用于需要自动时区转换、记录数据创建或修改时间戳的场景,尤其是当需要节省存储空间时(因为TIMESTAMP占用的空间较小)。
55.#{}和${}的区别
1. #{} 是预编译处理,${}是字符串替换。2. Mybatis在执行 SQL 时,#{} 会被替换成 ? 占位符,这样可以有效防止 SQL 注入攻击。MyBatis 会依据参数类型自动进行类型转换。3. Mybatis在 SQL 预编译之前,${} 就会被直接替换为参数值,这种方式存在 SQL 注入风险。参数值不会经过类型转换,所以需要手动处理引号等问题。主要用于动态表名、列名或者 ORDER BY 子句等场景。
56.索引哪些情况会失效
- 查询条件包含or
- like通配符可能导致索引失效。如 like '%a' 会失效, 而 like 'a%'不会失效
- 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效(最左前缀匹配原则)
- 索引列使用了函数或表达式 当查询条件中对索引列使用了函数、类型转换或表达式时,数据库需要先计算表达式的值,然后再进行查询,这会导致索引失效
- 对索引列运算(如,+、-、*、/),索引失效。
- 索引字段上使用(!= 或者 < >)时,可能会导致索引失效。
- 使用了NOT IN或NOT EXISTS:这些操作符在某些情况下可能导致索引失效,尤其是处理不确定数量的结果集时
- 索引列上存在过多的 NULL 值 索引字段上使用is null, is not null,可能导致索引失效。
- 表中数据量较少 对于数据量较少的表,数据库可能会选择全表扫描而不是利用索引,因为全表扫描的开销相对较小。
- 隐式类型转换:查询条件中的列与索引列的类型不匹配,数据库进行隐式类型转换,可能会使索引失效
57.书写高质量SQL的30条建议
- 查询SQL尽量不要使用select *而是select具体字段。
- 如果知道查询结果只有一条或者只要最大/最小一条记录,建议用limit 1
- 应尽量避免在where子句中使用or来连接条件
- 优化limit分页
- 优化你的like语句
- 使用where条件限定要查询的数据,避免返回多余的行
- 尽量避免在索引列上使用mysql的内置函数
- 应尽量避免在 where 子句中对字段进行表达式操作,这将导致系统放弃使用索引而进行全表扫
- Inner join 、left join、right join,优先使用Inner join,如果是left join,左边表结果尽量小
- 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
- 使用联合索引时,注意索引列的顺序,一般遵循最左匹配原则。
- 对查询进行优化,应考虑在where 及 order by 涉及的列上建立索引,尽量避免全表扫描。
- 如果插入数据过多,考虑批量插入。
- 在适当的时候,使用覆盖索引
- 慎用distinct关键字
- 删除冗余和重复索引
- 如果数据量较大,优化你的修改/ 删除语句。
- where子句中考虑使用默认值代替null。
- 不要有超过5个以上的表连接
- exists & in的合理利用
- 尽量用 union all 替换 union
- 索引不宜太多,一般5个以内
- 尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型
- 索引不适合建在有大量重复数据的字段上,如性别这类型数据库字段。
- 尽量避免向客户端返回过多数据量。
- 当在SQL语句中连接多个表时,请使用表的别名,并把别名前缀于每一列上,这样语义更加清晰。
- 尽可能使用varchar/nvarchar 代替 char/nchar。
- 为了提高group by 语句的效率,可以在执行到该语句前,把不需要的记录过滤掉。
- 如果字段类型是字符串,where时一定用引号括起来,否则索引失效
- 使用explain 分析你SQL的计划
58. BIO,NIO,AIO 有什么区别?
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。AIO:Asynchronous IO 是 NIO 的升级,也叫NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
59. MySQL慢查询怎么解决?
slow_query_log 慢查询开启状态。#查看 show variables like 'slow_query_log' #打开慢查询 set @@global.slow_query_log='on'
slow_query_log_file 慢查询日志存放的位置(这个目录需要MySQL的运行帐号的可写权限,一般设置为 MySQL的数据存放目录)。#查看位置 show variables like 'slow_query_log_file';
long_query_time 查询超过多少秒才记录。#查看慢查询时间 默认为10s show variables like 'long_query_time'; #更新为5s 更新当前会话的时间 set long_query_time=5 #更新全局 set global long_query_time=5
60.MySQL 高并发环境解决方案?
MySQL 高并发环境解决方案 分库 分表 分布式 增加二级缓存。。。。。需求分析:互联网单位 每天大量数据读取,写入,并发性高。现有解决方式:水平分库分表,由单点分布到多点数据库中,从而降低单点数据库压力。集群方案:解决DB宕机带来的单点DB不能访问问题。读写分离策略:极大限度提高了应用中Read数据的速度和并发量。无法解决高写入压力。
62. 线程的常用方法
join() :t.join()方法只会使主线程进入等待池并等待t线程执行完毕后才会被唤醒。并不影响同一时刻处在运行状态的其他线程。yield() :t.yield()让当前线程从“运行状态”进入到“就绪状态”,和让其它线程获一起回到起跑线取 cpu 执行权,wait 和 sleep区别1. 资源释放: sleep()只会释放时间片, wait()会同时释放时间片和锁标记
2. 来源和调用者: sleep()是来自Thread类的静态方法, 通过Thread类调用. wait()是来自于Object类的实例方法, 通过临界资源对象调用
3. 进入状态: sleep()使线程进入有限期等待状态, wait()是线程进入的是无限期等待状态
63.在 mapper 中如何传递多个参数?
顺序传参法(不推荐)arg传参使用arg0 ,arg1 .... 下标从0开始 分别代表第一、第二 ...参数param传参使用param1,param2 下标从1开始 分别代表第一、第二参数方法2:@Param注解传参法(常用)#{}里面的名称对应的是注解@Param括号里面修饰的名 称。这种方法在参数不多的情况还是比较直观的,推荐使用。方法3:Map传参法#{}里面的名称对应的是Map里面的key名称。这种方法适合传递多个参数,且参数易变能灵活传递的情况。方法4:Java Bean传参法#{}里面的名称对应的是User类里面的成员属性。这种方法直观,需要建一个实体类,扩展不容易,需要加属性,但代码可读性强,业务逻辑处理方便,推荐使用。
64. 介绍一下乐观锁和悲观锁
悲观锁(Pessimistic Locking)悲观锁假设最坏的情况,即认为冲突一定会发生,因此在操作数据前就进行加锁处理,确保数据加锁期间不会被其他线程修改。Java中常见的悲观锁实现方式包括:1. synchronized关键字:Java的内置锁,可用于方法或代码块上,确保同一时刻只有一个线程能执行该段代码。2. ReentrantLock:java.util.concurrent.locks包下的锁,提供了比synchronized更灵活的锁操作,如尝试非阻塞地获取锁、可中断地获取锁以及超时获取锁等。乐观锁(Optimistic Locking)乐观锁假设最好的情况,即认为冲突不会经常发生,因此在数据提交更新时,才会真正对数据加锁。乐观锁通常是通过版本号(version)或时间戳(timestamp)来实现的,在更新数据时,会检查版本号或时间戳是否发生了变化,从而判断是否发生了并发修改。
65.索引的作用
提高查询效率:通过使用索引,数据库可以避免全表扫描,快速定位到满足条件的数据,从而大大提高查询的速度。尤其是在处理大规模数据时,索引的作用更加明显。保证数据唯一性:可以通过创建唯一索引来确保表中某些列的数据具有唯一性,防止插入重复的数据记录。支持数据排序:索引中的数据是按照一定的顺序存储的,因此在进行排序操作时,如果排序的列上有索引,数据库可以直接利用索引的顺序来进行排序,而无需额外的排序操作,提高了排序的效率
66. union union all 区别
UNION 和 UNION ALL 都是 SQL 语句中用来合并两个或 多个 SELECT 语句的结果集的操作符,但它们之间有一些关键的区别:1. 去重UNION:在合并结果集时,会自动去除重复的行,只保留唯一的结果。UNION ALL:不会去除重复的行,所有的结果都会被包含在最终的结果集中,包括重复的行。2. 性能UNION:因为需要去重,所以可能会有额外的性能开销。UNION ALL:通常比 UNION 更快,因为它不需要检查重复的行。3. 使用场景当你需要合并的结果集中不包含重复的行时,使用UNION。当你需要包含所有结果,包括重复的行时,使用UNION ALL。
67.哈希码
哈希码(Hash Code)是计算机科学中用于快速数据查找的一种编码方式。它通过某种算法将输入(通常是一个字符串或一段数据)转换成一个固定长度的输出值,这个输出值就是哈希码。哈希码的主要目的是提高数据检索的效率,通过哈希码,可以快速定位到数据在存储结构中的位置,从而加快查找速度。哈希码的特点1. 固定长度:无论输入数据的长度如何,哈希码的长度都是固定的。2. 唯一性:理想情况下,不同的输入数据应该产生不同的哈希码,但在实际应用中,由于哈希函数的设计限制和哈希值的长度限制,可能会出现不同的输入数据产生相同的哈希码的情况,这称为哈希冲突(Hash Collision)。3. 不可逆性:从哈希码一般无法直接还原出原始数据,这是哈希码的一个重要安全特性。哈希码的应用1. 数据检索:在数据库和索引结构中,哈希码用于快速定位数据。2. 安全:在密码学和安全领域,哈希码用于生成摘要(如MD5、SHA-1、SHA-256等),用于验证数据的完整性和真实性。3. 集合实现:在Java等编程语言中,哈希码是实现 HashMap、HashSet等集合类的关键。这些集合通过哈希码来确定元素的存储位置,从而快速地进行插入、删除和查找操作。哈希函数的设计设计一个好的哈希函数非常重要,因为它直接影响到哈希表的性能和安全性。一个好的哈希函数应该具有以下 特点:均匀性:哈希值应该尽可能均匀地分布在哈希表的空间中,以减少哈希冲突。雪崩效应:输入数据的微小变化应该导致哈希值的显著变化,这有助于减少哈希冲突并提高安全性。计算效率:哈希函数的计算应该尽可能高效,以支持快速的数据检索。注意事项哈希冲突是不可避免的,因此设计哈希表时需要考虑如何处理冲突,常见的冲突解决方法有开放寻址法和链地址法。
68.索引的优缺点
索引的优点1. 提高检索速度:索引可以显著加快数据检索的速度,尤其是在大型数据集中,通过减少磁盘I/O操作次数,降低系统资源消耗。2. 加速排序和分组操作:索引可以优化ORDER BY和GROUP BY子句的执行速度,因为索引本身是有序的3. 提高连接性能:索引能加快多表连接(JOIN)操作的执行效率。4. 保证数据完整性:通过唯一性约束或主键约束,索引可以帮助保证数据的唯一性和完整性。5. 支持快速查找:索引提供快速的查找功能,使得查询更加灵活和高效。索引的缺点1. 占用存储空间:索引会占用额外的存储空间,这可能会随着数据量的增加而显著增大。2. 维护成本高:索引需要定期维护,包括创建、更新和删除索引,这可能会增加数据库的负担和维护成本3. 增加写操作的时间:对表进行插入、更新和删除操作时,索引也需要进行相应的更新,这可能会增加写操作的时间。4. 不适用于所有查询:并非所有的查询都适合使用索引,有些查询可能会因为索引而变得更慢。5. 索引失效:如果索引选择不当或者使用不当,可能会导致索引失效,从而影响查询性能。
69.如何获取生成的主键
对于支持主键自增的数据库
需要将useGeneratedKeys 设置为true,允许mybatis获取数据库内部自动生成的主键,任何使用keyProperty存储主键值的属性或key(如果参数类型是javabean,那么就指明其中一个存储主键值,如果参数类型是map,就指明存放主键值的key),keyColumn 指定在表中代表主键的字段名
对于不支持主键自增的数据库
需要使用selectkey元素处理主键
keyProperty 用于存放主键值
- 如果参数类型是javabean keyProperty 指明javabean中的哪个属性存储主键值
- 如果参数是Map类型,keyProperty 指明用于存放主键值的key
keyColumn 代表主键的列名
order 指定key值什么时候存入keyProperty , 是insert 前 或 后
值:
- BEFORE 生成主键,设置
keyProperty
再执行插入语句- AFTER 先执行插入语句,然后是
selectKey
中的语句
70. MyBatis实现一对一有几种方式?具体怎么操作的?
联合查询(嵌套结果)联合查询是几个表联合查询,只查询一次, 通过在 resultMap里面配置association节点配置一对一的类就可以完成;嵌套查询嵌套查询是先查一个表,根据这个表里面的结果的id做为参数(前提该id在另一个表中是外键关系 ),去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
71.MyBatis实现一对多有几种方式,怎么操作的?
区别
嵌套结果 是一条关联sql语句 ,所有的结果在ResultMap中映射 ,可以采用自动映射 ; 嵌套select语句,会出现1+N条sql语句 ,1指定的一对多中一端,一条sql语句 ,N 是额外发送的查询多的sql语句,sql语句简单
试想一下:如果没有高级结果映射,想返回一个Order对象,且里面包含订单明细,如何 实现?
嵌套select语句,在一对多结果映射,尽量只查询一个订单(因为有1+N的问题)
适用场景
- 如果每次根据id查询订单时都要订单明细,建议使用嵌套结果
- 如果有时只需要订单,不要明细,有时两者都要,可以考虑嵌套select 配置延迟加载
- 嵌套select不适合列表查询,会出现N+1问题,如果使用分页查询,还会出现分页不准。
72.为什么 wait() 定义在 object 里,而不是 Thread类里
Java的锁是对象级别的锁,而不是线程级别,所以 wait 是 Object 的方法,wait是操作的是对象监视器锁,而不是线程本身,所以只有放在object中,才能用任意对象作为锁实现线程间的等待。
73. Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?
MyBatis仅支持association关联对象和collection关联 集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。延迟加载全局延迟加载 开启时,所有关联对象都会延迟加 载<setting name="lazyLoadingEnabled" value="true"/>局部延迟加载 可以覆盖全局在association或collection上有fetchType属性。fetchType设置:eager 立即加载lazy 延迟加载原理:使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 student.getClazz().getName(),拦截器invoke()方法 发现student.getClazz()是null值,那么就会单独发送 事先保存好的查询关联班级Clazz对象的sql,把Clazz 查询上来,然后调用clazz.setName(name),于是 student的对象clazz属性就有值了,接着完成 student.getClazz().getName()方法的调用。
74.乐观锁的版本号和时间戳
版本号:
在数据库表中添加版本号字段,在每次读取时都读取其中的版本号,在更新提交时判断提交时的版本号,与新读取的版本号是否一致,如果不一致说明数据被修改过
时间戳:
在数据库表中添加时间戳字段,每次读取数据时,同时读取时间戳。更新数据时,比较当前时间戳和读取时的时间戳是否一致。
75.一级缓存和二级缓存的区别
一级缓存session缓存
- 默认mybatis开启一级缓存
- 在同一会话中,同一查询多次调用,会利用一级缓存 (第一次查询时,查询数据库,将对象缓存,再查询时,直接取缓存不再查询数据库)
- session 是线程非安全的,操作完会关闭session ,一级缓存会失效
二级缓存
命名空间级缓存 ,同一命名空间内缓存可以使用 。
特点
二级缓存 默认是不开启的
开启二级缓存,在映射文件中添加
要缓存的对象所属类必须实现序列化接口
序列化接口Serializable
扩展:
- 什么是序列化? 将对象以二进制形式进行存储或传输 ,反序列化是将二进制数据转换为对象
- 实现序列化要用到ObjectInputStream 实现反序列化,使用ObjectOutputStream实现序列化
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
如果一张表中的数据频繁的增删改 ,不适合做缓存
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存的数量是有限制的,默认缓存1024个引用
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存不会定时进行刷新(也就是说,没有刷新间隔)
默认情况下,如果表中数据没有增删改,缓存会一 直有效
缓存会被视为读/写缓存
- 只读缓存 不允许 修改缓存对象
- 写缓存 ,允许修改 查询缓存时,返回的是对象的浅拷贝
属性
eviction 缓存清除策略
LRU
(Least Recently Used)– 最近最少使用:移除最长时间不被使用的对象。 默认策略FIFO
(first in first out 队列结构)– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。flushInterval 定时刷新缓存,可以传递一个整数代表时间,单位ms, 默认不刷新
size 缓存引用数目,默认是1024
readOnly 缓存是否是只读的 默认值是 false
默认实现
二级缓存,默认实现类: PerpetualCache,永久缓存,它的底层使用HashMap进行缓存
76.Mybatis的解析和运行过程
MyBatis编程步骤是什么样的?1. 创建SqlSessionFactory2. 通过SqlSessionFactory创建SqlSession3. 通过sqlsession执行数据库操作4. 调用session.commit()提交事务5. 调用session.close()关闭会话请说说MyBatis的工作原理在学习 MyBatis 程序之前,需要了解一下 MyBatis 工作原理,以便于理解程序。MyBatis 的工作原理如下图1. 读取 MyBatis 配置文件:mybatis-config.xml 为MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。2. 加载映射文件:映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。3. 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。4. 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。5. Executor 执行器:MyBatis 底层定义了一个Executor 接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。6. MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的id、参数等信息。7. 输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。8. 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
77.异常处理
java 常见Exception(中)运行时异常和非运行时异常运行时异常都是RuntimeException类及其子类异常:IndexOutOfBoundsException 索引越界异常(常见于集合list 数组 indexOf查找)NullPointerException:空指针异常ArrayOutOfBoundsException:数组索引越界异常ClassNotFoundException:类文件未找到异常 (常见于需要依赖的类库jar包 找不到 )ClassCastException:造型异常(类型转换异常)这些异常是不检查异常(Unchecked Exception),程序中可以选择捕获处理,也可以不处理。这些异常 一般是由程序逻辑错误引起的。扩展: 自定义异常(验证异常、业务异常....)非运行时异常也称为编译期异常。 是RuntimeException以外的异常,类型上都属于 Exception类及其子类。从程序语法角度讲是必须进行处 理的异常,如果不处理,程序就不能编译通过。如:IOException、文件读写异常FileNotFoundException:文件未找到异常EOFException:读写文件尾异常MalformedURLException:URL格式错误异常SocketException:Socket异常SQLException:SQL数据库异常
Error 和Exception的区别Error是不可恢复的系统级错误也无法捕获Exception一般性错误可以捕获处理
78.Mybatis动态sql
MyBatis 动态 SQL 的作用MyBatis 动态 SQL 是 MyBatis 框架提供的一种强大功能,其主要作用在于提高 SQL 语句的灵活性和重用性。通过动态 SQL,开发者可以根据不同的业务逻辑和运行时传入的参数,动态地生成或拼接 SQL 语句,从而避免了手动编写大量条件判断语句和拼接 SQL 的繁琐过程。这不仅减少了代码的重复度,还提高了代码的可维护性和可读性。动态 SQL 的执行原理MyBatis 动态 SQL 的执行原理主要依赖于 XML 映射文件中的动态 SQL 标签和 OGNL(Object-GraphNavigation Language)表达式。当 MyBatis 加载映射文件时,会解析其中的动态 SQL 标签,并将它们转换为一系列的逻辑表达式和条件判断结构。在 后,MyBatis会根据这些参数值动态地决定哪些 SQL 片段应该被执行或忽略。具体来说,MyBatis 使用 OGNL 表达式来解析和计算动态 SQL 中的条件表达式,并在预编译阶段将动态生成的 SQL 发送给数据库进行预编译,然后在执行阶段绑定参数值并最终执行 SQL。这样既保证了 SQL 的安全性和性能(由于预编译),又实现了 SQL 结构的动态化和灵活性。MyBatis 中的动态 SQL 标签MyBatis 提供了多种动态 SQL 标签,以便在 XML 映射文件中编写灵活的 SQL 语句。以下是一些主要的动态SQL 标签及其用途:1. if:用于在生成的 SQL 语句中添加条件判断。可以根据指定的条件决定是否包含某个 SQL 语句片段。2. choose(包含 when、otherwise):类似于 Java中的 switch 语句,根据条件选择执行不同的 SQL 语句片段。choose 标签可以包含多个 when 标签和一个可选的 otherwise 标签。3. foreach:用于遍历集合或数组,并根据指定的模板将集合元素或数组元素插入到 SQL 语句中。通常用于处理批量操作或 IN 子查询等场景。4. set:用于在生成的 SQL 语句中添加 SET 子句。它主要用于更新操作,可以根据条件来动态生成需要更新的列。5. where:用于在生成的 SQL 语句中添加 WHERE 子句。它可以自动处理条件语句的前缀,并在有条件语句存在时添加 WHERE 关键字。6. trim:通过修剪 SQL 语句的开头和结尾来动态生成SQL 片段。它可以用于去除不必要的 SQL 关键字或条件语句,并提供了一些属性来定义修剪规则。7. bind:用于将表达式的结果绑定到一个变量上。可以在 SQL 语句中使用这个变量,避免重复计算表达式。这些动态 SQL 标签在 MyBatis 中提供了灵活的查询和更新操作的能力,使得开发者可以根据不同的业务逻辑和参数动态地生成 SQL 语句,从而提高了开发的效率和代码的质量。
79.哈希函数的设计和注意事项
均匀性:哈希值应该尽可能均匀地分布在哈希表的空间中,以减少哈希冲突。雪崩效应:输入数据的微小变化应该导致哈希值的显著变化,这有助于减少哈希冲突并提高安全性。计算效率:哈希函数的计算应该尽可能高效,以支持快速的数据检索。注意事项哈希冲突是不可避免的,因此设计哈希表时需要考虑如何处理冲突,常见的冲突解决方法有开放寻址法和链地址法。在使用哈希码进行安全性相关的操作时(如密码存储),应避免使用简单的哈希函数(如MD5、SHA- 1),因为这些函数存在已知的弱点,容易受到攻击。应使用更安全的哈希函数(如SHA-256、SHA-3 等)。
80.索引能提高查询性能的原因
快速定位数据:索引就像一本书的目录,通过它可以快速定位到所需数据在数据库中的位置,避免全表扫描。例如,在一个包含大量用户信息的表中,如果要查询特定姓名的用户记录,在姓名列上建立索引后,数据库可以直接根据索引找到对应的记录,而无需逐行扫描整个表,从而大大提高查询效率。减少 I/O 操作:数据库在读取数据时,需要从磁盘将数据块加载到内存中。使用索引可以精准地获取所需数据所在的数据块,减少不必要的数据块读取,降低 I/O 操作次数,进而提高查询性能。
81.序列化和反序列化
序列化:将对象以二进制形式存储到存储介质 (硬盘、文件、网络 )条件 : 对象所属的类必须实现序列化接口Serializable反序列化:从文件或网络中读取二进制转换为对象扩展:序列化 ObjectOutputStream (写)反序列化 ObjectInputStream (读)序列化是把内存Java对象保存到存储介质中反序列化就是把存储介质中的数据转化为Java对象。需要进行序列化的对象的类必须实现Serializable 接口Java通过ObjectInputStream和ObjectOutputStream实现序列化和反序列化应用场景:(重点前2个)缓存系统(如 Redis 存储 Java 对象)(如JDK序列化器)消息中间件(如 Kafka、RabbitMQ)传输对象时,需先序列化字节流再通过网络发送mybatis 二级缓存 (可以不说 因为用的少)
82.多线程同步有哪几种方式
单机:
synchronized
• JVM 内部实现,线程进入/退出同步块时自动加/放锁。
• 只在当前进程的内存地址空间里生效,进程外不可见。Lock(如 ReentrantLock)
• 由 JDK 提供的显式锁对象,功能比 synchronized 更丰富(可重入、可限时、可公平)。
• 同样仅作用于单进程,多实例部署时无法互相感知。分布:
Redis分布式锁:利用 Redis 单线程模型提供的 原子命令 完成“只有一个客户端能写成功”的互斥效果。
83. 反射的核心作用
什么是反射机制?(掌握)JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。反射的核心作用(掌握)动态获取类信息在运行时知道一个类的完整结构(如包含哪些方法、字段、构造器),无需在编译时提前确定。 例如:即使只拿到一个对象,也能反向 “看透” 它属于哪个类,以及这个类的所有细节。动态创建对象不通过 new 关键字,而是根据类名(字符串形式)直接创建对象。场景:框架(如 Spring)中根据配置文件动态生成Bean 对象。动态调用方法和操作字段运行时调用对象的方法(即使方法是私有的),或修改对象的字段值(无视访问修饰符)。例如:通过反射强制修改一个被 private 修饰的字段值。实现通用代码逻辑编写与具体类无关的通用逻辑(如序列化、依赖注入),增强代码灵活性。反射的典型应用场景(掌握)Spring 框架Bean 实例化:Spring 的核心功能之一是 IoC(控制反转)和 DI(依赖注入),它借助反射来创建和管理Bean 对象。在 Spring 的配置文件或者注解里,会定义 Bean 的信息。Spring 容器启动时,会依据这些信息,利用反射调用类的构造方法来创建 Bean 实例。比如,配置文件里定义了一个UserService类,Spring 就会通过反射实例化这个类。AOP(面向切面编程):AOP 是 Spring 框架的另一个关键特性,它可以在不修改原有代码的情况下,为程序添加额外的功能,像日志记录、事务管理等。Spring AOP 利用动态代理来实现,而动态代理的实现离不开反射机制。当代理对象的方法被调用时,Spring 会通过反射调用目标对象的方法,并且在方法 调用前后执行相应的增强逻辑。注解处理:Spring 大量使用注解来配置和管理Bean,例如@Component、@Service、@Autowired等。Spring 容器会通过反射扫描类上的注解,依据注解的信息进行相应的处理,像创建 Bean、注入依赖等。Hibernate 框架对象 - 关系映射(ORM):Hibernate 是一个流行的Java ORM 框架,它可以把 Java 对象映射到数据库表。Hibernate 在运行时利用反射来获取实体类的属性和方法信息,进而生成 SQL 语句,实现对象的持久化操作。比如,当保存一个实体对象时,Hibernate会通过反射获取对象的属性值,然后生成对应的INSERT语句。动态加载实体类:Hibernate 支持动态加载实体类,它可以在运行时通过反射加载实体类,并且根据实体类的映射信息来操作数据库。这样就能在不修改代码的情况下,动态地添加或修改实体类。MyBatis 框架Mapper 接口代理:MyBatis 提供了 Mapper 接口的代理机制,让开发者可以通过接口来操作数据库。MyBatis 在运行时会利用反射生成 Mapper 接口的代理对象,当调用 Mapper 接口的方法时,代理对象会根据方法名和参数,动态地生成 SQL 语句并执行。结果集映射:MyBatis 在处理查询结果时,会通过反射将查询结果映射到 Java 对象上。它会根据实体类的属性名和查询结果的列名进行匹配,然后通过反射调用实体类的 setter 方法来设置属性值。JUnit 框架测试方法执行:JUnit 是 Java 中常用的单元测试框架,它通过反射来发现和执行测试方法。JUnit 会扫描测试类中的所有方法,根据方法上的@Test注解来识别哪些方法是测试方法,然后通过反射调用这些测试方法。
84.Restful风格的特点
什么是Restful风格Restful是一种api设计风格,对同一资源路径,如/user ,不同的动词代表不同的作用。REST(英文:Representational State Transfer,简称REST),可译为"表现层状态转化”。RESTful(REST 风格)是一种当前比较流行的互联网软件架构模式, 它是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它充分并正确地利用 HTTP 协议的特性,为我们规定了一套统一的资源获取方式,以实现不同终端之间(客户端与服务端)的数据访问与交互。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。Restful的特点1.URI:每一个URI(统一资源定位符)指向一个特定的资源,通过URI来访问资源,独一无二2.客户端和服务器之间,传递这种资源的某种表现3.客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。RESTful 架构风格是围绕资源展开的,资源操作都是统一接口的:GET(SELECT):从服务器取出资源(一项或多项)。POST(CREATE):在服务器新建一个资源。PUT(UPDATE):在服务器更新资源(客户端提供完整资源数据)。PATCH(UPDATE):在服务器更新资源(客户端提供需要修改的资源数据)。DELETE(DELETE):从服务器删除资源。使用RESTful操作资源【GET】 /users # 查询用户信息列表【GET】 /users/1001 # 查看某个用户信息【POST】 /users # 新建用户信息【PUT】 /users/1001 # 更新用户信息(全部字段)【PATCH】 /users/1001 # 更新用户信息(部分字段)【DELETE】 /users/1001 # 删除用户信息总结RESTful风格要求每个资源都使用 URI (Universal Resource Identifier) 得到一个唯一的地址。所有资源都共享统一的接口,以便在客户端和服务器之间传输状态。使用的是标准的 HTTP 方法,比如 GET、PUT、POST 和 DELETE。总之就是REST是一种写法上规范,获取数据或者资源就用GET,更新数据就用PUT,删除数据就用DELETE,然后规定方法必须要传入哪些参数,每个资源都有一个地址。
85.Mybatis分页有哪几种方式
MyBatis实现分页的方式主要有以下几种:1. 物理分页原生SQL分页:直接在SQL语句中使用数据库提供的分页语法,如MySQL的LIMIT、Oracle的ROWNUM或ROW_NUMBER()、SQL Server的OFFSET FETCH等。这种方式依赖于数据库的分页功能,性 能较好,但SQL语句的编写需要针对具体的数据库进行调整。MyBatis插件(Interceptor):通过实现MyBatis的Interceptor接口,可以在SQL执行前后进行拦截处理,自动地在查询语句后添加分页参数。这种方式的好处是可以在不修改原有SQL语句的情况下实现分页,提高了代码的复用性和可维护性。PageHelper是MyBatis分页插件的一个流行选择,它支持多种数据库,并且配置和使用起来相对简单。2. 内存分页RowBounds(不推荐): 这种方式是在查询出所有结果后,在Java内存中通过程序逻辑进行分页处理。这种方法虽然实现简单,但在处理大量数据时效率极低,因为它需要将所有数据加载到内存中,消耗大量内存资源,并且分页操作(如跳页)时性能更差。因此,在实际开发中,几乎不会使用内存分页。3. 第三方分页插件或库除了PageHelper之外,还有其他一些第三方分页插件或库,MyBatis-Plus的分页功能。MyBatis Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。它内置了分页插件,可以非常方便地实现分页功能,并且支持多种数据库。4. 自定义分页查询在一些特殊场景下,可能需要根据业务逻辑实现自定义的分页查询。这通常涉及到在Mapper接口中定义分页查询的方法,并在对应的XML文件中编写相应的SQL语句。在SQL语句中,可以使用数据库的分页语法或者结合业务逻辑进行分页处理。这种方式需要开发者对数据库和MyBatis有较深的理解。在实际开发中,推荐使用物理分页的方式,特别是通过MyBatis插件(如PageHelper)来实现分页功能,因为它既高效又方便。如果项目已经集成了MyBatis-Plus,那么直接使用MyBatis-Plus的分页功能也是一个不错的选择。
86.超大分页怎么处理
在MySQL中处理大量数据的分页查询时,性能是一个重要的考虑因素。随着数据量的增加,分页查询可能会变得非常慢,特别是当请求的页码很高时。以下是一些优化MySQL分页查询性能的策略:1. 使用索引:确保在用于排序和过滤的列上建立了适当的索引。例如,如果你通常按日期或ID排序,那么在这些列上创建索引将有助于提高查询性能。2. 优化查询:避免在分页查询中使用OFFSET,因为它会导致MySQL扫描并丢弃大量的行,即使这些行最终不会被返回。相反,可以使用基于主键或唯一索引列的范围查询来实现分页。3. 基于主键或索引列的分页:如果有一个自增的主键或唯一索引列(如id),可以记录上一次查询的最后一个id值,并在下一次查询时使用这个值作为起点。4. 记住上一页的最后一条记录:除了使用主键外,还可以记住上一页查询结果的最后一条记录的完整信息(如所有列的值),并在下一页查询时使用这些信息作为过滤条件。这通常涉及到更复杂的查询,但可以在没有主键或唯一索引列的情况下使用。5. 使用覆盖索引:如果查询只涉及少数几列,并且这些列上都有索引,那么MySQL可以直接从索引中读取数据,而不需要回表查询。这可以显著提高查询性能。针对大偏移量的情况,可以采用覆盖索引 + 子查询的方式来优化。6. 延迟关联:如果查询涉及多个表,并且分0的列进行的,那么可以先对这个表进行分页查询,然后再与其他表进行关联。这可以减少需要处理的数据量。7. 优化服务器配置:确保MySQL服务器的配置是优化的,特别是与内存和缓存相关的配置。这可以帮助提高查询性能。8. 考虑使用全文索引或搜索引擎:对于非常大的数据集和复杂的查询,可能需要考虑使用全文索引或专门的搜索引擎(如Elasticsearch)来提高查询性能。9. 分析查询计划:使用EXPLAIN语句来分析查询计划,并根据结果调整查询和索引。10. 避免在分页查询中使用复杂的计算或函数:这些操作会增加查询的复杂性,并可能导致性能下降。
87. 分页插件的原理
MyBatis分页插件的原理主要基于MyBatis的插件机制,通过拦截SQL查询操作,在查询语句中动态添加分页参数,从而实现分页查询功能。具体来说,分页插件的原理可以归纳为以下几点:1. 插件实现分页插件通常需要实现MyBatis的Interceptor接口,并重写intercept方法。在intercept方法中,插件可以拦截到MyBatis执行SQL查询的关键点,即StatementHandler的prepare方法。2. SQL拦截与重写当StatementHandler的prepare方法被触发时,分页插件会拦截到这个查询操作,并获取到当前的目标对象(它包含了要执行的SQL语句信息)。随后,插件会根据分页参数(如当前页码、每页显示的记录数等)来重写SQL语句,通常是在SQL语句的末尾添加LIMIT和 OFFSET子句(或其他数据库特定的分页语法),以实现分页功能。3. 分页参数传递分页参数可以通过多种方式传递给分页插件,例如通过Mapper接口方法的参数、全局配置、注解等。在分页插件内部,这些参数会被用来计算LIMIT和OFFSET的值,并嵌入到SQL语句中。4. 执行分页查询修改后的SQL语句会由MyBatis执行,数据库根据这个带有分页参数的SQL语句返回对应页的数据。分页插件还会处理查询结果,确保只返回当前页的数据给调用者。5. 配置与使用分页插件的使用通常需要在MyBatis的配置文件中进行配置,指定要使用的分页插件类,并设置相应的参数。然后,在Mapper接口中定义分页查询的方法,并在对应的XML文件中编写原始的SQL语句(无需包含分页参数)。最后,在业务代码中调用Mapper接口的分页查询方法,并传入分页参数即可实现分页查询。6. 注意事项分页插件的实现可能会依赖于具体的数据库方言,因为不同的数据库有不同的分页语法。在使用分页插件时,需要注意SQL语句的编写规范,特别是不要在SQL语句的末尾添加分号(;),因为分页插件会在SQL语句后添加分页参数,分号会导致SQL语句执行出错。分页插件可能会提供多种配置选项,如是否计算总记录数、是否进行内存分页等,需要根据实际需求进行选择。
88.Mybatis插件的应用场景
1. SQL 性能监控与分析在 SQL 执行前后记录时间,统计慢查询,生成性能报表。2.数据权限过滤自动为 SQL 添加数据权限条件,实现行级数据隔离。3.分页功能实现自动生成分页 SQL,简化分页逻辑。4.敏感数据加密 / 解密自动对敏感字段(如手机号、身份证号)进行加密存储和查询解密。5.SQL 注入防护对 SQL 参数进行安全校验,防止 SQL 注入攻击。6.多数据源路由根据业务需求动态切换数据源,实现读写分离或分库分表。7.自动填充字段自动为实体类的创建时间、更新时间等字段赋值。
89. Mybatis如何执行批量操作
使用foreach标签foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。item表示集合中每一个元素进行迭代时的别名,随便起的变量名;index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;open表示该语句以什么开始,常用“(”;separator表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;close表示以什么结束,常用“)”。在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Mapmap的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
90. Mybatis的功能架构是怎样的
我们把Mybatis的功能架构分为三层:API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
91. 为什么需要预编译
定义SQL 预编译指的是数据库驱动在发送 SQL 语句和参数给DBMS 之前对 SQL 语句进行编译,这样 DBMS 执行SQL 时,就不需要重新编译。为什么需要预编译JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译。预编译阶段可以优化 SQL 的执行。预编译之后的 SQL 多数情况下可以直接执行,DBMS 不需要再次编译,越复杂的SQL,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。同时预编译语句对象可以重复利用。把一个 SQL 预编译后产生的PreparedStatement 对象缓存下来,下次对于同一个SQL,可以直接使用这个缓存的 PreparedState 对象。Mybatis默认情况下,将对所有的 SQL 进行预编译。
92.模糊查询like语句怎么写
1. ’%${question}%’ 可能引起SQL注入,不推荐2. “%”#{question}“%” 注意:因为#{…}解析成sql语句时候,会在变量外侧自动加单引号’ ',所以这里 % 需要使用双引号" ",不能使用单引号 ’ ',不然会查不到任何结果。3. CONCAT(’%’,#{question},’%’) 使用CONCAT()函数,4. 使用bind标签
93.你对spring框架的理解
Spring 是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性特征:Spring的主要特征:1. 轻量级Spring框架是一个轻量级的容器,其核心容器非常小巧,只依赖于少量的类库。这使得 Spring在性能上比一些重量级的框架更加出色,同时也便于快速部署和启动。2. 控制反转(IoC)Spring的核心是一个领域对象容器,它将创建和管理对象的控制权交给了容器,将依赖关系的管理交由容器来完成。这种技术促进了低耦合,使得代码更加灵活、可维护和可测试。3. 依赖注入(DI)依赖注入是控制反转的一种具体实现方式。Spring通过配置或注解标注,容器能够根据依赖关系将所需的依赖注入到目标对象中,降低了代码的耦合度。4. 面向切面编程(AOP)Spring提供了面向切面编程的支持,允许开发者通过将横切关注点(如日志记录、事务管理等)与业务逻辑分离,进行模块化的开发和维护。这样可以提高代码的重用性和可维护性。5. 容器管理Spring通过容器来管理应用程序的组件,包括对象的创建、销毁以及依赖关系的注入等。容器还提供了一些常用的功能,如生命周期管理、事件处理等。6. 模块化设计Spring框架具有很高的模块化性,开发者可以根据需要选择使用哪些模块,而不需要引入整个框架。这种设计使得Spring更加灵活和易于扩展。7. MVC框架Spring提供了一个基于分层的Web框架,通过模型-视图-控制器(MVC)的方式来进行开发。该框架提供了很多便捷的特性,如请求映射、数据绑定、表单验证等,简化了Web应用的开发过程。8. 集成性Spring框架可以与其他常用的框架(如Hibernate、MyBatis、Struts等)进行无缝集成,使得开发人员可以更加方便地使用这些框架。Spring通过提供丰富的集成支持和中间件抽象层,简化了应用程序的开发和部署过程。9. 支持声明式编程Spring提供了一系列的注解,可以通过注解方式实现声明式的一些功能,如事务控制、缓存管理等,简化了代码的编写。10. 面向接口编程Spring框架鼓励开发者使用接口来定义应用程序的组件,这样可以降低组件之间的耦合性,提高代码的可维护性和可测试性。11. 支持事务管理Spring提供了简单、一致的事务管理接口,可以很方便地对数据库事务进行控制。支持声明式事务管理以及编程式事务管理,能够适应不同的业务需求。IOCAOP
94. 谈谈你对Ioc Aop 的理解
IOC(控制反转)定义:IOC是一种设计原则,旨在通过将对象的创建和依赖关系的管理交给外部容器来降低代码的耦合度。它实现了对象之间的解耦,提高了代码的可重用性和可维护性。核心思想:将控制权从应用程序代码转移到框架或容器,实现松耦合和更易于测试的代码。依赖注入(DI)是实现IOC的一种方式,通过注入依赖对象来实现控制反转。实现方式:依赖注入:通过IOC容器自动注入依赖对象,从而消除了手动创建和管理依赖关系的繁琐过程。配置管理:通过IOC容器将配置文件中的参数注入到应用程序中,实现了配置和代码的分离。单例管理:通过IOC容器管理单例对象的生命周期,避免了手动管理单例对象的复杂性。生命周期管理:通过IOC容器管理对象的生命周期,实现了对象的初始化、销毁等生命周期的自动化管理。应用场景:广泛应用于Spring框架等IOC容器中,通过XML配置、注解等方式将对象实例化并注入到需要的地方。实现了面向接口编程,提高了代码的可维护性和扩展性。AOP(面向切面编程)定义:AOP是一种编程范式,旨在将应用程序的业务逻辑和横切关注点(如日志、事务、安全等)分离开来,以提高代码的可维护性和可重用性。核心思想:将跨越多个对象的通用功能(横向抽取)抽象出来,以便在程序中重用。通过横向切割的方式,将关注点(Aspect)从业务逻辑代码中分离出来,并在运行时动态地将这些关注点织入到业务逻辑中。实现方式:动态代理:AOP底层通常使用动态代理技术来实现,包括JDK动态代理和CGLIB动态代理。注解和XML配置:在Spring框架中,AOP可以通过注解(如@Aspect、@Before、@After等)或XML配置来实现。应用场景:广泛应用于日志记录、性能监控、安全控制、事务管理、异常处理等方面。在Spring框架中,AOP可以很方便地实现这些功能,而不必对业务逻辑代码进行修改。IOC与AOP的关系互补性:IOC和AOP在Spring框架等Java EE平台中通常是相互补充的。IOC负责对象的创建和依赖关系的管理,而AOP则负责将横切关注点从业务逻辑中分离出来。共同目标:两者都旨在提高代码的可维护性、可重用性和可扩展性,通过降低代码之间的耦合度来实现这一目标。
95.springbean实例化和初始化过程
Bean 实例化流程
2.1 BeanDefinition 的作用在 Spring 容器启动时,配置文件或注解会被解析为BeanDefinition 对象,存储在 BeanDefinitionRegistry 中。BeanDefinition 包含了 Bean 的元信息,例如:类名(beanClass)作用域(scope,如 singleton、prototype)工厂方法(factoryMethodName)依赖关系(dependsOn)是否延迟初始化(lazyInit)这些信息为后续的实例化提供了基础。2.2 实例化的触发Spring 容器在启动时会根据配置决定是否立即实例化单例 Bean(preInstantiateSingletons 方法)。对于非单例 Bean,只有在首次请求时才会触发实例化。实例化的核心入口是 AbstractBeanFactory 的 doGetBean方法。2.3 实例化过程实例化是指通过 BeanDefinition 创建 Bean 对象的阶段,主要步骤如下:1. 解析 Bean 类:Spring 通过 BeanDefinition 获取目标类的Class 对象。如果是工厂方法创建的 Bean,则调用指定的工厂方法。2. 选择构造方法:Spring 使用 ConstructorResolver 解析构造方法。如果有多个构造方法,Spring 会根据依赖注入的需要(例如 @Autowired 注解)选择合适的构造方法。如果没有显式构造方法,Spring 使用默认的无参构造方法。3. 创建实例:Spring 通过反射调用Constructor.newInstance() 创建 Bean 实例。如果 Bean 是通过 FactoryBean 创建的,则调用FactoryBean.getObject() 方法获取实例。此时,Bean 是一个“裸”对象,仅完成了内存分配,还未进行属性填充或初始化。4. 处理循环依赖:对于单例 Bean,Spring 在实例化后会将其放入一个“早期对象”缓存(earlySingletonObjects),以解决循环依赖问题。如果检测到循环依赖,Spring 会抛出BeanCurrentlyInCreationException,除非启 用了 @Lazy 或其他机制。2.4 实例化的关键类AbstractAutowireCapableBeanFactory:负责Bean 的实例化逻辑。ConstructorResolver:解析构造方法并决定使用哪个构造方法。InstantiationStrategy:定义实例化策略,默认使用 CglibSubclassingInstantiationStrategy(支持 CGLIB 动态代理)。三、Bean 初始化流程实例化完成后,Spring 会对 Bean 进行初始化,包括属性填充、依赖注入和初始化方法的调用。初始化的核心入口是 AbstractAutowireCapableBeanFactory 的initializeBean 方法。3.1 属性填充(Populate Bean)属性填充是指为 Bean 注入依赖的过程,主要步骤如下:1. 解析依赖:Spring 根据 BeanDefinition 中的propertyValues 或注解(例如 @Autowired)确定需要注入的依赖。如果使用了 @Autowired,Spring 会通过AutowiredAnnotationBeanPostProcessor 解析注解。2. 依赖注入:Spring 支持构造器注入、Setter 注入和字段注入。对于构造器注入,依赖在实例化阶段已经注入。对于 Setter 注入,Spring 调用 Setter 方法注入依赖。对于字段注入,Spring 直接通过反射设置字段值。3. 处理 @Resource 和 @Inject:如果使用了 JSR-250 的 @Resource 或 JSR-330 的@Inject,Spring 会通过相应的 BeanPostProcessor 完成注入。4. 循环依赖检查:在属性填充阶段,Spring 会再次检查循环依赖。如果依赖的 Bean 尚未创建完成,Spring 会尝试从早期对象缓存中获取。3.2 调用 Aware 接口在属性填充后,Spring 会检查 Bean 是否实现了某些Aware 接口,并调用相应的方法。例如:BeanNameAware:调用 setBeanName,传入 Bean的名称。BeanFactoryAware:调用 setBeanFactory,传入当前的 BeanFactory。ApplicationContextAware:调用 setApplicationContext,传入ApplicationContext。这些接口允许 Bean 获取容器中的上下文信息。3.3 执行 BeanPostProcessor 的前置处理Spring 提供了 BeanPostProcessor 接口,允许开发者在初始化前后对 Bean 进行自定义处理。在初始化之前,Spring 会调用所有注册的 BeanPostProcessor 的 postProcessBeforeInitialization 方法。例如,ApplicationContextAwareProcessor 会在这一阶段处理 ApplicationContextAware 接口的逻辑。3.4 执行初始化方法Spring 支持多种方式指定初始化方法:1. @PostConstruct 注解:如果 Bean 的方法上标注了 @PostConstruct,Spring 会通过CommonAnnotationBeanPostProcessor 调用该方法。2. 实现 InitializingBean 接口:如果 Bean 实现了 InitializingBean 接口,Spring 会调用其 afterPropertiesSet 方法。3. 自定义 init-method:在 XML 配置或 @Bean 注解中可以指定 init method,Spring 会调用该方法。这三种方式的执行顺序为:@PostConstruct →afterPropertiesSet → init-method。3.5 执行 BeanPostProcessor 的后置处理在初始化方法执行完成后,Spring 会调用所有BeanPostProcessor 的 postProcessAfterInitialization 方法。这一阶段常用于代理对象的创建,例如:AnnotationAwareAspectJAutoProxyCreator:为Bean 创建 AOP 代理。 AsyncAnnotationBeanPostProcessor:处理@Async 注解。3.6 注册销毁回调如果 Bean 实现了 DisposableBean 接口或指定了destroy-method,Spring 会注册销毁回调,在容器关闭时调用。
96.spring框架中哪些地方使用了反射
在 Spring 框架中,反射机制被广泛用于以下几个方面:
1. 依赖注入:Spring 使用反射机制获取对象并进行属性注入,从而实现依赖注入。2. AOP:Spring AOP 使用 JDK 动态代理或者 CGLIB 字节码增强技术来实现 AOP 的切面逻辑,这其中就包含了对被代理对象方法的反射调用。3. MVC 框架:Spring MVC 框架使用反射来调用相应的控制器方法,从而实现请求的处理。4. 数据库访问框架:Spring 的 JDBC 框架使用反射机制来实现对数据库的访问。5. 容器管理:Spring 容器也使用了反射机制来管理对象的实例化和依赖注入。需要注意的是,虽然反射机制为开发者提供了极大的便利性,但是过度使用反射也可能导致性能问题,在使用时需要进行适量控制。
97. springBean是线程安全的吗?
Spring框架中的Bean默认是线程不安全的。以下是对Spring Bean线程安全性的详细分析:
一、Bean的作用域与线程安全性1. 单例作用域(Singleton)这是Spring的默认作用域,意味着在整个Spring IoC容器中仅存在一个该类型的Bean实例。如果这个单例Bean是无状态的(即不包含任何成员变量或只包含不可变的成员变量),那么它是线程安全的。如果单例Bean包含可变的状态信息,并且这种状态被多个线程共享,则需要采取额外措施来确保线程安全,比如使用同步机制或设计成不可变对象等方法来保护数据的一致性。2. 原型作用域(Prototype)每次请求时都会创建一个新的Bean实例。在这种情况下,每个线程都有自己的Bean副本, 因此通常不需要担心线程安全问题。但如果多个线程访问同一个Prototype Bean的共享资源(例如静态变量或其他全局状态),仍需注意线程安全。3. 其他作用域如Request、Session、Application等作用域,这些作用域通常用于Web应用程序上下文。对于Request、Session这样的作用域,每个HTTP请求/会话都拥有独立的Bean实例,从而降低了跨线程的数据竞争风险。Application作用域类似于Singleton,需要注意潜在的并发访问问题。二、Bean的状态与线程安全性无状态Bean:如果一个Bean没有成员变量,或者成员变量都是不可变的对象(如String、Integer等基本类型包装类),那么这样的Bean是线程安全的。有状态Bean:如果一个Bean包含可变的状态信息(如集合、自定义对象等),则需要特别注意线程安全问题。在这种情况下,可以使用同步机制(如synchronized关键字)、线程安全的数据结构(如ConcurrentHashMap)或其他并发控制技术来保证线程安全。三、实现线程安全的方法1. 改变作用域:将有状态Bean的作用域由“singleton”单例改为“prototype”多例。这样每次请求都会创建一个新的Bean实例,从而避免线程安全问题。2. 避免可变成员变量:尽量保持Bean为无状态。无状态Bean没有成员变量或只有不可变的成员变量,因此线程安全。3. 使用ThreadLocal:在类中定义ThreadLocal的成员变量,并将需要的可变成员变量保存在ThreadLocal中。ThreadLocal本身就具备线程隔离的特性,为每个线程提供了一个独立的变量副本,从而解决线程安全问题。4. 使用同步机制:对于无法避免的状态共享,可以使用同步机制(如synchronized关键字或ReentrantLock加锁修改操作)来保证线程安全。5. 使用线程安全的数据结构:如使用java.util.concurrent包中的类(ConcurrentHashMap、AtomicInteger等)来保证线程安全。综上所述,Spring Bean的线程安全性并不是由Spring框架直接提供的特性,而是依赖于开发者的正确实现与配置。开发者需要根据Bean的作用域、状态以及使用场景来选择合适的线程安全策略。
98. spring中bean的作用域有那些
singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
application | 一般用于web应用环境,该作用域仅适用于WebApplicationContext环境 |
99.spring中有哪些设计模式
单例模式: Spring 的 Bean 默认是单例模式,即一个Bean 对象只会被创建一次并在整个应用中共享。这种方式可以提高性能和资源利用率。
工厂模式: Spring 使用工厂模式来创建和管理 Bean对象,即通过 BeanFactory 或 ApplicationContext等容器来创建和管理 Bean 对象。这种方式可以将对象的创建和管理解耦,并且可以灵活地配置和管理对象。代理模式:Spring 使用代理模式来实现 AOP(面向切面编程),即通过代理对象来增强原始对象的功能。这种方式可以实现横切关注点的复用,并且可以在不修改原始对象的情况下实现功能增强。观察者模式:Spring 使用观察者模式来实现事件驱动编程,即通过事件监听机制来处理事件。这种方式可以实现松耦合,使得对象之间的交互更加灵活。模板方法模式:Spring 使用模板方法模式来实现JdbcTemplate、HibernateTemplate 等模板类,即将相同的操作封装在模板方法中,而将不同的操作交给子类来实现。这种方式可以减少重复代码,提高代码的复用性。适配器模式:Spring 使用适配器模式来实现不同接口之间的适配,如 Spring MVC 中的 HandlerAdapter接口,可以将不同类型的 Controller 适配为统一的处理器。策略模式:Spring 使用策略模式来实现不同的算法或行为,如 Spring Security 中的AuthenticationStrategy 接口,可以根据不同的认证策略来进行认证。装饰器模式:Spring 使用装饰器模式来动态地增加对象的功能,如 Spring MVC 中的 HandlerInterceptor接口,可以在处理器执行前后添加额外的逻辑。组合模式:Spring 使用组合模式来实现复杂的对象结构,如 Spring MVC 中的HandlerMapping 接口,可以将多个 HandlerMapping 组合成一个HandlerMapping 链。迭代器模式:Spring 使用迭代器模式来访问集合对象中的元素,如 Spring 的 BeanFactory 和ApplicationContext 等容器都提供了迭代器来访问其中的 Bean 对象。注册表模式:Spring 使用注册表模式来管理对象的注册和查找,如 Spring 的BeanDefinitionRegistry 接口可以用来注册和管理 Bean 对象的定义。委托模式:Spring 使用委托模式来实现不同对象之间的消息传递,如 Spring 的ApplicationEventMulticaster 接口可以将事件委托给不同的监听器进行处理。状态模式:Spring 使用状态模式来管理对象的状态,如 Spring 的TransactionSynchronizationManager类可以根据不同的事务状态来进行处理。解释器模式:Spring 使用解释器模式来解析和处理一些复杂的表达式和规则,如 Spring Security 中的表达式语言可以用来实现访问控制。桥接模式:Spring 使用桥接模式来将抽象和实现分离,如 Spring JDBC 中的 DataSource 接口可以与不同的数据库实现进行桥接。
100. 常见的java算法
二分查找法:在二分查找方法中,将集合重复地分成两半,并根据关键字是小于还是大于集合的中间元素来在集合的左半部分或右半部分中搜索关键元素。二分查找要求用于查找的内容逻辑上来说是需要有序的,查找的数量只能是一个不能是多个。冒泡排序算法:它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。插入排序算法:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应的位置并插入。快速排序算法:对冒泡算法的一种改进。是指通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序。整个排序过程可以递归进行,以此达到整个数据变成有序序列。希尔排序算法:希尔排序是插入排序的一种又称“缩小增量排序”,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法归并排序算法:归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。桶排序算法:桶排序也叫箱排序,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是比较排序,他不受到 O(n log n) 下限的影响。基数排序算法:基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为 所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。剪枝算法:在搜索算法中优化中,剪枝,就是通过某种判断,避免一些不必要的遍历过程,形象的说,就是剪去了搜索树中的某些“枝条”,故称剪枝。应用剪枝优化的核心问题是设计剪枝判断方法,即确定哪些枝条应当舍弃,哪些枝条应当保留的方法。回溯算法:回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。最短路径算法:从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径叫做最短路径。解决最短路的问题有以下算法,Dijkstra 算法,Bellman-Ford 算法,Floyd 算法和 SPFA算法等。
101.bean的生命周期
Spring Bean 的生命周期可简化为以下核心阶段:
- 实例化:Spring 容器创建 Bean 对象(调用构造方法)
- 属性注入:为 Bean 设置配置的属性值和依赖(@Autowired 等)
- 初始化:
- 执行初始化方法(如 @PostConstruct 标注的方法、XML 中 init-method 指定的方法)
- 若实现 InitializingBean 接口,调用 afterPropertiesSet ()
- 使用:Bean 处于可用状态,供应用程序调用
- 销毁:
- 执行销毁方法(如 @PreDestroy 标注的方法、XML 中 destroy-method 指定的方法)
- 若实现 DisposableBean 接口,调用 destroy ()
核心流程:实例化 → 注入属性 → 初始化 → 使用 → 销毁
102.AOP原理
定义:AOP是一种编程范式,旨在将应用程序的业务逻辑和横切关注点(如日志、事务、安全等)分离开来,以提高代码的可维护性和可重用性。核心思想:将跨越多个对象的通用功能(横向抽取)抽象出来,以便在程序中重用。通过横向切割的方式,将关注点(Aspect)从业务逻辑代码中分离出来,并在运行时动态地将这些关注点织入到业务逻辑中。实现方式:动态代理:AOP底层通常使用动态代理技术来实现,包括JDK动态代理和CGLIB动态代理。注解和XML配置:在Spring框架中,AOP可以通过注解(如@Aspect、@Before、@After等)或XML配置来实现。应用场景:广泛应用于日志记录、性能监控、安全控制、事务管理、异常处理等方面。在Spring框架中,AOP可以很方便地实现这些功能,而不必对业务逻辑代码进行修改。
103.什么情况下事务会失效
失效原因:aop失效 ,动态代理失效
前提: 声明式事务的实现,利用的是aop ,aop的原理是动态代理
分两种情况 :
实现事务管理的类 有接口 实现 它会走jdk动态代理
事务管理在没有接口实现的类里,出现以下情况会失效 (走的是cglib代理)
- 目标类是final类型的
- 增加事务管理的方法是final或static 类型的
104. @Autowired 和@Resource两个注解什么区别?
- 来源不同
@Autowired
是 Spring 框架自带的注解(org.springframework.beans.factory.annotation.Autowired
)@Resource
是 JDK 自带的注解(javax.annotation.Resource
),属于 Java EE 规范的一部分- 注入方式不同
@Autowired
默认按照类型(byType) 注入,当存在多个同类型的 Bean 时,需要配合@Qualifier
指定 Bean 的名称@Resource
默认按照名称(byName) 注入,如果找不到对应名称的 Bean,会再按照类型查找- 支持的参数不同
@Autowired
只有一个required
参数,用于指定依赖是否必须存在(默认true
,不存在会抛出异常)@Resource
有更多参数,如name
(指定 Bean 名称)、type
(指定 Bean 类型)等- 使用范围不同
@Autowired
可用于构造方法、字段、setter 方法和参数上@Resource
可用于字段和 setter 方法上,不能用于构造方法和参数
105.ThreadLocal
1. 介绍概念2. 实现原理3. 应用场景4. 内存泄露ThreadLocal(线程局部变量)是Java中的一个类,它提供了一种线程局部变量的机制,即每个使用该变量的线程都会有一个该变量的独立副本,该副本对其他线程不可见,确保了线程间的数据隔离。以下是关于ThreadLocal的详细解释:一、基本概念定义:ThreadLocal为每个使用该变量的线程都提供一个独立的变量副本,每个线程都可以访问自己内部的副本变量,而不会影响到其他线程的副本。用途1. 线程上下文数据存储:在多线程应用程序中安全地存储和访问与线程相关的数据。2. 线程上下文传递:在异步编程场景中,将数据从一个线程传递到另一个线程。3. 线程局部状态维护:每个线程维护自己的局部状态信息,避免线程间的干扰。4. 性能统计和追踪:存储每个线程的计时器或统计器,用于性能分析和瓶颈定位。5. 临时资源分配:在需要临时分配资源的场景中,避免资源的竞争和冲突。6. 日志记录和调试:存储当前线程的标识或调试信息,帮助排查多线程环境下的问题。7. 测试场景模拟:在测试场景中模拟特定的线程环境。二、实现原理ThreadLocalMap:ThreadLocal使用内部类ThreadLocalMap来管理每个线程的数据。每个ThreadLocal对象在ThreadLocalMap中都有一个唯一的键值对,键为ThreadLocal对象自身,值为线程特定的数据。存储和访问当调用ThreadLocal的set方法时,会先获取当前线程的ThreadLocalMap,然后ThreadLocal对象作为键,要保存的值作为值,存储到ThreadLocalMap中。当线程需要获取ThreadLocal对象的值时,调用ThreadLocal的get方法,通过当前线程的ThreadLocalMap获取对应的值。内存泄漏问题:ThreadLocal在保存时会将自己作为弱引用(WeakReference)的键存储在ThreadLocalMap中。如果ThreadLocal对象没有外部强引用,那么在垃圾回收时可能会被回收,但其对应的value仍然存在于ThreadLocalMap中,导致内存泄漏。因此,在使用完ThreadLocal后,应该调用remove方法清除数据使用场景1. 需要保存线程的上下文信息,例如 用户信息,通过token解析出来的用户id等 ;2. 需要对线程的局部变量进行隔离,避免线程安全问题; LocalDate LocalDateTime3. 需要在跨类跨方法使用同一个变量,同时又不希望使用全局变量的情况;4. 需要避免传递参数的繁琐,例如在 Spring 框架中使用的事务管理。需要注意的是,由于 ThreadLocal 存储数据的副本是存储在每个线程的 ThreadLocalMap 中的,因此需要及时清理 ThreadLocal 中存储的数据,以避免内存泄漏问题。通常可以在使用完毕后通过调用 ThreadLocal 的remove() 方法来清理数据,或者使用 Java 8 引入的新特性
106.@Component和@Bean的区别
作用对象不同:@Component 注解作用于类@Bean注解作用于方法。2. @Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用@ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。3. @Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过@Bean来实现
107.事务
从以下几方面说:1. 事务的概念2. 事务的特性acid (可以结合日志文件说明 redo_log undo_log)3. 事务的隔离级别 (4种) 隔离级别对应的问题4. 扩展: 在实际开发中如何使用事务?分为两种:1. 单体结构 使用spring 管理事务@Transactionl 原理用的aop 注意事务的5个属性2. 在分布式中使用分布式事务如seata 提到二段提交 三段提交定义事务(Transaction)是数据库管理系统执行过程中的一个逻辑单位,它由一个或多个SQL语句组成,这些语句作为一个整体一起向系统提交,要么全部执行,要么全部不执行,即事务具有原子性(Atomicity)。事务的引入是为了解决数据库并发操作可能带来的数据不一致性和完整性问题。事务的特性(ACID)参考事务ACID核心日志机制事务具有四个基本特性,通常简称为ACID:1. 原子性(Atomicity):事务是数据库中的最小工作单元,事务中的所有操作要么全部完成,要么全部不做,事务在执行过程中发生错误会被回滚 (Rollback)到事务开始前的状态,就像这个事务从未执行过一样。2. 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态。一致性与原子性是密切相关的。3. 隔离性(Isolation):数据库系统提供一定的隔离级别,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,或者说,事务处理过程中的数据变化只有在这个事务提交时才对其他事务可见。4. 持久性(Durability):一旦事务被提交,它对数据库的修改就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。隔离级别为了解决并发事务可能带来的问题(如脏读、不可重复读、幻读),SQL标准定义了四个隔离级别:1. READ UNCOMMITTED:最低级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读或幻读。2. READ COMMITTED:允许读取已经提交的数据,可以阻止脏读,但不可重复读和幻读仍有可能发生。3. REPEATABLE READ:MySQL InnoDB的默认事务隔离级别。确保在同一个事务中多次读取同样记录的结果是一致的,但幻读仍有可能发生。4. SERIALIZABLE:最高的隔离级别,通过强制事务串行执行,避免了脏读、不可重复读和幻读,但这也将大大降低数据库的并发性能。
108.Mybatis的认识
介绍 mybaits
MyBatis 是一款优秀的持久层框架,它对 JDBC 操作数据库的过程进行了封装,使得开发者只需关注SQL 本身,而无需处理如注册驱动、创建连接、关闭资源等繁杂的过程。
主要特点
1. SQL 与代码分离:MyBatis 采用 XML 或注解的方式将 SQL 语句与 Java 代码分离,这样便于对SQL 进行统一管理和优化。
2. 灵活的 SQL 编写:它支持编写动态 SQL,借助OGNL 表达式能够实现如 if、choose、
where 等动态元素,极大地提升了 SQL 编写的灵活性。
3. 强大的映射能力:可以将结果集灵活地映射为Java 对象,不仅支持基本类型的映射,还支持关联对象和集合的映射。
4. 轻量级框架:MyBatis 框架本身不依赖过多外部库,使用起来较为轻便,学习成本也相对较低。
介绍mybatis的优缺点 适合场景
优缺点
优点:
能够完全掌控 SQL,便于进行性能优化。可以与传统项目无缝集成,尤其适合对 SQL 优
化有较高要求的场景。支持多种数据库,具备良好的数据库移植性。
缺点:
需要手动编写 SQL,在复杂查询场景下开发效率可能会受到一定影响。
对复杂的对象关系映射支持不够友好,可能需要编写大量的映射配置。
适用场景
适用于对 SQL 性能要求较高,需要精细控制SQL 执行的项目。适合传统的 CRUD 应用以及数据访问层相对稳定的项目。可用于与其他框架(如 Spring、Spring Boot)集成,构建企业级应用。
109.Spring注解
注解的定义Annotation(注解)就是Java提供了一种为程序元素关联任何信息或任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。 注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解析注解 来使用这些数据),常见的作用有以下几种:生成文档。这是最常见的,也是Java 最早提供的注解。常用的有@see @param @return 等;在编译时进行格式检查。如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;定义BeanSpring 提供了以下多个注解,这些注解可以直接标注在Java 类上,将它们定义成 Spring Bean。
@Component 将指定类注入到IOC容器中,默认bean的名称是类名称首字母小写 @Repository 该注解用于将数据访问层(Dao 层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。@Service 该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring 中的 Bean,其功能与@Component 相同。@Controller 该注解通常作用在控制层(如 Struts2的 Action、SpringMVC 的Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与@Component 相同。依赖注入我们可以通过以下注解将定义好 Bean 装配到其它的 Bean中。
@Autowired 可以应用到 Bean 的属性变量、setter 方法、非 setter 方法及构造函数等,默认按照 Bean 的类型进行装配。@Autowired 注解默认按照 Bean 的类型进行装配,默认情况下它要求依赖对象必须存在,如果允许 null 值,可以设置它的 required 属性为 false。如果我们想使用按照名称(byName)来装配,可以结合 @Qualifier 注解一起使用 @Resource 作用与 Autowired 相同,区别在于@Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 的名称进行装配。 @Resource 中有两个重要属性:name 和 type。 Spring 将name 属性解析为 Bean 的实例名称,type 属性解析为 Bean 的实例类型。如果指定 name 属性,则按实例名称进行装配;如果指定 type 属性,则按 Bean类型进行装配;如果都不指定,则先按Bean 实例名称装配,如果不能匹配,则再按照 Bean 类型进行装配;如果都无法匹配,则抛出NoSuchBeanDefinitionException 异常。@Qualifier 与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改为按 Bean的实例名称装配,Bean 的实例名称由@Qualifier 注解的参数指定。其它注解一旦 被配置后,你就可以开始注解你的代码,表明Spring 应该自动连接值到属性,方法和构造函数。让我们来看看几个重要的注解,并且了解它们是如何工作的:
@Required 这个注解仅仅表示,受影响的bean属性必须在配置的时候被填充,通过在bean定义或通过自动装配一个明确的属性值。否则会提示:Requiredproperties missing: '属性名'@Configuration 应用于配置类,通常读取配置文件,将配置类注入到IOC容器中 @PropertySource 指定配置文件位置。提供了一种方便的、声明性的机制,用于向Spring的环境添加PropertySource。与@configuration类一起使用。@ComponentScan 自动扫描指定包下所有使用@Service,@Component,@Controller,@Repository 的类并注册@Bean 应用在方法上,将方法返回类型注入到IOC容器中,默认bean的名称是方法名称@Value 读取配置文件中的key对应的值,接收表达式${key}@Transactional 开启事务
110.谈谈你对IOC AOP的理解
IOC 的定义:IOC(Inversion of Control,控制反转)是一种设计思想,它将对象的创建、依赖关系的维护等控制权从应用程序代码转移到外部容器(如 Spring IOC 容器),实现了组件之间的解耦。
IOC原理
核心 : 配置文件+工厂类 +反射
IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下。
- 加载配置文件:Spring 容器加载 XML 配置文件或扫描带有注解的类(包含对象之间的依赖关系 ),获取 Bean 的定义信息。
- 创建 Bean 实例:IOC容器(工厂类),根据 Bean 的定义信息,通过反射机制创建 Bean 对象
- 注入依赖:容器将 Bean 依赖的其他对象注入到当前 Bean 中。
- 提供 Bean:容器将创建好的 Bean 提供给应用程序使用。
定义:AOP是一种编程范式,旨在将应用程序的业务逻辑和横切关注点(如日志、事务、安全等)分离开来,以提高代码的可维护性和可重用性。核心思想:将跨越多个对象的通用功能(横向抽取)抽象出来,以便在程序中重用。通过横向切割的方式,将关注点(Aspect)从业务逻辑代码中分离出来,并在运行时动态地将这些关注点织入到业务逻辑中。实现方式:动态代理:AOP底层通常使用动态代理技术来实现,包括JDK动态代理和CGLIB动态代理。注解和XML配置:在Spring框架中,AOP可以通过注解(如@Aspect、@Before、@After等)或XML配置来实现。应用场景:广泛应用于日志记录、性能监控、安全控制、事务管理、异常处理等方面。在Spring框架中,AOP可以很方便地实现这些功能,而不必对业务逻辑代码进行修改
111.Spring中单例bean的线程安全问题
这是Spring的默认作用域,意味着在整个Spring IoC容器中仅存在一个该类型的Bean实例。如果这个单例Bean是无状态的(即不包含任何成员变量或只包含不可变的成员变量),那么它是线程安全的。如果单例Bean包含可变的状态信息,并且这种状态被多个线程共享,则需要采取额外措施来确保线程安全,比如使用同步机制或设计成不可变对象等方法来保护数据的一致性。实现线程安全的方法1. 改变作用域:将有状态Bean的作用域由“singleton”单例改为“prototype”多例。这样每次请求都会创建一个新的Bean实例,从而避免线程安全问题。2. 避免可变成员变量:尽量保持Bean为无状态。无状态Bean没有成员变量或只有不可变的成员变量,因此线程安全。3. 使用ThreadLocal:在类中定义ThreadLocal的成员变量,并将需要的可变成员变量保存在ThreadLocal中。 ThreadLocal本身就具备线程隔离的特性,为每个线程提供了一个独立的变量副本,从而解决线程安全问题。4. 使用同步机制:对于无法避免的状态共享,可以使用同步机制(如synchronized关键字或ReentrantLock加锁修改操作)来保证线程安全。5. 使用线程安全的数据结构:如使用java.util.concurrent包中的类(如ConcurrentHashMap、AtomicInteger等)来保证线程安全。
112.Spring是如何解决循环依赖的
循环依赖的定义循环依赖指的是两个或多个Bean之间相互依赖,形成一个闭环。例如,Bean A依赖于Bean B,而Bean B又依赖于Bean A,这就构成了一个循环依赖。Spring解决循环依赖的机制Spring通过三级缓存机制来解决单例Bean的循环依赖问题。这三级缓存分别是:1. 一级缓存(singletonObjects)作用:用于存储已经创建完成的单例 Bean 对象,是一个ConcurrentHashMap。当 Bean 创建完成后,会将其放入一级缓存中,后续获取 Bean 时直接从这里获取。特点:当Spring容器创建一个Bean时,会首先检查这个缓存中是否已经存在该Bean的实例,如果存在则直接返回,避免重复创建。2. 二级缓存(earlySingletonObjects)作用:用于存储早期暴露的 Bean 对象引用,也是一个ConcurrentHashMap。在 Bean 的创建过程中,将其提前暴露到二级缓存,这样在其他 Bean 依赖它时,可以先获取到一个早期的引用,解决循环依赖。特点:当Spring容器检测到循环依赖时,会将部分初始化完成的Bean放入这个缓存中,以便其他Bean能够引用。3. 三级缓存(singletonFactories)作用:是一个ConcurrentHashMap,用于存储 Bean的工厂方法,通过它可以生成 Bean 对象。主要用于处理代理对象等特殊情况,在需要时通过工厂方法创建 Bean 的代理对象。特点:当Spring容器需要创建一个Bean时,如果一级和二级缓存中都不存在该Bean的实例,那么会从 三级缓存中获取对应的Bean工厂,通过它来创建Bean的实例,并放入二级缓存中。解决循环依赖的具体过程1. 创建 BeanA:Spring 容器开始创建 BeanA,首先在一级缓存中查找是否存在 BeanA 的实例,如果不存在,则开始创建 BeanA。2. 实例化 BeanA:通过构造函数实例化 BeanA,但此时BeanA 还未完成属性注入,处于 “半成品” 状态。3. 暴露 BeanA 的早期引用:将 BeanA 的早期引用放入二级缓存中,这样其他 Bean 就可以通过二级缓存获取到BeanA 的引用,即使它还没有完全初始化。4. 注入 BeanA 依赖的 BeanB:在创建 BeanA 的过程中,发现它依赖于 BeanB,于是 Spring 容器开始创建BeanB。5. 创建 BeanB:同样,在一级缓存中查找 BeanB 的实例,不存在则开始创建。在实例化 BeanB 后,发现BeanB 又依赖于 BeanA。6. 获取 BeanA 的早期引用:由于 BeanA 已经将其早期引用放入了二级缓存,所以 BeanB 可以从二级缓存中获取到 BeanA 的引用,而不是再次尝试创建 BeanA,从而避免了循环创建的问题。7. 完成 BeanB 的创建:将 BeanA 注入到 BeanB 中,完成 BeanB 的属性注入和初始化,然后将 BeanB 放入一级缓存。8. 完成 BeanA 的创建:BeanA 获取到已经创建好的BeanB,完成自身的属性注入和初始化,最终将 BeanA放入一级缓存。
113.什么是Spring MVC
Spring MVC(Model-View-Controller)是基于Java的Spring框架的一个模块,用于构建Web应用程序。它是一个轻量级的、基于MVC设计模式的Web框架,能够帮助开发者高效地开发出结构清晰、易于维护的Web应用。
核心概念
Model(模型):模型是应用程序的业务逻辑部分,它封装了数据和业务规则。在Spring MVC中,模型通常是一些Java对象,用于存储和处理数据。
View(视图):视图是用户界面部分,负责将数据展示给用户。在Spring MVC中,视图可以是JSP页面、HTML模板、JSON格式的数据等。
Controller(控制器):控制器是连接模型和视图的桥梁,它接收用户的请求,调用模型处理业务逻辑,然后选择一个视图来展示结果。
114.Spring MVC工作流程
简化流程:
1. 客户端发送 HTTP 请求至服务器
2. 请求被 DispatcherServlet 接收
3. DispatcherServlet 调用 HandlerMapping 获取处理器(Handler)
4. DispatcherServlet 调用 HandlerAdapter 执行处理器
5. 处理器执行业务逻辑,返回 ModelAndView 对象
6. DispatcherServlet 将 ModelAndView 传递给ViewResolver
7. ViewResolver 解析得到具体 View 对象
8. View 渲染模型数据,生成响应内容
9. DispatcherServlet 将响应返回给客户端
115.Spring MVC的优点
可以支持各种视图技术,而不仅仅局限于JSP;与Spring框架集成(如IoC容器、AOP等);清晰的角色分配:1. 前端控制器(dispatcherServlet):是整个流程的核心,负责接收所有请求并协调其他组件工作2. 请求到处理器映射(handlerMapping):根据请求 URL 找到对应的处理器(Controller)。3. 处理器适配器(HandlerAdapter):用于调用处理器(Controller)的方法。4. 视图解析器(ViewResolver):用于解析视图信息,找到对应的视图页面。支持各种请求资源的映射策略。面向接口编程
116.Get和Post的区别
1. 用途:
1. GET: 用于请求服务器上的资源,通常是获取数据。GET 请求是幂等的,多次请求返回的结果应该是相同的,而且不应该对服务器产生任何副作用。2. POST: 用于向服务器提交数据,通常是提交表单数据或上传文件等。POST 请求可能对服务器产生副作用,例如在数据库中创建新的资源。2. 数据传输方式:1. GET: 数据通过 URL 参数传递,附在 URL 后面。因为数据在 URL 中可见,所以不适合传递敏感信息,且有长度限制。2. POST: 数据通常包含在请求体中,而不是直接暴露在 URL 中。相对于 GET,POST 能够传递更多的数据,且不受长度限制。3. 缓存:1. GET: 可以被浏览器缓存,默认情况下可以被收藏夹保存,可以被历史记录保存。2. POST: 通常不会被缓存,不会被保存在浏览器历史记录中。4. 安全性:1. GET: 因为数据附在 URL 中,对用户可见,不适合传递敏感信息。由于请求的数据在 URL 中,也可能被浏览器保存,因此对用户的隐私不够安全。2. POST: 数据包含在请求体中,对用户不可见,适合传递敏感信息。由于不会暴露在 URL 中,相对于 GET 更安全。5. 幂等性:1. GET: 幂等,多次请求应该返回相同的结果。2. POST: 不幂等,多次相同的请求可能产生不同的结果,例如创建相同的资源多次可能导致多个资源的创建。总的来说,选择使用 GET 还是 POST 取决于你的业务需求。如果是获取数据,使用 GET;如果是提交数据,使用 POST。在实际应用中,经常会根据 RESTful 设计原则,合理地选择 GET 和 POST 的使用场景。
117. mybatis是否支持延迟加载?原理?
MyBatis仅支持association关联对象和collection关联 集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。延迟加载全局延迟加载 开启时,所有关联对象都会延迟加 载<setting name="lazyLoadingEnabled" value="true"/>局部延迟加载 可以覆盖全局在association或collection上有fetchType属性。fetchType设置:eager 立即加载lazy 延迟加载原理:使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,mybatis会检查目标对象是否被加载,
如果目标对象已经被加载,则直接返回目标对象的值。
如果目标对象尚未被加载,则触发数据库查询,加载目标对象,并将结果缓存起来,以便后续访问。
在拦截方法中,MyBatis会根据配置的SQL语句和参数,执行数据库查询,加载关联对象。加载完成后,将关联对象存储在代理对象中,以便后续访问。
118.MyISAM与InnoDB区别
存储结构与文件类型MyISAM使用非聚集索引,索引和数据文件是分离的。在磁盘上存储为三个文件:.frm(表定义)、.MYD(数据文件)、.MYI(索引文件)。支持静态表、动态表和压缩表三种存储格式。InnoDB使用聚集索引,索引和数据是关联在一起的,表数据文件本身就是按B+Tree组织的一个索引结构。有两种存储方式:共享表空间存储和多表空间存储。表结构和MyISAM一样,以表名开头,扩展名是.frm。如果使用共享表空间,所有表的数据文件和索引文件保存在一个表空间里;如果使用多表空间,每个表都有一个表空间文(.ibd)。2. 事务与外键(重点)MyISAM不支持事务处理,也不支持外键。InnoDB支持事务处理,是事务安全的存储引擎。支持外键,可以确保数据的参照完整性。3. 锁机制(重点)MyISAM只支持表级锁,无论是读操作还是写操作都会锁定 整个表。表级锁开销小,加锁快,但并发度低。InnoDB支持表级锁和行级锁,默认情况下使用行级锁。行级锁开销大,但并发度高,可以有效减少锁冲突。InnoDB的行锁是通过索引实现的,如果没有命中索引,则会退化为表锁。4. 性能与功能MyISAM强调性能,特别是读操作的性能,因为表级锁在读取时不会阻塞其他读操作。适用于读多写少的场景,如Web应用中的静态内容表。支持全文索引(FULLTEXT),在全文搜索方面性 能较好。InnoDB提供了更丰富的数据库功能,如事务、外键等。适用于需要高并发写入和复杂查询的场景。不直接支持全文索引,但可以通过插件(如Sphinx)实现。5. 计数与数据维护MyISAM保存了表的总行数,执行SELECT COUNT(*) FROM table;时直接返回该值,速度很快。InnoDB不保存表的总行数,执行SELECT COUNT(*) FROM table;时需要全表扫描,速度较慢。在处理大量数据时,InnoDB的自动增长列和索引管理更为复杂。6. 备份与恢复MyISAM数据以文件形式存储,备份和恢复时可以单独针对某个表进行操作。InnoDB备份和恢复相对复杂,需要拷贝数据文件、备份binlog或使用mysqldump等工具。在数据量较大时,备份和恢复操作可能较为耗时。7. 适用场景MyISAM适用于读操作远多于写操作的场景,如Web应用的静态内容表。适用于不需要事务和外键支持的应用。InnoDB适用于需要事务处理、外键支持以及高并发写入的应用。适用于对数据完整性和一致性要求较高的场景。MyISAM:使用非聚集索引,也就是索引和数据文件是分离的。不支持事务处理,也不支持外键。只支持表级锁,无论是读操作还是写操作都会锁定 整个表。强调性能,特别是读操作的性能,因为表级锁在读取时不会阻塞其他读操作。同时也保存了表的总行数,在查询表的总行数时直接返回结果,数据以文件形式存储,备份和恢复时可以单独针对某个表进行操作。适用于读操作远多于写操作的场景,如Web应用的静态内容表。适用于不需要事务和外键支持的应用。InnoDB:使用聚集索引,支持事务处理,是事务安全的存储引擎。支持外键,可以确保数据的参照完整性。支持表级锁和行级锁,默认情况下使用行级锁。不保存表的总行数,执行SELECT COUNT(*) FROM table;时需要全表扫描,速度较慢。备份和恢复相对复杂,需要拷贝数据文件、备份binlog或使用mysqldump等工具。适用于需要事务处理、外键支持以及高并发写入的应用。适用于对数据完整性和一致性要求较高的场景。
119.mybatis的一级和二级缓存
一级缓存session缓存
- 默认mybatis开启一级缓存
- 在同一会话中,同一查询多次调用,会利用一级缓存 (第一次查询时,查询数据库,将对象缓存,再查询时,直接取缓存不再查询数据库)
- session 是线程非安全的,操作完会关闭session ,一级缓存会失效
二级缓存
命名空间级缓存 ,同一命名空间内缓存可以使用 。
特点
二级缓存 默认是不开启的
开启二级缓存,在映射文件中添加
要缓存的对象所属类必须实现序列化接口
序列化接口Serializable
扩展:
- 什么是序列化? 将对象以二进制形式进行存储或传输 ,反序列化是将二进制数据转换为对象
- 实现序列化要用到ObjectInputStream 实现反序列化,使用ObjectOutputStream实现序列化
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
如果一张表中的数据频繁的增删改 ,不适合做缓存
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存的数量是有限制的,默认缓存1024个引用
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存不会定时进行刷新(也就是说,没有刷新间隔)
默认情况下,如果表中数据没有增删改,缓存会一 直有效
缓存会被视为读/写缓存
- 只读缓存 不允许 修改缓存对象
- 写缓存 ,允许修改 查询缓存时,返回的是对象的浅拷贝
属性
eviction 缓存清除策略
LRU
(Least Recently Used)– 最近最少使用:移除最长时间不被使用的对象。 默认策略FIFO
(first in first out 队列结构)– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。flushInterval 定时刷新缓存,可以传递一个整数代表时间,单位ms, 默认不刷新
size 缓存引用数目,默认是1024
readOnly 缓存是否是只读的 默认值是 false
默认实现
二级缓存,默认实现类: PerpetualCache,永久缓存,它的底层使用HashMap进行缓存
120.@RestController 和@Controller 有什么区别?
返回结果不同@Controller 返回逻辑视图@RestController 返回的是xml或json格式数据组合不同@RestController 是@Controller+@ResponseBody两个注解的复合注解。@RestController 注解,在 @Controller 基础上,增加了 @ResponseBody 注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回 JSON 数据格式。
121.Spring中的拦截器是什么?他的作用是什么
1. 拦截器的定义
在Spring框架中,拦截器是一种特殊的组件,它可以在请求被处理之前、处理过程中以及处理完成之后,插入一些自定义的逻辑。拦截器的作用类似于现实生活中的“安检”或“门禁系统”,它可以在请求到达目标(比如Controller方法)之前,先进行一些检查或处理。
拦截器通常需要实现
HandlerInterceptor
接口,或者继承HandlerInterceptorAdapter
类(在Spring 5及更高版本中,HandlerInterceptorAdapter
已经被标记为过时,推荐直接实现HandlerInterceptor
接口)。拦截器的核心功能是通过三个主要方法来实现的:
preHandle
:在请求处理之前被调用。如果返回true
,表示请求可以继续向下执行;如果返回false
,则中断请求。
postHandle
:在请求处理方法执行完成后,但在视图渲染之前被调用。
afterCompletion
:在整个请求处理完成后被调用,无论请求是否成功。2. 拦截器的作用
拦截器的作用非常广泛,主要用于实现一些通用的功能,而不需要在每个具体的业务逻辑中重复编写代码。以下是拦截器的主要作用:
(1)权限校验
拦截器可以在请求到达Controller之前,检查用户是否有权限访问该请求。如果没有权限,可以直接拒绝请求,而无需调用Controller方法。这样可以避免不必要的资源消耗,并提高系统的安全性。
(2)日志记录
拦截器可以记录请求的详细信息,例如请求的URL、请求参数、请求的开始时间和结束时间等。这些日志信息对于系统的监控、问题排查以及性能分析非常有帮助。
(3)性能监控
通过拦截器,可以统计每个请求的执行时间,从而帮助开发者发现系统的性能瓶颈。例如,可以记录请求的开始时间和结束时间,计算出请求的处理时间,并将这些数据记录到日志中或发送到监控系统中。
(4)统一数据处理
拦截器可以在请求处理之前对请求数据进行预处理,例如对请求参数进行校验、格式化等。在请求处理完成后,也可以对响应数据进行统一处理,例如对响应数据进行加密、压缩等。这样可以避免在每个Controller方法中重复编写数据处理逻辑。
(5)国际化支持
拦截器可以用于设置请求的国际化语言环境。例如,根据用户的浏览器语言设置或用户在系统中选择的语言,动态设置请求的语言环境,从而实现国际化功能。
(6)异常处理
拦截器可以在请求处理完成后捕获异常,并进行统一的处理。例如,可以记录异常信息到日志中,或者返回统一的错误响应。这样可以避免在每个Controller方法中重复编写异常处理逻辑。
(7)资源清理
拦截器可以在请求处理完成后,进行资源清理工作。例如,关闭数据库连接、释放文件句柄等。这有助于避免资源泄漏,提高系统的稳定性。
3. 拦截器的优势
解耦:拦截器将通用逻辑(如日志记录、权限校验等)从具体的业务逻辑中分离出来,使得代码更加清晰、易于维护。
复用:拦截器可以被多个请求共享,避免了在每个Controller方法中重复编写相同的代码。
灵活性:拦截器可以在请求处理的不同阶段插入逻辑,提供了非常灵活的扩展方式。
122.Spring拦截器的执行顺序
Springmvc的拦截器实现HandlerInterceptor接口后,会有三个抽象方法需要实现,分别为方法前执行preHandle,方法后postHandle,页面渲染后 afterCompletion。1. 当俩个拦截器都实现放行操作时,顺序为preHandle1,preHandle 2,postHandle 2,postHandle 1, afterCompletion 2,afterCompletion 12. 当第一个拦截器preHandle返回false,也就是对其进行拦截时,第二个拦截器是完全不执行的,第一个拦截器只执行preHandle部分。3. 当第一个拦截器preHandle返回true,第二个拦截器preHandle返回false,顺序为preHandle 1,preHandle 2 ,afterCompletion 1总结:preHandle 按拦截器定义顺序调用postHandler 按拦截器定义逆序调用afterCompletion 按拦截器定义逆序调用postHandler 在拦截器链内所有拦截器返成功调用afterCompletion 只有preHandle返回true才调用
123.SpringMVC常用注解
124.Restful风格和特点
125.CROS的两种请求
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求只要同时满足以下两大条件,就属于简单请求。这是为了兼容表单(form),因为历史上表单一直可以发出跨域请求。AJAX 的跨域设计就是,只要表单可以发,AJAX 就可以直接发。凡是不同时满足上面两个条件,就属于非简单请求。浏览器对这两种请求的处理,是不一样的。非简单请求非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
126.mybatis都有哪些Exector执行器?区别是什么?
Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor,BatchExecutor。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
127.Spring中有哪些设计模式
单例模式: Spring 的 Bean 默认是单例模式,即一个Bean 对象只会被创建一次并在整个应用中共享。这种方式可以提高性能和资源利用率。
工厂模式: Spring 使用工厂模式来创建和管理 Bean对象,即通过 BeanFactory 或 ApplicationContext等容器来创建和管理 Bean 对象。这种方式可以将对象的创建和管理解耦,并且可以灵活地配置和管理对象。代理模式:Spring 使用代理模式来实现 AOP(面向切面编程),即通过代理对象来增强原始对象的功能。这种方式可以实现横切关注点的复用,并且可以在不修改原始对象的情况下实现功能增强。观察者模式:Spring 使用观察者模式来实现事件驱动编程,即通过事件监听机制来处理事件。这种方式可以实现松耦合,使得对象之间的交互更加灵活。模板方法模式:Spring 使用模板方法模式来实现JdbcTemplate、HibernateTemplate 等模板类,即将相同的操作封装在模板方法中,而将不同的操作交给子类来实现。这种方式可以减少重复代码,提高代码的复用性。适配器模式:Spring 使用适配器模式来实现不同接口之间的适配,如 Spring MVC 中的 HandlerAdapter接口,可以将不同类型的 Controller 适配为统一的处理器。策略模式:Spring 使用策略模式来实现不同的算法或行为,如 Spring Security 中的AuthenticationStrategy 接口,可以根据不同的认证策略来进行认证。装饰器模式:Spring 使用装饰器模式来动态地增加对象的功能,如 Spring MVC 中的 HandlerInterceptor接口,可以在处理器执行前后添加额外的逻辑。组合模式:Spring 使用组合模式来实现复杂的对象结构,如 Spring MVC 中的HandlerMapping 接口,可以将多个 HandlerMapping 组合成一个HandlerMapping 链。迭代器模式:Spring 使用迭代器模式来访问集合对象中的元素,如 Spring 的 BeanFactory 和ApplicationContext 等容器都提供了迭代器来访问其中的 Bean 对象。注册表模式:Spring 使用注册表模式来管理对象的注册和查找,如 Spring 的BeanDefinitionRegistry 接口可以用来注册和管理 Bean 对象的定义。委托模式:Spring 使用委托模式来实现不同对象之间的消息传递,如 Spring 的ApplicationEventMulticaster 接口可以将事件委托给不同的监听器进行处理。状态模式:Spring 使用状态模式来管理对象的状态,如 Spring 的TransactionSynchronizationManager类可以根据不同的事务状态来进行处理。解释器模式:Spring 使用解释器模式来解析和处理一些复杂的表达式和规则,如 Spring Security 中的表达式语言可以用来实现访问控制。桥接模式:Spring 使用桥接模式来将抽象和实现分离,如 Spring JDBC 中的 DataSource 接口可以与不同的数据库实现进行桥接。
128.验证注解
129.谈谈你对IOC AOP的理解
IOC 的定义:IOC(Inversion of Control,控制反转)是一种设计思想,它将对象的创建、依赖关系的维护等控制权从应用程序代码转移到外部容器(如 Spring IOC 容器),实现了组件之间的解耦。
IOC原理
核心 : 配置文件+工厂类 +反射
IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下。
- 加载配置文件:Spring 容器加载 XML 配置文件或扫描带有注解的类(包含对象之间的依赖关系 ),获取 Bean 的定义信息。
- 创建 Bean 实例:IOC容器(工厂类),根据 Bean 的定义信息,通过反射机制创建 Bean 对象
- 注入依赖:容器将 Bean 依赖的其他对象注入到当前 Bean 中。
- 提供 Bean:容器将创建好的 Bean 提供给应用程序使用。
定义:AOP是一种编程范式,旨在将应用程序的业务逻辑和横切关注点(如日志、事务、安全等)分离开来,以提高代码的可维护性和可重用性。核心思想:将跨越多个对象的通用功能(横向抽取)抽象出来,以便在程序中重用。通过横向切割的方式,将关注点(Aspect)从业务逻辑代码中分离出来,并在运行时动态地将这些关注点织入到业务逻辑中。实现方式:动态代理:AOP底层通常使用动态代理技术来实现,包括JDK动态代理和CGLIB动态代理。注解和XML配置:在Spring框架中,AOP可以通过注解(如@Aspect、@Before、@After等)或XML配置来实现。应用场景:广泛应用于日志记录、性能监控、安全控制、事务管理、异常处理等方面。在Spring框架中,AOP可以很方便地实现这些功能,而不必对业务逻辑代码进行修改
130.什么是线程死锁
定义:当存在多个临界资源对象时, 如果多个线程之间拥有的锁标记不同, 此时任意线程都无法获取完整锁标记进入运行状态, 就会形成死锁现象. 通常是由多个临界资源时休眠不当导致
产生原因:
1.互斥执行: 多个线程间的运行及对某个锁标记的拥有一定互斥
2. 不可抢占: 其他线程无法抢占拥有时间片和锁标记的线程的资源
3. 请求保持: 线程会一直保持对未拥有的锁标记的获取
4. 循环等待: 多个线程间互相等待对方所持有的锁标记资源如何避免:
1.尽量避免使用多个锁,尽量使用一个锁或者使用更加高级的锁2. 确保同步代码块的执行时间尽可能短,这样可以减少线程等待时间,从而避免死锁的产生。3. 破坏不剥夺条件,在Java中,可以使用可重入锁ReentrantLock
的tryLock()
方法。如果线程无法获取到所有需要的锁,就释放已经获取的锁4. 避免嵌套锁,如果需要使用多个锁,请确保它们的获取顺序是一致的,这样可以避免死锁。
131. mysql中in和exists的区别
in关键字确定给定的值是否与子查询或列表中的值相匹配。in在查询的时候,首先查询子查询的表,然后将内表和外表做一个笛卡尔积,然后按照条件进行筛选。所以相对内表比较小的时候,in的速度较快。exists关键字指定一个子查询,检测行的存在。遍历循环外表,然后看外表中的记录有没有和内表的数据一样的。匹配上就将结果放入结果集中。in 与 exists 的区别mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。1. 如果查询的两个表大小相当,那么用in和exists差别不大。2. 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。3. not in 和not exists:如果查询语句使用了not in(索引失效),那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。
132.如何解决跨域问题
前端解决 通过设置代理
后端解决
设置响应头:
- Access-Control-Allow-Origin 允许来自哪些域名的请求访问 通常设置* 允许来自所有请求
- Access-Control-Allow-Credentials 是否允许携带cookie 跨域请求 通常设置为false
- Access-Control-Expose-Headers 允许携带哪些请求头 通常设置为*
- Access-Control-Allow-Methods 允许请求的方法类型: GET,POST,PUT,DELETE,OPTIONS
- Access-Control-Max-Age 指定本次预检请求的有效时间,单位s
实际开发:
@Configuration public class WebConfig implements WebMvcConfigurer { /** * 解决跨域问题 * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { /*CorsRegistration cr=registry.addMapping("/**"); cr=cr.allowedOrigins("*"); cr.maxAge(100);*/ registry.addMapping("/**") //拦截所有的请求 .allowedOrigins("*")//允许来自哪些域名的请求 允许来自所域名 .allowedHeaders("*")//允许所有的请求头 .allowedMethods("GET","POST","PUT","DELETE","OPTIONS") .allowCredentials(false)//不允许携带cookie .maxAge(3600);//预检请求后的有效期 } }
133.Spring MVC 拦截器VS过滤器
134.SpringBoot的核心注解
@SpringBootApplication
该注解是一个复合注解,包含以下三个注解:
- @SpringBootConfiguration 将当前类 注入为配置类 相当于@Configuration
- @EnableAutoConfiguration 核心中的核心 启用自动配置它会根据项目的依赖(如类路径中的 jar 包)自动配置 Spring 应用。
- @ComponentScan 默认扫描当前类所在包及其子包
135.SpringBoot的特点
- 为所有 Spring 开发提供从根本上更快且可广泛访问的入门体验
- 可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的jar包和war包
- 开箱即用,提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置
- 内嵌Tomcat或Jetty等Servlet容器
- 尽可能自动配置Spring容器
- 提供一系列大型项目通用的非功能特性,(例如嵌入式服务器、安全性、指标、健康检查和外部化配置)
- 绝对没有代码生成,不需要XML配置。
136.配置绑定的两种方式的区别
@ConfigurationProperties通过 Spring Boot 提供的 @ConfigurationProperties 注解,可以将全局配置文件中的配置数据绑定到 JavaBean中。@Value当我们只需要读取配置文件中的某一个配置时,可以通过 @Value 注解获取。@ConfigurationProperties 与 @Value 的区别
适用范围不同
@ConfigurationProperties
适用于批量绑定配置文件的属性到 JavaBean 中,通常与prefix
配合使用,一次性映射多个属性。@Value
适用于单个属性的注入,支持直接读取配置文件中的特定值或 SpEL 表达式。使用场景不同
@ConfigurationProperties
适合结构化配置(如数据库连接信息、第三方服务配置)。@Value
适合零散配置或需要动态计算的场景(如临时开关、简单运算结果)。依赖关系不同
@ConfigurationProperties
需配合@Component
或其衍生注解(如@Configuration
)将类纳入 Spring 容器管理。@Value
可直接用于任何 Spring 托管的 Bean 的成员变量或方法参数上。表达式支持不同
@Value
支持 SpEL 表达式(如#{100-3}
),可进行运算或调用方法。@ConfigurationProperties
仅支持静态配置映射,不支持动态表达式。复杂结构处理
@ConfigurationProperties
自动处理嵌套对象、集合类型(List/Map/数组),无需额外解析。@Value
需手动通过 SpEL 或拆分字符串处理集合(如#{'${list}'.split(',')}
)。
137.Mybatis的一级缓存和二级缓存
一级缓存session缓存
- 默认mybatis开启一级缓存
- 在同一会话中,同一查询多次调用,会利用一级缓存 (第一次查询时,查询数据库,将对象缓存,再查询时,直接取缓存不再查询数据库)
- session 是线程非安全的,操作完会关闭session ,一级缓存会失效
二级缓存
命名空间级缓存 ,同一命名空间内缓存可以使用 。
特点
二级缓存 默认是不开启的
开启二级缓存,在映射文件中添加
要缓存的对象所属类必须实现序列化接口
序列化接口Serializable
扩展:
- 什么是序列化? 将对象以二进制形式进行存储或传输 ,反序列化是将二进制数据转换为对象
- 实现序列化要用到ObjectInputStream 实现反序列化,使用ObjectOutputStream实现序列化
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
如果一张表中的数据频繁的增删改 ,不适合做缓存
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存的数量是有限制的,默认缓存1024个引用
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存不会定时进行刷新(也就是说,没有刷新间隔)
默认情况下,如果表中数据没有增删改,缓存会一 直有效
缓存会被视为读/写缓存
- 只读缓存 不允许 修改缓存对象
- 写缓存 ,允许修改 查询缓存时,返回的是对象的浅拷贝
属性
eviction 缓存清除策略
LRU
(Least Recently Used)– 最近最少使用:移除最长时间不被使用的对象。 默认策略FIFO
(first in first out 队列结构)– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。flushInterval 定时刷新缓存,可以传递一个整数代表时间,单位ms, 默认不刷新
size 缓存引用数目,默认是1024
readOnly 缓存是否是只读的 默认值是 false
默认实现
二级缓存,默认实现类: PerpetualCache,永久缓存,它的底层使用HashMap进行缓存
138.SpringBoot的配置原理
两个重要策略 开箱既用 约定优于配置
SPI
自动配置
Spring Boot 的自动配置是其核心特性之一,它通过约定大于配置的理念,极大简化了 Spring 应用的搭建和开发过程。其核心原理可以概括为:基于类路径扫描、条件判断和动态 Bean 注册,自动完成常用组件的配置。
自动配置的核心实现流程
- @EnableAutoConfiguration 注解触发
- Spring Boot 应用的入口类通常标注
@SpringBootApplication
,该注解包含@EnableAutoConfiguration
,是自动配置的开关。@EnableAutoConfiguration
通过@Import(AutoConfigurationImportSelector.class)
导入自动配置逻辑。- 扫描候选配置类
AutoConfigurationImportSelector
的核心作用是从类路径中扫描所有META-INF/spring.factories
文件(Spring Boot 2.7+ 改为META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
)。- 这些文件中定义了所有候选的自动配置类(如
DataSourceAutoConfiguration
、WebMvcAutoConfiguration
等)。- 条件筛选有效配置类
- 候选配置类需满足注解中定义的条件才能生效,常见条件注解:
@ConditionalOnClass
:类路径存在指定类时生效@ConditionalOnMissingClass
:类路径不存在指定类时生效@ConditionalOnBean
:容器中存在指定 Bean 时生效@ConditionalOnMissingBean
:容器中不存在指定 Bean 时生效@ConditionalOnProperty
:配置文件中存在指定属性时生效- 例如,
DataSourceAutoConfiguration
会在类路径存在DataSource
类且容器中没有自定义DataSource
Bean 时生效。- 动态注册 Bean 到容器
- 满足条件的配置类会被 Spring 容器解析,其内部定义的
@Bean
方法会被执行,将组件(如数据源、DispatcherServlet 等)自动注册到 Spring 容器中。- 配置类会读取应用配置(
application.properties
/yml
)中的属性,实现组件的个性化配置(如通过spring.datasource.url
配置数据库连接)。- 用户配置优先覆盖
- 自动配置遵循「用户配置优先」原则:如果用户手动定义了某个 Bean(如自定义
DataSource
),则自动配置中对应的 Bean 会被忽略(通过@ConditionalOnMissingBean
实现)。核心组件与设计思想
- Spring Factories 机制:通过 SPI(服务提供者接口)模式,让第三方组件(如 MyBatis、Redis 等)能主动注册自己的自动配置类。
- 条件注解(@Conditional):基于 Spring 的条件注解扩展,实现了灵活的配置生效规则。
- 配置绑定(@ConfigurationProperties):将配置文件中的属性与 Java 类绑定(如
DataSourceProperties
),为自动配置提供参数。总结
Spring Boot 自动配置的本质是:通过预定义的配置类和条件判断,在满足特定条件时自动向容器中注册所需组件,并允许用户通过配置文件或自定义 Bean 进行个性化调整。这一机制消除了传统 Spring 应用中大量的 XML 或 Java 配置代码,实现了「开箱即用」的开发体验。
139.拦截器原理
拦截器基于 AOP(面向切面编程)思想,通过拦截请求的不同阶段来执行额外逻辑,主要作用于 Handler(控制器方法) 的调用过程。拦截器的工作流程
请求进入DispatcherServlet
用户请求首先到达DispatcherServlet,由它负责协调整个请求处理流程。在调用Controller方法前,DispatcherServlet会检查配置的拦截器链。拦截器链执行
拦截器按配置顺序依次执行preHandle()
方法。若任一拦截器的preHandle()
返回false
,请求会直接终止,后续拦截器和Controller均不执行。目标Controller处理
所有拦截器的preHandle()
通过后,DispatcherServlet调用对应的Controller方法处理业务逻辑。后处理与视图渲染
Controller方法执行完毕后,拦截器链按逆序执行postHandle()
方法(仅当preHandle()
成功时),最后执行afterCompletion()
(无论preHandle()
是否成功)。
140.Mybatis如何执行批量操作
使用foreach标签foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。item表示集合中每一个元素进行迭代时的别名,随便起的变量名;index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;open表示该语句以什么开始,常用“(”;separator表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;close表示以什么结束,常用“)”。在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Mapmap的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
141.动态SQL原理
MyBatis 动态 SQL 的作用MyBatis 动态 SQL 是 MyBatis 框架提供的一种强大功能,其主要作用在于提高 SQL 语句的灵活性和重用性。通过动态 SQL,开发者可以根据不同的业务逻辑和运行时传入的参数,动态地生成或拼接 SQL 语句,从而避免了手动编写大量条件判断语句和拼接 SQL 的繁琐过程。这不仅减少了代码的重复度,还提高了代码的可维护性和可读性。动态 SQL 的执行原理MyBatis 动态 SQL 的执行原理主要依赖于 XML 映射文件中的动态 SQL 标签和 OGNL(Object-GraphNavigation Language)表达式。当 MyBatis 加载映射文件时,会解析其中的动态 SQL 标签,并将它们转换为一系列的逻辑表达式和条件判断结构。在 后,MyBatis会根据这些参数值动态地决定哪些 SQL 片段应该被执行或忽略。具体来说,MyBatis 使用 OGNL 表达式来解析和计算动态 SQL 中的条件表达式,并在预编译阶段将动态生成的 SQL 发送给数据库进行预编译,然后在执行阶段绑定参数值并最终执行 SQL。这样既保证了 SQL 的安全性和性能(由于预编译),又实现了 SQL 结构的动态化和灵活性。MyBatis 中的动态 SQL 标签MyBatis 提供了多种动态 SQL 标签,以便在 XML 映射文件中编写灵活的 SQL 语句。以下是一些主要的动态SQL 标签及其用途:1. if:用于在生成的 SQL 语句中添加条件判断。可以根据指定的条件决定是否包含某个 SQL 语句片段。2. choose(包含 when、otherwise):类似于 Java中的 switch 语句,根据条件选择执行不同的 SQL 语句片段。choose 标签可以包含多个 when 标签和一个可选的 otherwise 标签。3. foreach:用于遍历集合或数组,并根据指定的模板将集合元素或数组元素插入到 SQL 语句中。通常用于处理批量操作或 IN 子查询等场景。4. set:用于在生成的 SQL 语句中添加 SET 子句。它主要用于更新操作,可以根据条件来动态生成需要更新的列。5. where:用于在生成的 SQL 语句中添加 WHERE 子句。它可以自动处理条件语句的前缀,并在有条件语句存在时添加 WHERE 关键字。6. trim:通过修剪 SQL 语句的开头和结尾来动态生成SQL 片段。它可以用于去除不必要的 SQL 关键字或条件语句,并提供了一些属性来定义修剪规则。7. bind:用于将表达式的结果绑定到一个变量上。可以在 SQL 语句中使用这个变量,避免重复计算表达式。这些动态 SQL 标签在 MyBatis 中提供了灵活的查询和更新操作的能力,使得开发者可以根据不同的业务逻辑和参数动态地生成 SQL 语句,从而提高了开发的效率和代码的质量。
142.谈谈你对IOC AOP的理解
IOC 的定义:IOC(Inversion of Control,控制反转)是一种设计思想,它将对象的创建、依赖关系的维护等控制权从应用程序代码转移到外部容器(如 Spring IOC 容器),实现了组件之间的解耦。
IOC原理
核心 : 配置文件+工厂类 +反射
IoC 底层通过工厂模式、Java 的反射机制、XML 解析等技术,将代码的耦合度降低到最低限度,其主要步骤如下。
- 加载配置文件:Spring 容器加载 XML 配置文件或扫描带有注解的类(包含对象之间的依赖关系 ),获取 Bean 的定义信息。
- 创建 Bean 实例:IOC容器(工厂类),根据 Bean 的定义信息,通过反射机制创建 Bean 对象
- 注入依赖:容器将 Bean 依赖的其他对象注入到当前 Bean 中。
- 提供 Bean:容器将创建好的 Bean 提供给应用程序使用。
定义:AOP是一种编程范式,旨在将应用程序的业务逻辑和横切关注点(如日志、事务、安全等)分离开来,以提高代码的可维护性和可重用性。核心思想:将跨越多个对象的通用功能(横向抽取)抽象出来,以便在程序中重用。通过横向切割的方式,将关注点(Aspect)从业务逻辑代码中分离出来,并在运行时动态地将这些关注点织入到业务逻辑中。实现方式:动态代理:AOP底层通常使用动态代理技术来实现,包括JDK动态代理和CGLIB动态代理。注解和XML配置:在Spring框架中,AOP可以通过注解(如@Aspect、@Before、@After等)或XML配置来实现。应用场景:广泛应用于日志记录、性能监控、安全控制、事务管理、异常处理等方面。在Spring框架中,AOP可以很方便地实现这些功能,而不必对业务逻辑代码进行修改
143.@Transactional的5个属性
隔离级别 5个 相比于数据库多了个默认的,默认采用数据库本身的隔离级别
脏读 :一个事务读到了另一个事务的未提交的数据不可重复读 :一个事务读到了另一个事务已经提交的update 的数据导致多次查询结果不一致幻读 :一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致
DEFAULT
:使用数据库默认级别READ_UNCOMMITTED
:允许读取未提交数据(最低隔离)READ_COMMITTED
:只能读取已提交数据(Oracle默认)REPEATABLE_READ
:同一事务内多次读取结果一致(MySQL默认)SERIALIZABLE
:串行化执行(最高隔离)
传播行为 嵌套事务时如何处理事务
REQUIRED
(默认):当前有事务则加入,没有则新建REQUIRES_NEW
:始终新建事务,挂起现有事务SUPPORTS
:有事务则加入,没有则以非事务运行NOT_SUPPORTED
:非事务方式执行,挂起现有事务MANDATORY
:必须在事务中调用,否则抛出异常NEVER
:必须在非事务中调用,否则抛出异常NESTED
:嵌套事务执行
是否只读 查看的时候 设置为只读 可以提高效率
事务超时 单们是秒, 默认值为-1 永不超时
回滚规则
- 指定哪些异常回滚 rollbackFor={}
- 指定哪些异常不回滚 noRollbackFor={}
144.后端验证注解
145.spring框架中哪些地方使用了反射
在 Spring 框架中,反射机制被广泛用于以下几个方面:
1. 依赖注入:Spring 使用反射机制获取对象并进行属性注入,从而实现依赖注入。2. AOP:Spring AOP 使用 JDK 动态代理或者 CGLIB 字节码增强技术来实现 AOP 的切面逻辑,这其中就包含了对被代理对象方法的反射调用。3. MVC 框架:Spring MVC 框架使用反射来调用相应的控制器方法,从而实现请求的处理。4. 数据库访问框架:Spring 的 JDBC 框架使用反射机制来实现对数据库的访问。5. 容器管理:Spring 容器也使用了反射机制来管理对象的实例化和依赖注入。需要注意的是,虽然反射机制为开发者提供了极大的便利性,但是过度使用反射也可能导致性能问题,在使用时需要进行适量控制。
146.Spring中的单例bean的线程安全问题
这是Spring的默认作用域,意味着在整个Spring IoC容器中仅存在一个该类型的Bean实例。如果这个单例Bean是无状态的(即不包含任何成员变量或只包含不可变的成员变量),那么它是线程安全的。
如果单例Bean包含可变的状态信息,并且这种状态被多个线程共享,则需要采取额外措施来确保线程安全,比如使用同步机制或设计成不可变对象等方法来保护数据的一致性。实现线程安全的方法1. 改变作用域:将有状态Bean的作用域由“singleton”单例改为“prototype”多例。这样每次请求都会创建一个新的Bean实例,从而避免线程安全问题。2. 避免可变成员变量:尽量保持Bean为无状态。无状态Bean没有成员变量或只有不可变的成员变量,因此线程安全。3. 使用ThreadLocal:在类中定义ThreadLocal的成员变量,并将需要的可变成员变量保存在ThreadLocal中。 ThreadLocal本身就具备线程隔离的特性,为每个线程提供了一个独立的变量副本,从而解决线程安全问题。4. 使用同步机制:对于无法避免的状态共享,可以使用同步机制(如synchronized关键字或ReentrantLock加锁修改操作)来保证线程安全。5. 使用线程安全的数据结构:如使用java.util.concurrent包中的类(如ConcurrentHashMap、AtomicInteger等)来保证线程安全。
147.Mybatis是否支持延迟加载?原理 ?
MyBatis仅支持association关联对象和collection关联 集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。延迟加载全局延迟加载 开启时,所有关联对象都会延迟加 载<setting name="lazyLoadingEnabled" value="true"/>局部延迟加载 可以覆盖全局在association或collection上有fetchType属性。fetchType设置:eager 立即加载lazy 延迟加载原理:使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,mybatis会检查目标对象是否被加载,
如果目标对象已经被加载,则直接返回目标对象的值。
如果目标对象尚未被加载,则触发数据库查询,加载目标对象,并将结果缓存起来,以便后续访问。
在拦截方法中,MyBatis会根据配置的SQL语句和参数,执行数据库查询,加载关联对象。加载完成后,将关联对象存储在代理对象中,以便后续访问。
148.Mybatis插件应用场景
1. SQL 性能监控与分析在 SQL 执行前后记录时间,统计慢查询,生成性能报表。2.数据权限过滤自动为 SQL 添加数据权限条件,实现行级数据隔离。3.分页功能实现自动生成分页 SQL,简化分页逻辑。4.敏感数据加密 / 解密自动对敏感字段(如手机号、身份证号)进行加密存储和查询解密。5.SQL 注入防护对 SQL 参数进行安全校验,防止 SQL 注入攻击。6.多数据源路由根据业务需求动态切换数据源,实现读写分离或分库分表。7.自动填充字段自动为实体类的创建时间、更新时间等字段赋值。
149. Spring Bean的生命周期
Spring Bean 的生命周期可简化为以下核心阶段:
- 实例化:Spring 容器创建 Bean 对象(调用构造方法)
- 属性注入:为 Bean 设置配置的属性值和依赖(@Autowired 等)
- 初始化:
- 执行初始化方法(如 @PostConstruct 标注的方法、XML 中 init-method 指定的方法)
- 若实现 InitializingBean 接口,调用 afterPropertiesSet ()
- 使用:Bean 处于可用状态,供应用程序调用
- 销毁:
- 执行销毁方法(如 @PreDestroy 标注的方法、XML 中 destroy-method 指定的方法)
- 若实现 DisposableBean 接口,调用 destroy ()
核心流程:实例化 → 注入属性 → 初始化 → 使用 → 销毁
150.Spring MVC的工作流程
简化流程:
1. 客户端发送 HTTP 请求至服务器
2. 请求被 DispatcherServlet 接收
3. DispatcherServlet 调用 HandlerMapping 获取处理器(Handler)
4. DispatcherServlet 调用 HandlerAdapter 执行处理器
5. 处理器执行业务逻辑,返回 ModelAndView 对象
6. DispatcherServlet 将 ModelAndView 传递给ViewResolver
7. ViewResolver 解析得到具体 View 对象
8. View 渲染模型数据,生成响应内容
9. DispatcherServlet 将响应返回给客户端
151.Spring Boot的核心注解
@SpringBootApplication
该注解是一个复合注解,包含以下三个注解:
- @SpringBootConfiguration 将当前类 注入为配置类 相当于@Configuration
- @EnableAutoConfiguration 核心中的核心 启用自动配置它会根据项目的依赖(如类路径中的 jar 包)自动配置 Spring 应用。
- @ComponentScan 默认扫描当前类所在包及其子包
152.线程池的原理
线程池基本原理:
1、任务提交到线程池以后,如果工作线程数小于 coreSize,直接交给核心线程执行任务2、核心线程数都满了,任务放到队列中3、队列满了,创建核心线程数以外的线程执行任务4、达到最大线程数,且队列仍然是满的状态,使用拒绝策略拒绝策略
- ThreadPoolExecutor.AbortPolicy(系统默认): 丢弃任务并抛出RejectedExecutionException 异常,让你感知 到任务被拒绝了,我们可以根据业务逻辑选择重试或者放弃提交等策略
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常,相对而言存在一定 的风险,因为我们提交的时候根本不知道这个 任务会被丢弃,可能造成数据丢失。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程),通常是存活时间最长的任务,它也存在一定的数据丢失风险
- ThreadPoolExecutor.CallerRunsPolicy:既不抛弃任务也不抛出异常,而是将某些任务回退到调用者,让调用者去执行它。
153.Hash碰撞是什么?如何解决?
hash碰撞指的是,两个不同的值(比如张三、李四的 id)经过hash计算后,得到的hash值相同,后来的李四 要放到原来的张三的位置,但是数组的位置已经被张三占了,导致冲突。开放寻址法开放定址法通过探测哈希表中的空闲位置来解决哈希碰撞。当发生碰撞时,哈希表会寻找下一个空闲的槽位来存储元素。常见的探测策略有:线性探测:如果当前位置发生碰撞,检查下一个位置,直到找到空位。二次探测:采用平方的方式逐步探测空闲位置。双重哈希:使用第二个哈希函数来计算探测步长。拉链法链地址法是通过为每个哈希表的槽位创建一个链表来存 储所有发生碰撞的元素。当发生碰撞时,新的元素会被加入到该位置的链表中。再哈希法当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。建立公共溢出区将哈希表分为基本表和溢出表。凡是与基本表发生冲突 的元素,一律填入溢出表。查找时,先在基本表中查 找,若找不到再到溢出表中查找。这种方法实现相对简 单,但可能增加查找的时间开销。
154.Mybatis分页有哪几种方式
MyBatis实现分页的方式主要有以下几种:1. 物理分页原生SQL分页:直接在SQL语句中使用数据库提供的分页语法,如MySQL的LIMIT、Oracle的ROWNUM或ROW_NUMBER()、SQL Server的OFFSET FETCH等。这种方式依赖于数据库的分页功能,性 能较好,但SQL语句的编写需要针对具体的数据库进行调整。MyBatis插件(Interceptor):通过实现MyBatis的Interceptor接口,可以在SQL执行前后进行拦截处理,自动地在查询语句后添加分页参数。这种方式的好处是可以在不修改原有SQL语句的情况下实现分页,提高了代码的复用性和可维护性。PageHelper是MyBatis分页插件的一个流行选择,它支持多种数据库,并且配置和使用起来相对简单。2. 内存分页RowBounds(不推荐): 这种方式是在查询出所有结果后,在Java内存中通过程序逻辑进行分页处理。这种方法虽然实现简单,但在处理大量数据时效率极低,因为它需要将所有数据加载到内存中,消耗大量内存资源,并且分页操作(如跳页)时性能更差。因此,在实际开发中,几乎不会使用内存分页。3. 第三方分页插件或库除了PageHelper之外,还有其他一些第三方分页插件或库,MyBatis-Plus的分页功能。MyBatis Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。它内置了分页插件,可以非常方便地实现分页功能,并且支持多种数据库。4. 自定义分页查询在一些特殊场景下,可能需要根据业务逻辑实现自定义的分页查询。这通常涉及到在Mapper接口中定义分页查询的方法,并在对应的XML文件中编写相应的SQL语句。在SQL语句中,可以使用数据库的分页语法或者结合业务逻辑进行分页处理。这种方式需要开发者对数据库和MyBatis有较深的理解。在实际开发中,推荐使用物理分页的方式,特别是通过MyBatis插件(如PageHelper)来实现分页功能,因为它既高效又方便。如果项目已经集成了MyBatis-Plus,那么直接使用MyBatis-Plus的分页功能也是一个不错的选择。
155.Spring中的拦截器是什么?他的作用是什么
1. 拦截器的定义
在Spring框架中,拦截器是一种特殊的组件,它可以在请求被处理之前、处理过程中以及处理完成之后,插入一些自定义的逻辑。拦截器的作用类似于现实生活中的“安检”或“门禁系统”,它可以在请求到达目标(比如Controller方法)之前,先进行一些检查或处理。
拦截器通常需要实现
HandlerInterceptor
接口,或者继承HandlerInterceptorAdapter
类(在Spring 5及更高版本中,HandlerInterceptorAdapter
已经被标记为过时,推荐直接实现HandlerInterceptor
接口)。拦截器的核心功能是通过三个主要方法来实现的:
preHandle
:在请求处理之前被调用。如果返回true
,表示请求可以继续向下执行;如果返回false
,则中断请求。
postHandle
:在请求处理方法执行完成后,但在视图渲染之前被调用。
afterCompletion
:在整个请求处理完成后被调用,无论请求是否成功。2. 拦截器的作用
拦截器的作用非常广泛,主要用于实现一些通用的功能,而不需要在每个具体的业务逻辑中重复编写代码。以下是拦截器的主要作用:
(1)权限校验
拦截器可以在请求到达Controller之前,检查用户是否有权限访问该请求。如果没有权限,可以直接拒绝请求,而无需调用Controller方法。这样可以避免不必要的资源消耗,并提高系统的安全性。
(2)日志记录
拦截器可以记录请求的详细信息,例如请求的URL、请求参数、请求的开始时间和结束时间等。这些日志信息对于系统的监控、问题排查以及性能分析非常有帮助。
(3)性能监控
通过拦截器,可以统计每个请求的执行时间,从而帮助开发者发现系统的性能瓶颈。例如,可以记录请求的开始时间和结束时间,计算出请求的处理时间,并将这些数据记录到日志中或发送到监控系统中。
(4)统一数据处理
拦截器可以在请求处理之前对请求数据进行预处理,例如对请求参数进行校验、格式化等。在请求处理完成后,也可以对响应数据进行统一处理,例如对响应数据进行加密、压缩等。这样可以避免在每个Controller方法中重复编写数据处理逻辑。
(5)国际化支持
拦截器可以用于设置请求的国际化语言环境。例如,根据用户的浏览器语言设置或用户在系统中选择的语言,动态设置请求的语言环境,从而实现国际化功能。
(6)异常处理
拦截器可以在请求处理完成后捕获异常,并进行统一的处理。例如,可以记录异常信息到日志中,或者返回统一的错误响应。这样可以避免在每个Controller方法中重复编写异常处理逻辑。
(7)资源清理
拦截器可以在请求处理完成后,进行资源清理工作。例如,关闭数据库连接、释放文件句柄等。这有助于避免资源泄漏,提高系统的稳定性。
3. 拦截器的优势
解耦:拦截器将通用逻辑(如日志记录、权限校验等)从具体的业务逻辑中分离出来,使得代码更加清晰、易于维护。
复用:拦截器可以被多个请求共享,避免了在每个Controller方法中重复编写相同的代码。
灵活性:拦截器可以在请求处理的不同阶段插入逻辑,提供了非常灵活的扩展方式。
156.Mybatis动态sql
MyBatis 动态 SQL 的作用MyBatis 动态 SQL 是 MyBatis 框架提供的一种强大功能,其主要作用在于提高 SQL 语句的灵活性和重用性。通过动态 SQL,开发者可以根据不同的业务逻辑和运行时传入的参数,动态地生成或拼接 SQL 语句,从而避免了手动编写大量条件判断语句和拼接 SQL 的繁琐过程。这不仅减少了代码的重复度,还提高了代码的可维护性和可读性。动态 SQL 的执行原理MyBatis 动态 SQL 的执行原理主要依赖于 XML 映射文件中的动态 SQL 标签和 OGNL(Object-GraphNavigation Language)表达式。当 MyBatis 加载映射文件时,会解析其中的动态 SQL 标签,并将它们转换为一系列的逻辑表达式和条件判断结构。在 后,MyBatis会根据这些参数值动态地决定哪些 SQL 片段应该被执行或忽略。具体来说,MyBatis 使用 OGNL 表达式来解析和计算动态 SQL 中的条件表达式,并在预编译阶段将动态生成的 SQL 发送给数据库进行预编译,然后在执行阶段绑定参数值并最终执行 SQL。这样既保证了 SQL 的安全性和性能(由于预编译),又实现了 SQL 结构的动态化和灵活性。MyBatis 中的动态 SQL 标签MyBatis 提供了多种动态 SQL 标签,以便在 XML 映射文件中编写灵活的 SQL 语句。以下是一些主要的动态SQL 标签及其用途:1. if:用于在生成的 SQL 语句中添加条件判断。可以根据指定的条件决定是否包含某个 SQL 语句片段。2. choose(包含 when、otherwise):类似于 Java中的 switch 语句,根据条件选择执行不同的 SQL 语句片段。choose 标签可以包含多个 when 标签和一个可选的 otherwise 标签。3. foreach:用于遍历集合或数组,并根据指定的模板将集合元素或数组元素插入到 SQL 语句中。通常用于处理批量操作或 IN 子查询等场景。4. set:用于在生成的 SQL 语句中添加 SET 子句。它主要用于更新操作,可以根据条件来动态生成需要更新的列。5. where:用于在生成的 SQL 语句中添加 WHERE 子句。它可以自动处理条件语句的前缀,并在有条件语句存在时添加 WHERE 关键字。6. trim:通过修剪 SQL 语句的开头和结尾来动态生成SQL 片段。它可以用于去除不必要的 SQL 关键字或条件语句,并提供了一些属性来定义修剪规则。7. bind:用于将表达式的结果绑定到一个变量上。可以在 SQL 语句中使用这个变量,避免重复计算表达式。这些动态 SQL 标签在 MyBatis 中提供了灵活的查询和更新操作的能力,使得开发者可以根据不同的业务逻辑和参数动态地生成 SQL 语句,从而提高了开发的效率和代码的质量。
157.@Component和@Bean的区别
作用对象不同:@Component 注解作用于类@Bean注解作用于方法。2. @Component通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用@ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了Spring这是某个类的示例,当我需要用它的时候还给我。3. @Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过@Bean来实现
158.@Autowired 和@Resource两个注解什么区别?
- 来源不同
@Autowired
是 Spring 框架自带的注解(org.springframework.beans.factory.annotation.Autowired
)@Resource
是 JDK 自带的注解(javax.annotation.Resource
),属于 Java EE 规范的一部分- 注入方式不同
@Autowired
默认按照类型(byType) 注入,当存在多个同类型的 Bean 时,需要配合@Qualifier
指定 Bean 的名称@Resource
默认按照名称(byName) 注入,如果找不到对应名称的 Bean,会再按照类型查找- 支持的参数不同
@Autowired
只有一个required
参数,用于指定依赖是否必须存在(默认true
,不存在会抛出异常)@Resource
有更多参数,如name
(指定 Bean 名称)、type
(指定 Bean 类型)等- 使用范围不同
@Autowired
可用于构造方法、字段、setter 方法和参数上@Resource
可用于字段和 setter 方法上,不能用于构造方法和参数
159.Mybatis如何执行批量操作
使用foreach标签foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。item表示集合中每一个元素进行迭代时的别名,随便起的变量名;index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;open表示该语句以什么开始,常用“(”;separator表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;close表示以什么结束,常用“)”。在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Mapmap的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
160.BeanFactory和FactoryBean的区别
BeanFactory
BeanFactory 是 Spring 框架最基础的容器接口,负责管理 Bean 的生命周期、依赖注入等核心功能。它是所有 Spring 容器的顶层接口,如ApplicationContext
就是其子接口。BeanFactory 采用懒加载机制,只有在真正调用getBean()
时才会实例化 Bean。FactoryBean
FactoryBean 是一个特殊的 Bean,用于封装复杂对象的创建逻辑。它本身是一个工厂,可以生成其他 Bean 实例。开发者通过实现FactoryBean
接口自定义对象的创建过程,例如整合第三方库或管理特殊依赖关系。调用getBean()
时,实际返回的是FactoryBean.getObject()
方法生成的对象。核心差异
角色不同
- BeanFactory 是容器,负责管理和装配 Bean。
- FactoryBean 是特殊的 Bean,充当工厂角色,用于生成其他对象。
调用方式不同
- 从 BeanFactory 获取普通 Bean 直接返回目标实例。
- 获取 FactoryBean 时,默认返回其
getObject()
方法生成的对象;若需获取 FactoryBean 本身,需在 Bean 名称前加&
(如&myFactoryBean
)。使用场景
- BeanFactory 是框架基础,通常直接使用其子类(如
ApplicationContext
)。- FactoryBean 适用于需要自定义实例化逻辑的场景,如集成 MyBatis 的
SqlSessionFactoryBean
。
161.分页插件的原理
MyBatis分页插件的原理主要基于MyBatis的插件机制,通过拦截SQL查询操作,在查询语句中动态添加分页参数,从而实现分页查询功能。具体来说,分页插件的原理可以归纳为以下几点:1. 插件实现分页插件通常需要实现MyBatis的Interceptor接口,并重写intercept方法。在intercept方法中,插件可以拦截到MyBatis执行SQL查询的关键点,即StatementHandler的prepare方法。2. SQL拦截与重写当StatementHandler的prepare方法被触发时,分页插件会拦截到这个查询操作,并获取到当前的目标对象(它包含了要执行的SQL语句信息)。随后,插件会根据分页参数(如当前页码、每页显示的记录数等)来重写SQL语句,通常是在SQL语句的末尾添加LIMIT和 OFFSET子句(或其他数据库特定的分页语法),以实现分页功能。3. 分页参数传递分页参数可以通过多种方式传递给分页插件,例如通过Mapper接口方法的参数、全局配置、注解等。在分页插件内部,这些参数会被用来计算LIMIT和OFFSET的值,并嵌入到SQL语句中。4. 执行分页查询修改后的SQL语句会由MyBatis执行,数据库根据这个带有分页参数的SQL语句返回对应页的数据。分页插件还会处理查询结果,确保只返回当前页的数据给调用者。5. 配置与使用分页插件的使用通常需要在MyBatis的配置文件中进行配置,指定要使用的分页插件类,并设置相应的参数。然后,在Mapper接口中定义分页查询的方法,并在对应的XML文件中编写原始的SQL语句(无需包含分页参数)。最后,在业务代码中调用Mapper接口的分页查询方法,并传入分页参数即可实现分页查询。6. 注意事项分页插件的实现可能会依赖于具体的数据库方言,因为不同的数据库有不同的分页语法。在使用分页插件时,需要注意SQL语句的编写规范,特别是不要在SQL语句的末尾添加分号(;),因为分页插件会在SQL语句后添加分页参数,分号会导致SQL语句执行出错。分页插件可能会提供多种配置选项,如是否计算总记录数、是否进行内存分页等,需要根据实际需求进行选择。
162.spring事务的传播行为有哪些?
163.spring注解
164.#{}和${}的区别
1. #{} 是预编译处理,${}是字符串替换。2. Mybatis在执行 SQL 时,#{} 会被替换成 ? 占位符,这样可以有效防止 SQL 注入攻击。MyBatis 会依据参数类型自动进行类型转换。3. Mybatis在 SQL 预编译之前,${} 就会被直接替换为参数值,这种方式存在 SQL 注入风险。参数值不会经过类型转换,所以需要手动处理引号等问题。主要用于动态表名、列名或者 ORDER BY 子句等场景。
165.MyBatis实现一对一有几种方式?具体怎么操作的?
联合查询(嵌套结果)联合查询是几个表联合查询,只查询一次, 通过在 resultMap里面配置association节点配置一对一的类就可以完成;嵌套查询嵌套查询是先查一个表,根据这个表里面的结果的id做为参数(前提该id在另一个表中是外键关系 ),去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
166.MyBatis实现一对多有几种方式,怎么操作的?
区别
嵌套结果 是一条关联sql语句 ,所有的结果在ResultMap中映射 ,可以采用自动映射 ; 嵌套select语句,会出现1+N条sql语句 ,1指定的一对多中一端,一条sql语句 ,N 是额外发送的查询多的sql语句,sql语句简单
试想一下:如果没有高级结果映射,想返回一个Order对象,且里面包含订单明细,如何 实现?
嵌套select语句,在一对多结果映射,尽量只查询一个订单(因为有1+N的问题)
适用场景
- 如果每次根据id查询订单时都要订单明细,建议使用嵌套结果
- 如果有时只需要订单,不要明细,有时两者都要,可以考虑嵌套select 配置延迟加载
- 嵌套select不适合列表查询,会出现N+1问题,如果使用分页查询,还会出现分页不准。
167.Mysql事务
从以下几方面说:1. 事务的概念2. 事务的特性acid (可以结合日志文件说明 redo_log undo_log)3. 事务的隔离级别 (4种) 隔离级别对应的问题4. 扩展: 在实际开发中如何使用事务?分为两种:1. 单体结构 使用spring 管理事务@Transactionl 原理用的aop 注意事务的5个属性2. 在分布式中使用分布式事务如seata 提到二段提交 三段提交定义事务(Transaction)是数据库管理系统执行过程中的一个逻辑单位,它由一个或多个SQL语句组成,这些语句作为一个整体一起向系统提交,要么全部执行,要么全部不执行,即事务具有原子性(Atomicity)。事务的引入是为了解决数据库并发操作可能带来的数据不一致性和完整性问题。事务的特性(ACID)参考事务ACID核心日志机制事务具有四个基本特性,通常简称为ACID:1. 原子性(Atomicity):事务是数据库中的最小工作单元,事务中的所有操作要么全部完成,要么全部不做,事务在执行过程中发生错误会被回滚 (Rollback)到事务开始前的状态,就像这个事务从未执行过一样。2. 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态。一致性与原子性是密切相关的。3. 隔离性(Isolation):数据库系统提供一定的隔离级别,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,或者说,事务处理过程中的数据变化只有在这个事务提交时才对其他事务可见。4. 持久性(Durability):一旦事务被提交,它对数据库的修改就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。隔离级别为了解决并发事务可能带来的问题(如脏读、不可重复读、幻读),SQL标准定义了四个隔离级别:1. READ UNCOMMITTED:最低级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读或幻读。2. READ COMMITTED:允许读取已经提交的数据,可以阻止脏读,但不可重复读和幻读仍有可能发生。3. REPEATABLE READ:MySQL InnoDB的默认事务隔离级别。确保在同一个事务中多次读取同样记录的结果是一致的,但幻读仍有可能发生。4. SERIALIZABLE:最高的隔离级别,通过强制事务串行执行,避免了脏读、不可重复读和幻读,但这也将大大降低数据库的并发性能。
1.Mybatis的认识
介绍 mybaits
MyBatis 是一款优秀的持久层框架,它对 JDBC 操作数据库的过程进行了封装,使得开发者只需关注SQL 本身,而无需处理如注册驱动、创建连接、关闭资源等繁杂的过程。
主要特点
1. SQL 与代码分离:MyBatis 采用 XML 或注解的方式将 SQL 语句与 Java 代码分离,这样便于对SQL 进行统一管理和优化。
2. 灵活的 SQL 编写:它支持编写动态 SQL,借助OGNL 表达式能够实现如 if、choose、
where 等动态元素,极大地提升了 SQL 编写的灵活性。
3. 强大的映射能力:可以将结果集灵活地映射为Java 对象,不仅支持基本类型的映射,还支持关联对象和集合的映射。
4. 轻量级框架:MyBatis 框架本身不依赖过多外部库,使用起来较为轻便,学习成本也相对较低。
介绍mybatis的优缺点 适合场景
优缺点
优点:
能够完全掌控 SQL,便于进行性能优化。可以与传统项目无缝集成,尤其适合对 SQL 优
化有较高要求的场景。支持多种数据库,具备良好的数据库移植性。
缺点:
需要手动编写 SQL,在复杂查询场景下开发效率可能会受到一定影响。
对复杂的对象关系映射支持不够友好,可能需要编写大量的映射配置。
适用场景
适用于对 SQL 性能要求较高,需要精细控制SQL 执行的项目。适合传统的 CRUD 应用以及数据访问层相对稳定的项目。可用于与其他框架(如 Spring、Spring Boot)集成,构建企业级应用。
2.Mybatis分页有哪几种方式
MyBatis实现分页的方式主要有以下几种:1. 物理分页原生SQL分页:直接在SQL语句中使用数据库提供的分页语法,如MySQL的LIMIT、Oracle的ROWNUM或ROW_NUMBER()、SQL Server的OFFSET FETCH等。这种方式依赖于数据库的分页功能,性 能较好,但SQL语句的编写需要针对具体的数据库进行调整。MyBatis插件(Interceptor):通过实现MyBatis的Interceptor接口,可以在SQL执行前后进行拦截处理,自动地在查询语句后添加分页参数。这种方式的好处是可以在不修改原有SQL语句的情况下实现分页,提高了代码的复用性和可维护性。PageHelper是MyBatis分页插件的一个流行选择,它支持多种数据库,并且配置和使用起来相对简单。2. 内存分页RowBounds(不推荐): 这种方式是在查询出所有结果后,在Java内存中通过程序逻辑进行分页处理。这种方法虽然实现简单,但在处理大量数据时效率极低,因为它需要将所有数据加载到内存中,消耗大量内存资源,并且分页操作(如跳页)时性能更差。因此,在实际开发中,几乎不会使用内存分页。3. 第三方分页插件或库除了PageHelper之外,还有其他一些第三方分页插件或库,MyBatis-Plus的分页功能。MyBatis Plus是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。它内置了分页插件,可以非常方便地实现分页功能,并且支持多种数据库。4. 自定义分页查询在一些特殊场景下,可能需要根据业务逻辑实现自定义的分页查询。这通常涉及到在Mapper接口中定义分页查询的方法,并在对应的XML文件中编写相应的SQL语句。在SQL语句中,可以使用数据库的分页语法或者结合业务逻辑进行分页处理。这种方式需要开发者对数据库和MyBatis有较深的理解。在实际开发中,推荐使用物理分页的方式,特别是通过MyBatis插件(如PageHelper)来实现分页功能,因为它既高效又方便。如果项目已经集成了MyBatis-Plus,那么直接使用MyBatis-Plus的分页功能也是一个不错的选择。
3.Mybatis插件应用场景
1. SQL 性能监控与分析在 SQL 执行前后记录时间,统计慢查询,生成性能报表。2.数据权限过滤自动为 SQL 添加数据权限条件,实现行级数据隔离。3.分页功能实现自动生成分页 SQL,简化分页逻辑。4.敏感数据加密 / 解密自动对敏感字段(如手机号、身份证号)进行加密存储和查询解密。5.SQL 注入防护对 SQL 参数进行安全校验,防止 SQL 注入攻击。6.多数据源路由根据业务需求动态切换数据源,实现读写分离或分库分表。7.自动填充字段自动为实体类的创建时间、更新时间等字段赋值
4.Mybatis是否支持延迟加载?原理 ?
MyBatis仅支持association关联对象和collection关联 集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。延迟加载全局延迟加载 开启时,所有关联对象都会延迟加 载<setting name="lazyLoadingEnabled" value="true"/>局部延迟加载 可以覆盖全局在association或collection上有fetchType属性。fetchType设置:eager 立即加载lazy 延迟加载原理:使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,mybatis会检查目标对象是否被加载,
如果目标对象已经被加载,则直接返回目标对象的值。
如果目标对象尚未被加载,则触发数据库查询,加载目标对象,并将结果缓存起来,以便后续访问。
在拦截方法中,MyBatis会根据配置的SQL语句和参数,执行数据库查询,加载关联对象。加载完成后,将关联对象存储在代理对象中,以便后续访问。
5.Mybatis的一级缓存和二级缓存
一级缓存session缓存
- 默认mybatis开启一级缓存
- 在同一会话中,同一查询多次调用,会利用一级缓存 (第一次查询时,查询数据库,将对象缓存,再查询时,直接取缓存不再查询数据库)
- session 是线程非安全的,操作完会关闭session ,一级缓存会失效
二级缓存
命名空间级缓存 ,同一命名空间内缓存可以使用 。
特点
二级缓存 默认是不开启的
开启二级缓存,在映射文件中添加
要缓存的对象所属类必须实现序列化接口
序列化接口Serializable
扩展:
- 什么是序列化? 将对象以二进制形式进行存储或传输 ,反序列化是将二进制数据转换为对象
- 实现序列化要用到ObjectInputStream 实现反序列化,使用ObjectOutputStream实现序列化
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
如果一张表中的数据频繁的增删改 ,不适合做缓存
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
缓存的数量是有限制的,默认缓存1024个引用
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
缓存不会定时进行刷新(也就是说,没有刷新间隔)
默认情况下,如果表中数据没有增删改,缓存会一 直有效
缓存会被视为读/写缓存
- 只读缓存 不允许 修改缓存对象
- 写缓存 ,允许修改 查询缓存时,返回的是对象的浅拷贝
属性
eviction 缓存清除策略
LRU
(Least Recently Used)– 最近最少使用:移除最长时间不被使用的对象。 默认策略FIFO
(first in first out 队列结构)– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。flushInterval 定时刷新缓存,可以传递一个整数代表时间,单位ms, 默认不刷新
size 缓存引用数目,默认是1024
readOnly 缓存是否是只读的 默认值是 false
默认实现
二级缓存,默认实现类: PerpetualCache,永久缓存,它的底层使用HashMap进行缓存
6.Mybatis如何执行批量操作
使用foreach标签foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。item表示集合中每一个元素进行迭代时的别名,随便起的变量名;index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;open表示该语句以什么开始,常用“(”;separator表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;close表示以什么结束,常用“)”。在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Mapmap的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
7.Mybatis动态sql
MyBatis 动态 SQL 的作用MyBatis 动态 SQL 是 MyBatis 框架提供的一种强大功能,其主要作用在于提高 SQL 语句的灵活性和重用性。通过动态 SQL,开发者可以根据不同的业务逻辑和运行时传入的参数,动态地生成或拼接 SQL 语句,从而避免了手动编写大量条件判断语句和拼接 SQL 的繁琐过程。这不仅减少了代码的重复度,还提高了代码的可维护性和可读性。动态 SQL 的执行原理MyBatis 动态 SQL 的执行原理主要依赖于 XML 映射文件中的动态 SQL 标签和 OGNL(Object-GraphNavigation Language)表达式。当 MyBatis 加载映射文件时,会解析其中的动态 SQL 标签,并将它们转换为一系列的逻辑表达式和条件判断结构。在 后,MyBatis会根据这些参数值动态地决定哪些 SQL 片段应该被执行或忽略。具体来说,MyBatis 使用 OGNL 表达式来解析和计算动态 SQL 中的条件表达式,并在预编译阶段将动态生成的 SQL 发送给数据库进行预编译,然后在执行阶段绑定参数值并最终执行 SQL。这样既保证了 SQL 的安全性和性能(由于预编译),又实现了 SQL 结构的动态化和灵活性。MyBatis 中的动态 SQL 标签MyBatis 提供了多种动态 SQL 标签,以便在 XML 映射文件中编写灵活的 SQL 语句。以下是一些主要的动态SQL 标签及其用途:1. if:用于在生成的 SQL 语句中添加条件判断。可以根据指定的条件决定是否包含某个 SQL 语句片段。2. choose(包含 when、otherwise):类似于 Java中的 switch 语句,根据条件选择执行不同的 SQL 语句片段。choose 标签可以包含多个 when 标签和一个可选的 otherwise 标签。3. foreach:用于遍历集合或数组,并根据指定的模板将集合元素或数组元素插入到 SQL 语句中。通常用于处理批量操作或 IN 子查询等场景。4. set:用于在生成的 SQL 语句中添加 SET 子句。它主要用于更新操作,可以根据条件来动态生成需要更新的列。5. where:用于在生成的 SQL 语句中添加 WHERE 子句。它可以自动处理条件语句的前缀,并在有条件语句存在时添加 WHERE 关键字。6. trim:通过修剪 SQL 语句的开头和结尾来动态生成SQL 片段。它可以用于去除不必要的 SQL 关键字或条件语句,并提供了一些属性来定义修剪规则。7. bind:用于将表达式的结果绑定到一个变量上。可以在 SQL 语句中使用这个变量,避免重复计算表达式。这些动态 SQL 标签在 MyBatis 中提供了灵活的查询和更新操作的能力,使得开发者可以根据不同的业务逻辑和参数动态地生成 SQL 语句,从而提高了开发的效率和代码的质量。
8.动态sql标签
MyBatis 中的动态 SQL 标签MyBatis 提供了多种动态 SQL 标签,以便在 XML 映射文件中编写灵活的 SQL 语句。以下是一些主要的动态SQL 标签及其用途:1. if:用于在生成的 SQL 语句中添加条件判断。可以根据指定的条件决定是否包含某个 SQL 语句片段。2. choose(包含 when、otherwise):类似于 Java中的 switch 语句,根据条件选择执行不同的 SQL 语句片段。choose 标签可以包含多个 when 标签和一个可选的 otherwise 标签。3. foreach:用于遍历集合或数组,并根据指定的模板将集合元素或数组元素插入到 SQL 语句中。通常用于处理批量操作或 IN 子查询等场景。4. set:用于在生成的 SQL 语句中添加 SET 子句。它主要用于更新操作,可以根据条件来动态生成需要更新的列。5. where:用于在生成的 SQL 语句中添加 WHERE 子句。它可以自动处理条件语句的前缀,并在有条件语句存在时添加 WHERE 关键字。6. trim:通过修剪 SQL 语句的开头和结尾来动态生成SQL 片段。它可以用于去除不必要的 SQL 关键字或条件语句,并提供了一些属性来定义修剪规则。7. bind:用于将表达式的结果绑定到一个变量上。可以在 SQL 语句中使用这个变量,避免重复计算表达式。这些动态 SQL 标签在 MyBatis 中提供了灵活的查询和更新操作的能力,使得开发者可以根据不同的业务逻辑和参数动态地生成 SQL 语句,从而提高了开发的效率和代码的质量。
9.MyBatis实现一对一有几种方式?具体怎么操作的?
联合查询(嵌套结果)联合查询是几个表联合查询,只查询一次, 通过在 resultMap里面配置association节点配置一对一的类就可以完成;嵌套查询嵌套查询是先查一个表,根据这个表里面的结果的id做为参数(前提该id在另一个表中是外键关系 ),去再另外一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。
10.MyBatis实现一对多有几种方式,怎么操作的?
区别
嵌套结果 是一条关联sql语句 ,所有的结果在ResultMap中映射 ,可以采用自动映射 ; 嵌套select语句,会出现1+N条sql语句 ,1指定的一对多中一端,一条sql语句 ,N 是额外发送的查询多的sql语句,sql语句简单
试想一下:如果没有高级结果映射,想返回一个Order对象,且里面包含订单明细,如何 实现?
嵌套select语句,在一对多结果映射,尽量只查询一个订单(因为有1+N的问题)
适用场景
- 如果每次根据id查询订单时都要订单明细,建议使用嵌套结果
- 如果有时只需要订单,不要明细,有时两者都要,可以考虑嵌套select 配置延迟加载
- 嵌套select不适合列表查询,会出现N+1问题,如果使用分页查询,还会出现分页不准。
11.MyBatis编程步骤是什么样的?
MyBatis编程步骤是什么样的?1. 创建SqlSessionFactory2. 通过SqlSessionFactory创建SqlSession3. 通过sqlsession执行数据库操作4. 调用session.commit()提交事务5. 调用session.close()关闭会话
12.请说说MyBatis的工作原理
1. 读取 MyBatis 配置文件:mybatis-config.xml 为MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,例如数据库连接信息。2. 加载映射文件:映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。3. 构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。4. 创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。5. Executor 执行器:MyBatis 底层定义了一个Executor 接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。6. MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的id、参数等信息。7. 输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。8. 输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
13.Mybatis的功能架构是怎样的
我们把Mybatis的功能架构分为三层:API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
14.mybatis的框架架构设计是怎么样的
加载配置:配置来源于两个地方,一处是配置文件, 一处是Java代码的注解,将SQL的配置信息加载成为 一个个MappedStatement对象(包括了传入参数映 射配置、执行的SQL语句、结果映射配置),存储在 内存中。
SQL解析:当API接口层接收到调用请求时,会接收到 传入SQL的ID和传入对象(可以是Map、JavaBean或 者基本数据类型),Mybatis会根据SQL的ID找到对应 的MappedStatement,然后根据传入参数对象对 MappedStatement进行解析,解析后可以得到最终 要执行的SQL语句和参数。
SQL执行:将最终得到的SQL和参数拿到数据库进行 执行,得到操作数据库的结果。
结果映射:将操作数据库的结果按照映射的配置进行 转换,可以转换成HashMap、JavaBean或者基本数 据类型,并将最终结果返回
15.mybatis都有哪些Exector执行器?区别是什么?
Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor,BatchExecutor。
SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。
1.你对spring框架的理解
Spring 是一种轻量级开发框架,旨在提高开发人员的开发效率以及系统的可维护性特征:Spring的主要特征:1. 轻量级Spring框架是一个轻量级的容器,其核心容器非常小巧,只依赖于少量的类库。这使得 Spring在性能上比一些重量级的框架更加出色,同时也便于快速部署和启动。2. 控制反转(IoC)Spring的核心是一个领域对象容器,它将创建和管理对象的控制权交给了容器,将依赖关系的管理交由容器来完成。这种技术促进了低耦合,使得代码更加灵活、可维护和可测试。3. 依赖注入(DI)依赖注入是控制反转的一种具体实现方式。Spring通过配置或注解标注,容器能够根据依赖关系将所需的依赖注入到目标对象中,降低了代码的耦合度。4. 面向切面编程(AOP)Spring提供了面向切面编程的支持,允许开发者通过将横切关注点(如日志记录、事务管理等)与业务逻辑分离,进行模块化的开发和维护。这样可以提高代码的重用性和可维护性。5. 容器管理Spring通过容器来管理应用程序的组件,包括对象的创建、销毁以及依赖关系的注入等。容器还提供了一些常用的功能,如生命周期管理、事件处理等。6. 模块化设计Spring框架具有很高的模块化性,开发者可以根据需要选择使用哪些模块,而不需要引入整个框架。这种设计使得Spring更加灵活和易于扩展。7. MVC框架Spring提供了一个基于分层的Web框架,通过模型-视图-控制器(MVC)的方式来进行开发。该框架提供了很多便捷的特性,如请求映射、数据绑定、表单验证等,简化了Web应用的开发过程。8. 集成性Spring框架可以与其他常用的框架(如Hibernate、MyBatis、Struts等)进行无缝集成,使得开发人员可以更加方便地使用这些框架。Spring通过提供丰富的集成支持和中间件抽象层,简化了应用程序的开发和部署过程。9. 支持声明式编程Spring提供了一系列的注解,可以通过注解方式实现声明式的一些功能,如事务控制、缓存管理等,简化了代码的编写。10. 面向接口编程Spring框架鼓励开发者使用接口来定义应用程序的组件,这样可以降低组件之间的耦合性,提高代码的可维护性和可测试性。11. 支持事务管理Spring提供了简单、一致的事务管理接口,可以很方便地对数据库事务进行控制。支持声明式事务管理以及编程式事务管理,能够适应不同的业务需求。IOCAOP
2.@RestController 和@Controller 有什么区别?
返回结果不同@Controller 返回逻辑视图@RestController 返回的是xml或json格式数据组合不同@RestController 是@Controller+@ResponseBody两个注解的复合注解。@RestController 注解,在 @Controller 基础上,增加了 @ResponseBody 注解,更加适合目前前后端分离的架构下,提供 Restful API ,返回 JSON 数据格式。
3.@Autowired 和@Resource两个注解什么区别?
- 来源不同
@Autowired
是 Spring 框架自带的注解(org.springframework.beans.factory.annotation.Autowired
)@Resource
是 JDK 自带的注解(javax.annotation.Resource
),属于 Java EE 规范的一部分- 注入方式不同
@Autowired
默认按照类型(byType) 注入,当存在多个同类型的 Bean 时,需要配合@Qualifier
指定 Bean 的名称@Resource
默认按照名称(byName) 注入,如果找不到对应名称的 Bean,会再按照类型查找- 支持的参数不同
@Autowired
只有一个required
参数,用于指定依赖是否必须存在(默认true
,不存在会抛出异常)@Resource
有更多参数,如name
(指定 Bean 名称)、type
(指定 Bean 类型)等- 使用范围不同
@Autowired
可用于构造方法、字段、setter 方法和参数上@Resource
可用于字段和 setter 方法上,不能用于构造方法和参数
4.spring注解
5.spring框架中哪些地方使用了反射
在 Spring 框架中,反射机制被广泛用于以下几个方面:
1. 依赖注入:Spring 使用反射机制获取对象并进行属性注入,从而实现依赖注入。2. AOP:Spring AOP 使用 JDK 动态代理或者 CGLIB 字节码增强技术来实现 AOP 的切面逻辑,这其中就包含了对被代理对象方法的反射调用。3. MVC 框架:Spring MVC 框架使用反射来调用相应的控制器方法,从而实现请求的处理。4. 数据库访问框架:Spring 的 JDBC 框架使用反射机制来实现对数据库的访问。5. 容器管理:Spring 容器也使用了反射机制来管理对象的实例化和依赖注入。需要注意的是,虽然反射机制为开发者提供了极大的便利性,但是过度使用反射也可能导致性能问题,在使用时需要进行适量控制。
6.Spring Bean的生命周期
Spring Bean 的生命周期可简化为以下核心阶段:
- 实例化:Spring 容器创建 Bean 对象(调用构造方法)
- 属性注入:为 Bean 设置配置的属性值和依赖(@Autowired 等)
- 初始化:
- 执行初始化方法(如 @PostConstruct 标注的方法、XML 中 init-method 指定的方法)
- 若实现 InitializingBean 接口,调用 afterPropertiesSet ()
- 使用:Bean 处于可用状态,供应用程序调用
- 销毁:
- 执行销毁方法(如 @PreDestroy 标注的方法、XML 中 destroy-method 指定的方法)
- 若实现 DisposableBean 接口,调用 destroy ()
核心流程:实例化 → 注入属性 → 初始化 → 使用 → 销毁
7.@Transactional的5个属性
隔离级别 5个 相比于数据库多了个默认的,默认采用数据库本身的隔离级别
脏读 :一个事务读到了另一个事务的未提交的数据不可重复读 :一个事务读到了另一个事务已经提交的update 的数据导致多次查询结果不一致幻读 :一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致
DEFAULT
:使用数据库默认级别READ_UNCOMMITTED
:允许读取未提交数据(最低隔离)READ_COMMITTED
:只能读取已提交数据(Oracle默认)REPEATABLE_READ
:同一事务内多次读取结果一致(MySQL默认)SERIALIZABLE
:串行化执行(最高隔离)
传播行为 嵌套事务时如何处理事务
REQUIRED
(默认):当前有事务则加入,没有则新建REQUIRES_NEW
:始终新建事务,挂起现有事务SUPPORTS
:有事务则加入,没有则以非事务运行NOT_SUPPORTED
:非事务方式执行,挂起现有事务MANDATORY
:必须在事务中调用,否则抛出异常NEVER
:必须在非事务中调用,否则抛出异常NESTED
:嵌套事务执行
是否只读 查看的时候 设置为只读 可以提高效率
事务超时 单们是秒, 默认值为-1 永不超时
回滚规则
- 指定哪些异常回滚 rollbackFor={}
- 指定哪些异常不回滚 noRollbackFor={}
8.Spring中的单例bean的线程安全问题
这是Spring的默认作用域,意味着在整个Spring IoC容器中仅存在一个该类型的Bean实例。如果这个单例Bean是无状态的(即不包含任何成员变量或只包含不可变的成员变量),那么它是线程安全的。
如果单例Bean包含可变的状态信息,并且这种状态被多个线程共享,则需要采取额外措施来确保线程安全,比如使用同步机制或设计成不可变对象等方法来保护数据的一致性。实现线程安全的方法1. 改变作用域:将有状态Bean的作用域由“singleton”单例改为“prototype”多例。这样每次请求都会创建一个新的Bean实例,从而避免线程安全问题。2. 避免可变成员变量:尽量保持Bean为无状态。无状态Bean没有成员变量或只有不可变的成员变量,因此线程安全。3. 使用ThreadLocal:在类中定义ThreadLocal的成员变量,并将需要的可变成员变量保存在ThreadLocal中。 ThreadLocal本身就具备线程隔离的特性,为每个线程提供了一个独立的变量副本,从而解决线程安全问题。4. 使用同步机制:对于无法避免的状态共享,可以使用同步机制(如synchronized关键字或ReentrantLock加锁修改操作)来保证线程安全。5. 使用线程安全的数据结构:如使用java.util.concurrent包中的类(如ConcurrentHashMap、AtomicInteger等)来保证线程安全。
9.Spring是如何解决循环依赖的
循环依赖的定义循环依赖指的是两个或多个Bean之间相互依赖,形成一个闭环。例如,Bean A依赖于Bean B,而Bean B又依赖于Bean A,这就构成了一个循环依赖。Spring解决循环依赖的机制Spring通过三级缓存机制来解决单例Bean的循环依赖问题。这三级缓存分别是:1. 一级缓存(singletonObjects)作用:用于存储已经创建完成的单例 Bean 对象,是一个ConcurrentHashMap。当 Bean 创建完成后,会将其放入一级缓存中,后续获取 Bean 时直接从这里获取。特点:当Spring容器创建一个Bean时,会首先检查这个缓存中是否已经存在该Bean的实例,如果存在则直接返回,避免重复创建。2. 二级缓存(earlySingletonObjects)作用:用于存储早期暴露的 Bean 对象引用,也是一个ConcurrentHashMap。在 Bean 的创建过程中,将其提前暴露到二级缓存,这样在其他 Bean 依赖它时,可以先获取到一个早期的引用,解决循环依赖。特点:当Spring容器检测到循环依赖时,会将部分初始化完成的Bean放入这个缓存中,以便其他Bean能够引用。3. 三级缓存(singletonFactories)作用:是一个ConcurrentHashMap,用于存储 Bean的工厂方法,通过它可以生成 Bean 对象。主要用于处理代理对象等特殊情况,在需要时通过工厂方法创建 Bean 的代理对象。特点:当Spring容器需要创建一个Bean时,如果一级和二级缓存中都不存在该Bean的实例,那么会从 三级缓存中获取对应的Bean工厂,通过它来创建Bean的实例,并放入二级缓存中。解决循环依赖的具体过程1. 创建 BeanA:Spring 容器开始创建 BeanA,首先在一级缓存中查找是否存在 BeanA 的实例,如果不存在,则开始创建 BeanA。2. 实例化 BeanA:通过构造函数实例化 BeanA,但此时BeanA 还未完成属性注入,处于 “半成品” 状态。3. 暴露 BeanA 的早期引用:将 BeanA 的早期引用放入二级缓存中,这样其他 Bean 就可以通过二级缓存获取到BeanA 的引用,即使它还没有完全初始化。4. 注入 BeanA 依赖的 BeanB:在创建 BeanA 的过程中,发现它依赖于 BeanB,于是 Spring 容器开始创建BeanB。5. 创建 BeanB:同样,在一级缓存中查找 BeanB 的实例,不存在则开始创建。在实例化 BeanB 后,发现BeanB 又依赖于 BeanA。6. 获取 BeanA 的早期引用:由于 BeanA 已经将其早期引用放入了二级缓存,所以 BeanB 可以从二级缓存中获取到 BeanA 的引用,而不是再次尝试创建 BeanA,从而避免了循环创建的问题。7. 完成 BeanB 的创建:将 BeanA 注入到 BeanB 中,完成 BeanB 的属性注入和初始化,然后将 BeanB 放入一级缓存。8. 完成 BeanA 的创建:BeanA 获取到已经创建好的BeanB,完成自身的属性注入和初始化,最终将 BeanA放入一级缓存。
10.Spring MVC工作流程
简化流程:
1. 客户端发送 HTTP 请求至服务器
2. 请求被 DispatcherServlet 接收
3. DispatcherServlet 调用 HandlerMapping 获取处理器(Handler)
4. DispatcherServlet 调用 HandlerAdapter 执行处理器
5. 处理器执行业务逻辑,返回 ModelAndView 对象
6. DispatcherServlet 将 ModelAndView 传递给ViewResolver
7. ViewResolver 解析得到具体 View 对象
8. View 渲染模型数据,生成响应内容
9. DispatcherServlet 将响应返回给客户端
11.Spring中的拦截器是什么?他的作用是什么
1. 拦截器的定义
在Spring框架中,拦截器是一种特殊的组件,它可以在请求被处理之前、处理过程中以及处理完成之后,插入一些自定义的逻辑。拦截器的作用类似于现实生活中的“安检”或“门禁系统”,它可以在请求到达目标(比如Controller方法)之前,先进行一些检查或处理。
拦截器通常需要实现
HandlerInterceptor
接口,或者继承HandlerInterceptorAdapter
类(在Spring 5及更高版本中,HandlerInterceptorAdapter
已经被标记为过时,推荐直接实现HandlerInterceptor
接口)。拦截器的核心功能是通过三个主要方法来实现的:
preHandle
:在请求处理之前被调用。如果返回true
,表示请求可以继续向下执行;如果返回false
,则中断请求。
postHandle
:在请求处理方法执行完成后,但在视图渲染之前被调用。
afterCompletion
:在整个请求处理完成后被调用,无论请求是否成功。2. 拦截器的作用
拦截器的作用非常广泛,主要用于实现一些通用的功能,而不需要在每个具体的业务逻辑中重复编写代码。以下是拦截器的主要作用:
(1)权限校验
拦截器可以在请求到达Controller之前,检查用户是否有权限访问该请求。如果没有权限,可以直接拒绝请求,而无需调用Controller方法。这样可以避免不必要的资源消耗,并提高系统的安全性。
(2)日志记录
拦截器可以记录请求的详细信息,例如请求的URL、请求参数、请求的开始时间和结束时间等。这些日志信息对于系统的监控、问题排查以及性能分析非常有帮助。
(3)性能监控
通过拦截器,可以统计每个请求的执行时间,从而帮助开发者发现系统的性能瓶颈。例如,可以记录请求的开始时间和结束时间,计算出请求的处理时间,并将这些数据记录到日志中或发送到监控系统中。
(4)统一数据处理
拦截器可以在请求处理之前对请求数据进行预处理,例如对请求参数进行校验、格式化等。在请求处理完成后,也可以对响应数据进行统一处理,例如对响应数据进行加密、压缩等。这样可以避免在每个Controller方法中重复编写数据处理逻辑。
(5)国际化支持
拦截器可以用于设置请求的国际化语言环境。例如,根据用户的浏览器语言设置或用户在系统中选择的语言,动态设置请求的语言环境,从而实现国际化功能。
(6)异常处理
拦截器可以在请求处理完成后捕获异常,并进行统一的处理。例如,可以记录异常信息到日志中,或者返回统一的错误响应。这样可以避免在每个Controller方法中重复编写异常处理逻辑。
(7)资源清理
拦截器可以在请求处理完成后,进行资源清理工作。例如,关闭数据库连接、释放文件句柄等。这有助于避免资源泄漏,提高系统的稳定性。
3. 拦截器的优势
解耦:拦截器将通用逻辑(如日志记录、权限校验等)从具体的业务逻辑中分离出来,使得代码更加清晰、易于维护。
复用:拦截器可以被多个请求共享,避免了在每个Controller方法中重复编写相同的代码。
灵活性:拦截器可以在请求处理的不同阶段插入逻辑,提供了非常灵活的扩展方式。
12. Spring MVC 拦截器VS过滤器
13.Spring拦截器的执行顺序
Springmvc的拦截器实现HandlerInterceptor接口后,会有三个抽象方法需要实现,分别为方法前执行preHandle,方法后postHandle,页面渲染后 afterCompletion。1. 当俩个拦截器都实现放行操作时,顺序为preHandle1,preHandle 2,postHandle 2,postHandle 1, afterCompletion 2,afterCompletion 12. 当第一个拦截器preHandle返回false,也就是对其进行拦截时,第二个拦截器是完全不执行的,第一个拦截器只执行preHandle部分。3. 当第一个拦截器preHandle返回true,第二个拦截器preHandle返回false,顺序为preHandle 1,preHandle 2 ,afterCompletion 1总结:preHandle 按拦截器定义顺序调用postHandler 按拦截器定义逆序调用afterCompletion 按拦截器定义逆序调用postHandler 在拦截器链内所有拦截器返成功调用afterCompletion 只有preHandle返回true才调用
14.后端验证注解
15.SpringMVC常用注解
1.八大基本类型默认值和引用类型默认值
整型 : 0,浮点型 :0.0,布尔型:false,字符型:空,引用类型:null
两者区别:基本类型直接存储值在栈内存中,传递时复制值本身;引用类型存储的是对象在堆内存中的地址,传递时复制地址而非对象本身。
2.什么是死锁?如何产生?如何避免?
定义:当存在多个临界资源对象时, 如果多个线程之间拥有的锁标记不同, 此时任意线程都无法获取完整锁标记进入运行状态, 就会形成死锁现象. 通常是由多个临界资源时休眠不当导致
产生原因:
1.互斥执行: 多个线程间的运行及对某个锁标记的拥有一定互斥
2. 不可抢占: 其他线程无法抢占拥有时间片和锁标记的线程的资源
3. 请求保持: 线程会一直保持对未拥有的锁标记的获取
4. 循环等待: 多个线程间互相等待对方所持有的锁标记资源如何避免:
1.尽量避免使用多个锁,尽量使用一个锁或者使用更加高级的锁2. 确保同步代码块的执行时间尽可能短,这样可以减少线程等待时间,从而避免死锁的产生。3. 破坏不剥夺条件,在Java中,可以使用可重入锁ReentrantLock
的tryLock()
方法。如果线程无法获取到所有需要的锁,就释放已经获取的锁4. 避免嵌套锁,如果需要使用多个锁,请确保它们的获取顺序是一致的,这样可以避免死锁。
3.string, stringBuffer,stringBuilder的区别
string与可变长字符串:string值不可改,可以使用“”,可以使用+=,可以使用串池
stringBuffer与stringBuilder:
stringBuffer:JDK1.0 线程安全, 效率低
stringBuilder:JDK5.0 线程不安全, 效率高
4.Threadlocal
ThreadLocal为每个使用该变量的线程都提供一个独立的变量副本,每个线程都可以访问自己内部的副本变量,而不会影响到其他线程的副本。用途1. 线程上下文数据存储:在多线程应用程序中安全地存储和访问与线程相关的数据。2. 线程上下文传递:在异步编程场景中,将数据从一个线程传递到另一个线程。3. 线程局部状态维护:每个线程维护自己的局部状态信息,避免线程间的干扰。4. 性能统计和追踪:存储每个线程的计时器或统计器,用于性能分析和瓶颈定位。5. 临时资源分配:在需要临时分配资源的场景中,避免资源的竞争和冲突。6. 日志记录和调试:存储当前线程的标识或调试信息,帮助排查多线程环境下的问题。7. 测试场景模拟:在测试场景中模拟特定的线程环境。实现原理:ThreadLocalMap:ThreadLocal使用内部类ThreadLocalMap来管理每个线程的数据。每个ThreadLocal对象在ThreadLocalMap中都有一个唯一的键值对,键为ThreadLocal对象自身,值为线程特定的数据。存储和访问当调用ThreadLocal的set方法时,会先获取当前线程的ThreadLocalMap,然后将ThreadLocal对象作为键,要保存的值作为值,存储到ThreadLocalMap中。当线程需要获取ThreadLocal对象的值时,调用 ThreadLocal的get方法,通过当前线程的ThreadLocalMap获取对应的值。内存泄漏问题:ThreadLocal在保存时会将自己作为弱引用(WeakReference)的键存储在ThreadLocalMap中。如果ThreadLocal对象没有外部 强引用,那么在垃圾回收时可能会被回收,但其对应的value仍然存在于ThreadLocalMap中,导致内存泄漏。因此,在使用完ThreadLocal后,应该调用remove方法清除数据
5.接口和抽象类的区别
6.多线程的创建方式
- 创建一个类继承自
Thread
类,重写run()
方法,该方法中定义线程要执行的任务。然后创建该类的实例并调用start()
方法启动线程。- 创建一个类实现
Runnable
接口,实现run()
方法,将该类的实例作为参数传递给Thread
类的构造函数,然后调用start()
方法启动线程。- 创建一个类实现
Callable
接口,实现call()
方法,该方法可以有返回值。将该类的实例作为参数传递给FutureTask
类的构造函数,再将FutureTask
实例作为参数传递给Th
read
类的构造函数,最后调用start()
方法启动线程。可以通过FutureTask
的get()
方法获取线程执行的返回结果。- 使用
ExecutorService
接口及其实现类(如ThreadPoolExecutor
、Executors
提供的工厂方法)创建线程池,将任务(实现Runnable
或Callable
接口的类的实例)提交给线程池执行。线程池基本原理:
1、任务提交到线程池以后,如果工作线程数小于 coreSize,直接交给核心线程执行任务2、核心线程数都满了,任务放到队列中3、队列满了,创建核心线程数以外的线程执行任务4、达到最大线程数,且队列仍然是满的状态,使用拒绝策略拒绝策略
- ThreadPoolExecutor.AbortPolicy(系统默认): 丢弃任务并抛出RejectedExecutionException 异常,让你感知 到任务被拒绝了,我们可以根据业务逻辑选择重试或者放弃提交等策略
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常,相对而言存在一定 的风险,因为我们提交的时候根本不知道这个 任务会被丢弃,可能造成数据丢失。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程),通常是存活时间最长的任务,它也存在一定的数据丢失风险
- ThreadPoolExecutor.CallerRunsPolicy:既不抛弃任务也不抛出异常,而是将某些任务回退到调用者,让调用者去执行它。
7.Hashmao底层原理
1.加载因子 0.75 16*0.75=12 当数组中元素的数量大于12时,扩容为原来的1倍 16-->322. 结构: 数组+链表 +红黑树 在第一次使用时初始化(第一次调用put方法存值时)3. threshold 阈值 = capacity * load factor4.树化的条件1. 链表长度达到阈值:链表的长度达到 8 时,会触发树化的检查。2. 桶数组的容量达到一定值:HashMap 的桶数组(即 table 数组)长度达到 64。5. 反树化的条件当红黑树中的节点数量减少到 6 个时,红黑树会转换为链表6. HashMap的重要参数HashMap 的实例有两个参数影响其性能:初始容量和加载因子。初始容量容量默认为 16加载因子: 0.75 扩容的倍数是:2
8.Java集合框架分几类,核心区别是什么
list:有序,有下标,元素可以重复,通过索引访问
ArrayList
JDK1.2 底层数组实现 查询快, 增删慢 线程不安全, 效率高
LinkedList
JDK1.2 底层链表实现 查询慢, 增删快 线程不安全, 效率高
Vector
JDK1.0 底层数组实现 都慢 线程安全, 效率低set:无序,无下标,元素不能重复,通过for循环
HashSet
JDK1.2 底层哈希表(数组+链表)实现 线程不安全, 效率高
LinkedHashSet
JDK1.2 是HashSet的子类, 底层哈希表实现 线程不安全, 效率高
TreeSet
JDK1.2 是`SortedSet`的实现类, 底层红黑树实现 线程不安全, 效率高map:通过键值对存储,键:无序,无下标,不能重复,值:无序,无下标,可以重复,
通过键访问值
HashMap
JDK1.2 底层哈希表实现 线程不安全, 效率高
LinkedHashMap
JDK1.2 是HashMap的子类, 底层哈希表实现 线程不安全, 效率高
TreeMap
JDK1.2 是SortedMap的实现类, 底层红黑树实现 线程不安全, 效率高
Hashtable
JDK1.0 底层哈希表实现 线程安全, 效率低
Properties
JDK1.0 是Hashtable的子类, 底层哈希表实现 线程安全, 效率低基本特性键值对存储:HashMap以键值对形式存储数据,每个键都是唯一的,一个键对应一个值。无序性:存储的键值对是无序的,不会按照插入顺序或其他特定顺序排列。允许 null 值和 null 键:可以有一个 null 键也能有多个 null 值。非线程安全:(这个特点可以在介绍完hashmap后引出线程安全的实现)在多线程环境下,若多个线程同时访问HashMap,并且至 少有一个线程对其结构进行修改,那么它必须在外部进行同步。底层:加载因子 0.75 16*0.75=12 当数组中元素的数量大于12时,扩容为原来的1倍 16-->322. 结构: 数组+链表 +红黑树 在第一次使用时初始化(第一次调用put方法存值时)3. threshold 阈值 = capacity * load factor4.树化的条件1. 链表长度达到阈值:链表的长度达到 8 时,会触发树化的检查。2. 桶数组的容量达到一定值:HashMap 的桶数组(即 table 数组)长度达到 64。5. 反树化的条件当红黑树中的节点数量减少到 6 个时,红黑树会转换为链表6. HashMap的重要参数HashMap 的实例有两个参数影响其性能:初始容量和加载因子。初始容量容量默认为 16加载因子: 0.75 扩容的倍数是:2
9.sleep和wait的区别
资源释放: sleep()只会释放时间片, wait()会同时释放时间片和锁标记
2. 来源和调用者: sleep()是来自Thread类的静态方法, 通过Thread类调用. wait()是来自于Object类的实例方法, 通过临界资源对象调用
3. 进入状态: sleep()使线程进入有限期等待状态, wait()是线程进入的是无限期等待状态
10.如何比较两个对象是否相等
- 通过==来比较对象是否相等,但不推荐因为==比较的是两个对象的地址
- 通过重写equals()方法和hashCode()方法来比较对象是否相等
equals():该方法默认比较对象地址, 但是实际开发中, 更多的场景需要比较的为对象内容, 所以需要重写
hashCode():该方法默认根据对象地址获取哈希码值, 但是实际开发中, 更多关注的是对象内容, 内容一致则哈希码值理应一致, 所以需要重写
11.synchronized的原理
它会使 用对象的内置锁(也称为监视器锁)来实现同步,每个对象都有一把内置锁,当一个线程访问一个同步代码块 时,它会尝试获取这个锁,如果锁被其他线程持有,则 该线程将被阻塞,直到锁被释放。Synchronized 有以下几个特点:互斥性:Synchronized 保证同一时刻只有一个线程可 以获取锁,并且只有该线程可以执行同步代码块中的 代码。可重入性:同一个线程可以多次获取同步锁而不会被 阻塞,这样可以避免死锁的发生。独占性: 如果一个线程获得了对象的锁,则其他线程 必须等待该线程释放锁之后才能获取锁。缺点:非公平锁 ,当锁被释放后,任何一个线程都有 机会竞争得到锁,这样做的目的是提高效率,但缺点 是可能产生线程饥饿现象。
12.java四大引用
强引用(Strong Reference)定义:强引用是最常见的引用类型,我们平时使用的普通对象引用就是强引用。特点:只要对象具有强引用,垃圾回收器就永远不会回收它。即使内存不足,也会导致程序抛出OutOfMemoryError异常。2. 软引用(Soft Reference)定义:软引用是一种比强引用稍微弱一些的引用类型。特点:只有在系统将要发生内存溢出之前,软引用指向的对象才会被垃圾回收器回收。如果内存足够,垃圾回收器就不会回收它。3. 弱引用(Weak Reference)定义:弱引用是一种比软引用更弱的引用类型。特点:弱引用关联的对象只能生存到下一次垃圾收集发生为止,无论当前内存是否足够,只要垃圾收集器开始工作,都会回收掉只被弱引用关联的对象。用途:主要用于实现规范的映射关系,可以自动清理不再使用的对象。实现:需要使用java.lang.ref.WeakReference类来实现。4. 虚引用(Phantom Reference)定义:虚引用是四种引用类型中最弱的一种。特点:虚引用与垃圾回收过程没有直接关系,主要用于跟踪对象被垃圾回收器回收的活动。虚引用必须和引用队列(ReferenceQueue)一起使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收之前,把这个虚引用加入到与之关联的引用队列中。用途:主要用于跟踪对象的回收情况。实现:需要使用java.lang.ref.PhantomReference类来实现。访问对象:虚引用无法直接访问对象,其get()方法总是返回null。
13序列化和反序列化
序列化:将对象以二进制形式存储到存储介质 (硬盘、文件、网络 )条件 : 对象所属的类必须实现序列化接口Serializable反序列化:从文件或网络中读取二进制转换为对象扩展:序列化 ObjectOutputStream (写)反序列化 ObjectInputStream (读)序列化是把内存Java对象保存到存储介质中反序列化就是把存储介质中的数据转化为Java对象。需要进行序列化的对象的类必须实现Serializable 接口Java通过ObjectInputStream和ObjectOutputStream实现序列化和反序列化应用场景:(重点前2个)缓存系统(如 Redis 存储 Java 对象)(如JDK序列化器)消息中间件(如 Kafka、RabbitMQ)传输对象时,需先序列化字节流再通过网络发送mybatis 二级缓存 (可以不说 因为用的少)
14.线程的常用方法
join() :t.join()方法只会使主线程进入等待池并等待t线程执行完毕后才会被唤醒。并不影响同一时刻处在运行状态的其他线程。yield() :t.yield()让当前线程从“运行状态”进入到“就绪状态”,和让其它线程获一起回到起跑线取 cpu 执行权,wait 和 sleep区别1. 资源释放: sleep()只会释放时间片, wait()会同时释放时间片和锁标记
2. 来源和调用者: sleep()是来自Thread类的静态方法, 通过Thread类调用. wait()是来自于Object类的实例方法, 通过临界资源对象调用
3. 进入状态: sleep()使线程进入有限期等待状态, wait()是线程进入的是无限期等待状态
15.什么是泛型,泛型的作用是什么,泛型怎么定义
泛型是一种允许在定义类、接口和方法时,使用类型参数来指定操作的数据类型的技术。
泛型是Java 5引入的类型参数化机制,本质是编译期的类型安全检查工具
用于集合时, 可以约束集合存储的数据类型
泛型的作用
泛型的主要作用包括以下几点:
(1)类型安全
泛型可以确保在编译时类型安全,避免类型转换错误。例如,使用
ArrayList<String>
时,你只能向其中添加字符串类型的对象,如果尝试添加其他类型的对象,编译器会报错。(2)代码复用
泛型允许编写通用的代码,减少重复代码的编写。例如,一个泛型方法可以处理多种类型的数据,而不需要为每种数据类型编写单独的方法。
(3)提高代码可读性
泛型可以使代码更加清晰和易于理解。通过明确指定类型参数,代码的意图更加明确,其他开发者更容易理解代码的逻辑。
定义:类,方法,接口
1.Java中的多态是什么?如何实现?
大类型引用的多种赋值形态, 具体表述为大类型引用指向不同的小类型对象
通过方法的重载和重写
重载:在同一类中, 方法名相同, 参数列表不同, 与其他部分无关
重写:发生在继承|实现关系之上;返回值类型、方法名、形参列表必须与父类一致;访问修饰符必须与父类相同|更宽;不允许抛出比父类更大|更多的异常
2.finalize,final,finally的区别
final是一个修饰符, 都能修饰类不能被继承;修饰方法不能被重写;修饰变量变成常量值不可改(
(1)局部常量
对赋值时机无要求, 未二次赋值即可
(2) 常量属性
1. 没有默认值
2. 必须在声明创建时赋值, 保证空间分配. 赋值时机:
- 声明时直接赋值
- 在所有构造中提供赋值操作
)
finally是一个代码块, 通常与try-catch结合使用, 作用为执行关闭|释放资源的代码
finalize是Object中的一个方法, 作用为在垃圾回收机制回收对象之前执行清理操作
3.线程的五种基本状态
新建状态:新建一个线程,但并未执行start()
就绪状态:线程执行start()方法进入就绪状态,等待CPU分配时间片
运行状态:线程拿到CPU所分配的时间片,开始执行
阻塞状态:通过sleep或join方法wait方法使线程进入等待状态,
终止状态:线程执行完全部代码后,结束运行
4.如何获取生成的主键
对于支持主键自增的数据库
需要将useGeneratedKeys 设置为true,允许mybatis获取数据库内部自动生成的主键,任何使用keyProperty存储主键值的属性或key(如果参数类型是javabean,那么就指明其中一个存储主键值,如果参数类型是map,就指明存放主键值的key),keyColumn 指定在表中代表主键的字段名
对于不支持主键自增的数据库
需要使用selectkey元素处理主键
keyProperty 用于存放主键值
- 如果参数类型是javabean keyProperty 指明javabean中的哪个属性存储主键值
- 如果参数是Map类型,keyProperty 指明用于存放主键值的key
keyColumn 代表主键的列名
order 指定key值什么时候存入keyProperty , 是insert 前 或 后
值:
- BEFORE 生成主键,设置
keyProperty
再执行插入语句- AFTER 先执行插入语句,然后是
selectKey
中的语句
5.Mybatis如何执行批量操作
使用foreach标签foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。item表示集合中每一个元素进行迭代时的别名,随便起的变量名;index指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用;open表示该语句以什么开始,常用“(”;separator表示在每次进行迭代之间以什么符号作为分隔符,常用“,”;close表示以什么结束,常用“)”。在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:1. 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list2. 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array3. 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Mapmap的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key
6.面向对象的五大基本原则
单一职责原则(Single Responsibility Principle,SRP)
含义:一个类或者模块应该有且只有一个改变的原因。也就是说,一个类只负责一项职责。
好处:降低类的复杂度,提高代码的可读性和可维护性,当某一职责发生变化时,不会影响到其他职责。
开闭原则(Open Closed Principle,OCP)
含义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。即可以通过扩展来实现新功能,而不是修改已有的代码。
好处:增强软件的可扩展性和稳定性,减少引入新问题的风险。
里氏替换原则(Liskov Substitution Principle,LSP)
含义:子类可以替换其父类并且出现在父类能够出现的任何地方,而不会影响系统的正确性。也就是说,子类必须能够完全实现父类的功能。
好处:保证系统的继承体系稳定,提高代码的复用性和可维护性。
接口隔离原则(Interface Segregation Principle,ISP)
含义:客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。
好处:降低类之间的耦合度,提高系统的灵活性和可维护性。
依赖倒置原则(Dependency Inversion Principle,DIP)
含义:高层模块不应该依赖低层模块,二者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。简单来说,就是要面向接口编程,而不是面向实现编程。
好处:降低模块间的耦合度,提高系统的可扩展性和可维护性。
7.JVM是什么?谈谈你对JVM的认识
JVM即Java虚拟机(Java Virtual Machine),它是一个抽象的计算机,是Java程序的运行基础。JVM可以在不同的操作系统上提供统一的运行环境,使得Java程序能够实现“一次编写,到处运行”的特性。
JVM内存模型:
程序计数器:程序计数器是一块较小的内存空间,是当前线程正在执行的那条字节码指令的地址。
当在多线程情况下,记录的是当前线程执行的位置,进行线程切换时就可以清楚知道上次线程执行到哪里,线程私有
java虚拟机栈:
Java 虚拟机栈会为每一个即将运行的 Java 方法创建一块叫做“栈帧”的区域,用于存放该方法运行过程中的一些信息
本地方法栈:本地方法栈是为 JVM 运行 Native 方法准备的空间,它与 Java 虚拟机栈实现的功能类似,只不过本地方法栈是描述本地方法运行过程的内存模型。
堆:线程共享,是垃圾回收的主要场所,
堆可分为新生代、老年代。
new的对象会放到新生代,当新生代满了之后会触发垃圾回收机制,把不会再被引用的对象回收,将新生代中剩余对象放入幸存区中,默认经过15次回收后,将幸存区中对象放入老年区中
方法区:方法区也是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
8.构造器是否可以被重写
不能
原因:构造器的名称必须和类名一致,这就决定了子类不能定义出与父类构造器名称、参数列表、返回类型都相同且符合重写规则的构造器
9.面对对象的四大特性
封装
将数据和操作数据的方法绑定在一起,隐藏内部实现细节,仅对外暴露必要接口。通过访问控制权限(如私有、保护、公有)确保数据安全性,降低模块间耦合度。继承
允许子类继承父类的属性和方法,实现代码复用和层次化设计。子类可扩展或重写父类功能,支持单继承或多继承(视语言而定),体现"is-a"关系。多态
同一操作作用于不同对象时产生不同行为。通过方法重写(子类覆盖父类方法)和接口实现,实现运行时动态绑定,增强系统灵活性和可扩展性。抽象
提取核心特征而忽略非关键细节,通过抽象类或接口定义规范。强制子类实现约定方法,分离"做什么"与"怎么做",降低系统复杂度。
10.堆和栈的区别
栈:主要存储局部变量与方法调用信息、由系统自动完成内存的分配与回收、内存空间相对较小,不过访问速度快
堆:用于存放对象实例与数组、内存的分配和回收由垃圾回收器负责、内存空间较大,但访问速度慢
11.什么是反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。在 Spring 框架中,反射机制被广泛用于以下几个方面:
1. 依赖注入:Spring 使用反射机制获取对象并进行属性注入,从而实现依赖注入。2. AOP:Spring AOP 使用 JDK 动态代理或者 CGLIB 字节码增强技术来实现 AOP 的切面逻辑,这其中就包含了对被代理对象方法的反射调用。3. MVC 框架:Spring MVC 框架使用反射来调用相应的控制器方法,从而实现请求的处理。4. 数据库访问框架:Spring 的 JDBC 框架使用反射机制来实现对数据库的访问。5. 容器管理:Spring 容器也使用了反射机制来管理对象的实例化和依赖注入。需要注意的是,虽然反射机制为开发者提供了极大的便利性,但是过度使用反射也可能导致性能问题,在使用时需要进行适量控制。
12.短路机制&&,||与&,|
短路机制:当执行到可以奠定最终结果的表达式时, 后续表达式将不再执行
短路时机:
&&: 执行到false时
||: 执行到true时
非短路运算符: &和|特点为无论如何所有表达式都会执行
13.事务
事务(Transaction)是数据库管理系统执行过程中的一 个逻辑单位,它由一个或多个SQL语句组成,这些语句作为一个整体一起向系统提交,要么全部执行,要么全 部不执行,即事务具有原子性(Atomicity)。1.原子性(Atomicity):事务是数据库中的最小工作 单元,事务中的所有操作要么全部完成,要么全部不 做,事务在执行过程中发生错误会被回滚 到事务开始前的状态,就像这个事务从 未执行过一样。2. 一致性(Consistency):事务必须使数据库从一个 一致性状态变换到另一个一致性状态。一致性与原子 性是密切相关的。3. 隔离性(Isolation):数据库系统提供一定的隔离级别,保证事务在不受外部并发操作影响的“独立”环境 执行。这意味着事务处理过程中的中间状态对外部是 不可见的,或者说,事务处理过程中的数据变化只有 在这个事务提交时才对其他事务可见。4. 持久性(Durability):一旦事务被提交,它对数据 库的修改就是永久性的,接下来的其他操作和数据库 故障不应该对其有任何影响隔离级别1. 读未提交:最低级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读或幻读。2.读已提交:允许读取已经提交的数据,可以阻止脏读,但不可重复读和幻读仍有可能发生。3. 可重复读:MySQL InnoDB的默认事务隔离级别。确保在同一个事务中多次读取同样记录的结果是一致的,但幻读仍有可能发生。4. 可串行化:最高的隔离级别,通过强制事务串行执行,避免了脏读、不可重复读和幻读,但这也将大大降低数据库的并发性能。
14.Hash碰撞是什么?如何解决?
hash碰撞指的是,两个不同的值(比如张三、李四的 id)经过hash计算后,得到的hash值相同,后来的李四 要放到原来的张三的位置,但是数组的位置已经被张三占了,导致冲突。开放寻址法开放定址法通过探测哈希表中的空闲位置来解决哈希碰撞。当发生碰撞时,哈希表会寻找下一个空闲的槽位来存储元素。常见的探测策略有:线性探测:如果当前位置发生碰撞,检查下一个位置,直到找到空位。二次探测:采用平方的方式逐步探测空闲位置。双重哈希:使用第二个哈希函数来计算探测步长。拉链法链地址法是通过为每个哈希表的槽位创建一个链表来存 储所有发生碰撞的元素。当发生碰撞时,新的元素会被加入到该位置的链表中。再哈希法当发生冲突时,使用第二个、第三个、哈希函数计算地址,直到无冲突时。缺点:计算时间增加。建立公共溢出区将哈希表分为基本表和溢出表。凡是与基本表发生冲突 的元素,一律填入溢出表。查找时,先在基本表中查 找,若找不到再到溢出表中查找。这种方法实现相对简 单,但可能增加查找的时间开销。
15.java中异常体系是如何分类的
Throwable: 不正常情况的总父类
Error : 错误
特点: 无法解决也无法提前避免
触发原因: 通常由硬件问题|内存问题导致
Exception:异常
特点: 可以解决也可以提前避免
触发原因: 通常由代码导致
分类:
RuntimeException: 运行时异常, 也称为未检查异常|未检异常等`
特点: 编译成功, 运行报错, 可以选择性处理
常见运行时异常:
java.lang.ArithmeticException: 数学运算异常
java.lang.ArrayIndexOutOfBoundsException: 数组下标越界异常
java.lang.StringIndexOutOfBoundsException: 字符串下标越界异常
java.lang.IndexOutOfBoundsException: 下标越界异常
java.lang.NullPointerException: 空指针异常
java.lang.ClassCastException: 类型转换异常
java.lang.NumberFormatException: 数据类型转换异常
1.MyISAM与InnoDB区别
存储结构与文件类型MyISAM使用非聚集索引,索引和数据文件是分离的。在磁盘上存储为三个文件:.frm(表定义)、.MYD(数据文件)、.MYI(索引文件)。支持静态表、动态表和压缩表三种存储格式。InnoDB使用聚集索引,索引和数据是关联在一起的,表数据文件本身就是按B+Tree组织的一个索引结构。有两种存储方式:共享表空间存储和多表空间存储。表结构和MyISAM一样,以表名开头,扩展名是.frm。如果使用共享表空间,所有表的数据文件和索引文件保存在一个表空间里;如果使用多表空间,每个表都有一个表空间文(.ibd)。2. 事务与外键(重点)MyISAM不支持事务处理,也不支持外键。InnoDB支持事务处理,是事务安全的存储引擎。支持外键,可以确保数据的参照完整性。3. 锁机制(重点)MyISAM只支持表级锁,无论是读操作还是写操作都会锁定 整个表。表级锁开销小,加锁快,但并发度低。InnoDB支持表级锁和行级锁,默认情况下使用行级锁。行级锁开销大,但并发度高,可以有效减少锁冲突。InnoDB的行锁是通过索引实现的,如果没有命中索引,则会退化为表锁。4. 性能与功能MyISAM强调性能,特别是读操作的性能,因为表级锁在读取时不会阻塞其他读操作。适用于读多写少的场景,如Web应用中的静态内容表。支持全文索引(FULLTEXT),在全文搜索方面性 能较好。InnoDB提供了更丰富的数据库功能,如事务、外键等。适用于需要高并发写入和复杂查询的场景。不直接支持全文索引,但可以通过插件(如Sphinx)实现。5. 计数与数据维护MyISAM保存了表的总行数,执行SELECT COUNT(*) FROM table;时直接返回该值,速度很快。InnoDB不保存表的总行数,执行SELECT COUNT(*) FROM table;时需要全表扫描,速度较慢。在处理大量数据时,InnoDB的自动增长列和索引管理更为复杂。6. 备份与恢复MyISAM数据以文件形式存储,备份和恢复时可以单独针对某个表进行操作。InnoDB备份和恢复相对复杂,需要拷贝数据文件、备份binlog或使用mysqldump等工具。在数据量较大时,备份和恢复操作可能较为耗时。7. 适用场景MyISAM适用于读操作远多于写操作的场景,如Web应用的静态内容表。适用于不需要事务和外键支持的应用。InnoDB适用于需要事务处理、外键支持以及高并发写入的应用。适用于对数据完整性和一致性要求较高的场景。MyISAM:使用非聚集索引,也就是索引和数据文件是分离的。不支持事务处理,也不支持外键。只支持表级锁,无论是读操作还是写操作都会锁定 整个表。强调性能,特别是读操作的性能,因为表级锁在读取时不会阻塞其他读操作。同时也保存了表的总行数,在查询表的总行数时直接返回结果,数据以文件形式存储,备份和恢复时可以单独针对某个表进行操作。适用于读操作远多于写操作的场景,如Web应用的静态内容表。适用于不需要事务和外键支持的应用。InnoDB:使用聚集索引,支持事务处理,是事务安全的存储引擎。支持外键,可以确保数据的参照完整性。支持表级锁和行级锁,默认情况下使用行级锁。不保存表的总行数,执行SELECT COUNT(*) FROM table;时需要全表扫描,速度较慢。备份和恢复相对复杂,需要拷贝数据文件、备份binlog或使用mysqldump等工具。适用于需要事务处理、外键支持以及高并发写入的应用。适用于对数据完整性和一致性要求较高的场景。
2.什么是索引
索引是数据库中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的类型:单列索引:针对表中单个列的索引,适用于该列的等值查询、范围查询和排序操作。多列索引(复合索引):针对表中多个列的联合索引,适用于这些列的复合查询。唯一索引:确保索引列的值在整个表中是唯一的,可以加速唯一值的查询。用户名唯一 、订单单号 这类的数据如果经常查,建议创建唯一索 引主键索引:一种特殊的唯一索引,用于唯一标识表中的每一行,每个表只能有一个主键索引。全文索引:用于对文本类型的列进行全文搜索,能高效处理大文本字段的查找。空间索引:用于地理空间数据类型的列,例如Point、LineString和Polygon,可以加速对地理空间数据的搜索和分析索引的优点:1.提高检索速度2. 加速排序和分组操作3. 提高连接性能4. 保证数据完整性5. 支持快速查找索引的缺点1. 占用存储空间2. 维护成本高3. 增加写操作的时间4. 不适用于所有查询5. 索引失效
3.索引的优缺点
索引的优点1. 提高检索速度:索引可以显著加快数据检索的速度,尤其是在大型数据集中,通过减少磁盘I/O操作次数,降低系统资源消耗。2. 加速排序和分组操作:索引可以优化ORDER BY和GROUP BY子句的执行速度,因为索引本身是有序的3. 提高连接性能:索引能加快多表连接(JOIN)操作的执行效率。4. 保证数据完整性:通过唯一性约束或主键约束,索引可以帮助保证数据的唯一性和完整性。5. 支持快速查找:索引提供快速的查找功能,使得查询更加灵活和高效。索引的缺点1. 占用存储空间:索引会占用额外的存储空间,这可能会随着数据量的增加而显著增大。2. 维护成本高:索引需要定期维护,包括创建、更新和删除索引,这可能会增加数据库的负担和维护成本3. 增加写操作的时间:对表进行插入、更新和删除操作时,索引也需要进行相应的更新,这可能会增加写操作的时间。4. 不适用于所有查询:并非所有的查询都适合使用索引,有些查询可能会因为索引而变得更慢。5. 索引失效:如果索引选择不当或者使用不当,可能会导致索引失效,从而影响查询性能。
4.主键索引与唯一索引的区别
主键索引与唯一索引在数据库管理系统中扮演着重要的 角色,但它们在多个方面存在显著的差异。以下是主键 索引与唯一索引的主要区别:
1. 定义与本质
主键索引:
主键索引是一种特殊的约束,它要求表中 的每一行都有一个唯一标识,即主键值。主键索引不 仅保证了数据的唯一性,还决定了表中数据的物理存 储顺序(在支持聚簇索引的数据库中,如InnoDB)。 主键索引不允许有空值。
唯一索引:唯一索引是一种索引类型,它确保索引列 中的每个值都是唯一的,不允许重复。与主键索引不 同,唯一索引列可以包含空值(但需要注意的是,如 果列中已存在空值,则只能有一个空值,因为索引的 唯一性约束)。
2. 数量限制
主键索引:一个表只能有一个主键索引。这是由主键 索引的唯一性和非空性决定的。
唯一索引:一个表可以有多个唯一索引。这些唯一索 引可以应用于不同的列或列组合,只要它们各自保证 唯一性即可。
3. 空值处理
主键索引:
主键索引列不允许有空值。这是主键约束 的一部分,确保每一行都有一个明确且唯一的标识。
唯一索引:唯一索引列允许有空值,但空值在唯一性 约束中被视为不同的值(即多个空值不会违反唯一性 约束,但通常只允许一个空值存在,除非数据库配置 或索引策略允许多个空值)。
4. 外键引用
主键索引:主键索引可以被其他表用作外键,建立表 之间的关联关系。这是实现数据库参照完整性的重要 机制之一。
唯一索引:唯一索引不能直接被其他表用作外键。外 键约束通常要求引用的是主键或具有唯一性的列,但 直接引用唯一索引作为外键的情况较少见,因为主键 索引提供了额外的完整性和性能优势。
5. 索引类型与性能
在某些数据库系统中(如MySQL的InnoDB存储引 擎),主键索引是聚簇索引,它决定了表中数据的物 理存储顺序。这意味着通过主键索引查询数据可以非 常高效,因为数据本身就是按照索引顺序存储的。
唯一索引可以是聚簇索引(如果它是表上的第一个索 引且表支持聚簇索引)或非聚簇索引。非聚簇唯一索 引在查询时可能需要额外的步骤来定位数据行,因为 索引和数据在物理上是分开的。
6. 创建方式 主键索引:
主键索引通常是在创建表时通过定义主键 约束来创建的,也可以使用ALTER TABLE语句在表创 建后添加主键约束。
唯一索引:唯一索引可以通过CREATE UNIQUE INDEX语句在表创建后添加,也可以在创建表时通过 定义唯一性约束来间接创建。
5.索引哪些情况会失效
- 查询条件包含or
- like通配符可能导致索引失效。如 like '%a' 会失效, 而 like 'a%'不会失效
- 联合索引,查询时的条件列不是联合索引中的第一个列,索引失效(最左前缀匹配原则)
- 索引列使用了函数或表达式 当查询条件中对索引列使用了函数、类型转换或表达式时,数据库需要先计算表达式的值,然后再进行查询,这会导致索引失效
- 对索引列运算(如,+、-、*、/),索引失效。
- 索引字段上使用(!= 或者 < >)时,可能会导致索引失效。
- 使用了NOT IN或NOT EXISTS:这些操作符在某些情况下可能导致索引失效,尤其是处理不确定数量的结果集时
- 索引列上存在过多的 NULL 值 索引字段上使用is null, is not null,可能导致索引失效。
- 表中数据量较少 对于数据量较少的表,数据库可能会选择全表扫描而不是利用索引,因为全表扫描的开销相对较小。
- 隐式类型转换:查询条件中的列与索引列的类型不匹配,数据库进行隐式类型转换,可能会使索引失效
6.数据库如何优化
索引优化合理创建索引:分析查询语句中用于条件过滤、连接操作和排序的列,并在这些列上创建索引。但索引并非越多越好,过多的索引会增加存储成本和数据更新的开销。覆盖索引:尽量使用覆盖索引,即索引包含了查询所需的所有列,这样查询时可以直接从索引中获取数据,而无需回表查询,提高查询效率。查询语句优化避免全表扫描:确保查询语句能够利用索引来过滤数据,避免使用没有索引支持的条件进行查询,导致全表扫描。优化连接查询:在连接多个表时,确保连接条件正确且使用了合适的索引,以减少连接操作的成本。使用合适的聚合函数:在进行聚合查询时,选择合适的聚合函数,并确保查询语句能够高效地计算聚合结果。数据类型优化选择合适的数据类型:根据数据的实际范围和用途,选择占用空间小、查询效率高的数据类型。避免使用 TEXT 和 BLOB 类型:如果可能,尽量避免 在经常查询的列中使用 TEXT 和 BLOB 类型,因为这些类型的数据存储和查询效率相对较低。数据库设计优化范式化设计:遵循数据库设计的范式,以减少数据冗余,提高数据的一致性和完整性。但在某些情况下,为了提高查询性能,可以适当违反范式,进行反范式化设计。合理分表:根据数据的特点和查询需求,将大表拆分成多个小表,以提高查询效率和维护性。例如,可以按照数据的时间范围、业务模块等进行分表。服务器配置优化调整内存配置:根据服务器的物理内存大小和数据库的使用情况,合理配置数据库的内存参数,如缓冲池大小、排序缓冲区大小等,以提高数据库的性能。优化磁盘 I/O:使用高速的磁盘设备,如固态硬盘(SSD),可以显著提高数据库的读写性能。同时,可以通过调整磁盘缓存、优化磁盘分区等方式来进一步优化磁盘 I/O。定期维护数据库清理无用数据:定期删除不再使用的数据,以减少数据库的存储空间和查询数据量。优化表结构:定期对表进行分析和优化,以更新统计信息、整理碎片,提高数据库的性能。备份数据库:定期备份数据库,以防止数据丢失,并在必要时能够快速恢复数据库。
7. MySQL慢查询怎么解决?
slow_query_log 慢查询开启状态。#查看 show variables like 'slow_query_log' #打开慢查询 set @@global.slow_query_log='on'
slow_query_log_file 慢查询日志存放的位置(这个目录需要MySQL的运行帐号的可写权限,一般设置为 MySQL的数据存放目录)。#查看位置 show variables like 'slow_query_log_file';
long_query_time 查询超过多少秒才记录。#查看慢查询时间 默认为10s show variables like 'long_query_time'; #更新为5s 更新当前会话的时间 set long_query_time=5 #更新全局 set global long_query_time=5
8.mysql什么是死锁,怎么解决
死锁的定义在 MySQL 里,死锁指的是两个或多个事务在执行过程中,因争夺锁资源而造成的一种互相等待的局面。若没有外力的介入,这些事务都无法继续推进。解决死锁的方法1. 优化事务逻辑减少事务持有锁的时间:把不必要的操作移出事务,让事务尽快提交或回滚,以此减少锁的持有时间,降低死锁发生的概率。按相同顺序访问资源:在多个事务里,都按照相同的顺序来访问表或者行,避免循环等待的情况。2. 调整隔离级别降低隔离级别:不同的隔离级别对锁的使用和持有时间是不一样的。可以把隔离级别从较高的(如可串行化)调整为较低的(如读已提交),减少锁的竞争。不过要注意,降低隔离级别可能会引发其他并发问题,像脏读、不可重复读等。3. 设置锁超时时间设置 innodb_lock_wait_timeout 参数:该参数规定了事务等待锁的最长时间。当事务等待锁的时间超过这个设定值时,就会自动回滚,从而解除死锁。可以通过以下语句设置该参数:上述语句把全局的锁等待超时时间设定为 60 秒。4. 死锁检测和回滚机制InnoDB 存储引擎的死锁检测:InnoDB 会自动检测死锁,一旦发现死锁,就会选择一个事务进行回滚,以解除死锁。被回滚的事务通常是开销较小的事务。可以通过以下语句查看死锁信息:该语句会显示 InnoDB 存储引擎的详细状态信息,其中包含最近一次死锁的详细情况,有助于分析死锁产生的原因。5. 数据库设计优化索引优化:合理地创建索引能够减少锁的范围,提高查询效率,进而降低死锁的发生概率。表结构优化:避免在一张表中存储过多的数据,可根据业务需求进行表的拆分,减少锁的竞争。
9.Mysql事务
从以下几方面说:1. 事务的概念2. 事务的特性acid (可以结合日志文件说明 redo_log undo_log)3. 事务的隔离级别 (4种) 隔离级别对应的问题4. 扩展: 在实际开发中如何使用事务?分为两种:1. 单体结构 使用spring 管理事务@Transactionl 原理用的aop 注意事务的5个属性2. 在分布式中使用分布式事务如seata 提到二段提交 三段提交定义事务(Transaction)是数据库管理系统执行过程中的一个逻辑单位,它由一个或多个SQL语句组成,这些语句作为一个整体一起向系统提交,要么全部执行,要么全部不执行,即事务具有原子性(Atomicity)。事务的引入是为了解决数据库并发操作可能带来的数据不一致性和完整性问题。事务的特性(ACID)参考事务ACID核心日志机制事务具有四个基本特性,通常简称为ACID:1. 原子性(Atomicity):事务是数据库中的最小工作单元,事务中的所有操作要么全部完成,要么全部不做,事务在执行过程中发生错误会被回滚 (Rollback)到事务开始前的状态,就像这个事务从未执行过一样。2. 一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态。一致性与原子性是密切相关的。3. 隔离性(Isolation):数据库系统提供一定的隔离级别,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,或者说,事务处理过程中的数据变化只有在这个事务提交时才对其他事务可见。4. 持久性(Durability):一旦事务被提交,它对数据库的修改就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。隔离级别为了解决并发事务可能带来的问题(如脏读、不可重复读、幻读),SQL标准定义了四个隔离级别:1. READ UNCOMMITTED:最低级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读或幻读。2. READ COMMITTED:允许读取已经提交的数据,可以阻止脏读,但不可重复读和幻读仍有可能发生。3. REPEATABLE READ:MySQL InnoDB的默认事务隔离级别。确保在同一个事务中多次读取同样记录的结果是一致的,但幻读仍有可能发生。4. SERIALIZABLE:最高的隔离级别,通过强制事务串行执行,避免了脏读、不可重复读和幻读,但这也将大大降低数据库的并发性能。
10.数据库锁
基于属性的分类
1. 共享锁(Share锁) 也称为读锁,当一个事务为数据加上读锁后,其他 事务只能对该数据加读锁,而不能加写锁,直到所 有的读锁释放后,其他事务才能对数据进行加写 锁。 主要用于支持并发的读取数据,读取数据时不支持 修改,避免出现重复读的问题。
2. 排他锁(X锁) 也称为写锁,当一个事务为数据加上写锁时,其他 事务将无法再为数据加任何锁,直到该锁释放后, 其他事务才能对数据进行加锁。 主要用于保护数据的写操作,确保在数据修改时, 不允许其他人同时修改或读取,从而避免脏数据和 脏读的问题。
基于粒度分类
1. 行级锁 行级锁是Mysql中锁定粒度最细的一种锁,表示只 针对当前操作的行进行加锁。行级锁能大大减少数 据库操作的冲突。其加锁粒度最小,但加锁的开销 也最大。行级锁分为共享锁(读) 和 排他锁 (写)。 特点:开销大,加锁慢;会出现死锁;锁定粒度最 小,发生锁冲突的概率最低,并发度也最高。
2. 表级锁 表级锁是MySQL中锁定粒度最大的一种锁,表示对 当前操作的整张表加锁,它实现简单,资源消耗较 少,被大部分MySQL引擎支持。最常使用的 MYISAM与INNODB都支持表级锁定。表级锁定分 为表共享读锁(共享锁)与表独占写锁(排他 锁)。 特点:开销小,加锁快;不会出现死锁;锁定粒度 大,发出锁冲突的概率最高,并发度最低。
3. 页级锁(了解) 页级锁是MySQL中锁定粒度介于行级锁和表级锁中 间的一种锁。表级锁速度快,但冲突多,行级冲突 少,但速度慢。所以取了折衷的页级,一次锁定相 邻的一组记录。 特点:开销和加锁时间界于表锁和行锁之间;会出 现死锁;锁定粒度界于表锁和行锁之间,并发度一 般
基于策略分类
1. 乐观锁 假设并发操作时不会发生冲突,只在提交事务时检 查数据是否被其他事务修改过。 常用于读多写少的场景,通过版本号或时间戳等方 式实现。
2. 悲观锁 假设并发操作时会发生冲突,因此在操作期间持有 锁来避免冲突。 常用于写多读少的场景,通过SELECT ... FOR UPDATE等语句实现。
其他特殊锁(了解)
1. 全局锁 对整个数据库实例加锁,限制除了超级用户外的所 有查询和修改操作。 一般用于备份、恢复等操作。
2. 元数据锁(MDL)锁定数据库对象的元数据,如表结构,用于保证数 据定义的一致性。 在对表进行CRUD操作时自动加上,事务提交后释 放。
3. 意向锁 是一种表明事务将要请求什么类型锁的锁。 包括意向共享锁和意向排他锁,用于辅助系统在给 定事务请求锁之前,判断是否会与其他事务的锁冲 突。
11.Mysql中常用的函数有那些?
数字函数CEIL(x)/CEILING(x):返回大于或等于x的最小整数。FLOOR(x):返回小于或等于x的最大整数。ROUND(x):返回离x最近的整数;ROUND(x,y)对x进行四舍五入的操作,返回值保留小数点后面指定的y位。二、字符串函数CONCAT(s1,s2,...):将s1,s2等多个字符串合并为一个字符串,分隔符默认是 逗号CONCAT_WS(x,s1,s2,...):同CONCAT(s1,s2,...),但是每个字符串之间要加上x,x可以是分隔符。GROUP_CONCAT(expr) 将多条记录合并为一个LENGTH(str)/CHAR_LENGTH(s)/CHARACTER_LENGTH(s):返回字符串s的字符数(不是字节长度)。LOWER(s)/LCASE(s):将字符串s的所有字母变成小写字母。UPPER(s)/UCASE(s):将字符串s的所有字母变成大写字母。TRIM(s):去掉字符串s开始和结尾处的空格。LTRIM(s):去掉字符串s开始处的空格。RTRIM(s):去掉字符串s结尾处的空格。SUBSTR(s, start, length)/SUBSTRING(s, start,length):从字符串s的start位置截取长度为length的子字符串。REPLACE(s,from_str,to_str):把字符串s中的from_str内容替换为to_str。POSITION(s1 IN s)/LOCATE(s1,s):从字符串s中获取s1的开始位置。三、日期函数CURDATE()/CURRENT_DATE():返回当前日期。CURRENT_TIME()/CURTIME():返回当前时间。NOW():返回当前日期和时间。DATE_ADD(date, INTERVAL expr type):计算起始日期date加上一个时间段后的日期。DATEDIFF(d1,d2):计算日期d1和d2之间相隔的天数。DATE_FORMAT(date,format):按表达式format的要求显示日期date。四、聚合函数AVG(expression):返回某列的平均值。COUNT(expression):返回某列/某组/整表的行数。MAX(expression):返回某列的最大值。MIN(expression):返回某列的最小值。SUM(expression):返回指定字段的总和。五、流程控制函数IF(test,v1,v2):如果表达式test成立,返回结果v1;否则,返回结果v2。ifnull(exp1,exp2) 如果表达式exp1不是null 返回 exp1,否则返回exp2
12.sql语句的执行顺序
FROM子句:首先,SQL会执行FROM子句,以确定查询中涉及的所有表和视图。如果存在多表连接(如JOIN操作),则在这一步也会处理表之间的连接关系。FROM子句还负责数据的组装,即将来自不同数据源的数据合并成一个结果集。2. JOIN:如果有多个表参与查询,则指定它们之间的连接类型和条件。3. ON:在执行JOIN操作时,应用ON子句中的条件来筛选连接的记录。4. WHERE子句:接下来,SQL会执行WHERE子句,对FROM子句产生的结果集进行筛选。WHERE子句中的条件用于过滤掉不满足条件的行,只保留满足条件的行进入下一步处理。5. GROUP BY子句:如果查询中包含了GROUP BY子句,则SQL会在WHERE子句之后执行GROUP BY子句。GROUP BY子句将结果集中的行按照一个或多个列的值进行分组,每个分组包含具有相同列值的行。6. HAVING子句:HAVING子句通常与GROUP BY子句一起使用,用于对分组后的结果进行过滤。与WHERE子句不同的是,HAVING子句可以对聚合函数的结果进行过滤。如果查询中没有GROUP BY子句,HAVING子句也可以单独使用,但此时它会将整个结果集视为一个大的分组。7. SELECT子句:在WHERE、GROUP BY和HAVING子句处理完毕后,SQL会执SELECT子句。SELECT子句指定了要从结果集中检索的列或表达式。如果查询中包含了聚合函数(如SUM、AVG、COUNT等),则这些函数会在这一步进行计算。8. DISTINCT关键字(如果使用了):如果SELECT子句中包含了DISTINCT关键字,则SQL会在返回最终结果之前去除重复的行。9. ORDER BY子句:最后,如果查询中包含了ORDER BY子句,则SQL会根据ORDER BY子句中的列或表达式对结果集进行排序。排序可以是升序(ASC)或降序(DESC),默认为升序。10. LIMIT/OFFSET子句(或等效的):在某些数据库系统中,可能还允许使用LIMIT/OFFSET子句(或其等效物,如FETCH FIRST、OFFSET ROWS FETCH NEXT等)来限制返回的记录数或跳过某些记录。这些子句通常在查询的最后阶段执行。
13.Mysql B+tree索引和hash索引的区别
特性
|
Hash 索引
|
B+Tree 索引
|
---|---|---|
数据结构
| 哈希表实现通过哈希函数将键值映射到哈希表中的位置。 | 使用 B + 树结构所有数据都存储在叶子节点,非叶子节点仅用于索引导航。 |
查询性能
| 低 | 高 |
排序支持
|
不支持排序,因为哈希表中的数据是无
序的。
|
支持排序,因为 B + 树的叶子节点是有序的。
|
索引键值
|
仅支持等值比较
|
支持各种比较操作
|
存储空间
|
比 B+Tree 索引更紧凑
|
空间占用较大。
|
哈希冲突处理
|
通过链表或开放寻址法处理冲突
|
不存在哈希冲突问题。
|
适用场景
|
内存表、哈希分区
|
大多数场景
|
14.数据库三大范式是什么
第一范式(1 NF):字段不可再拆分。例如对数据“销售部张三”来说就不遵循第一范式,但并不代表遵循第一范式就一定是好的,例如大多数地址数据都可以拆分,但拆分后表就显得复杂第二范式(2 NF):表中任意一个主键或任意一组联合主键,可以确定除该主键外的所有的非主键值。也就是根据主键可以得到其他非主键的唯一值第三范式(3 NF):在任一主键都可以确定所有非主键字段值的情况下,不能存在某非主键字段 A 可以获取 某非主键字段 B。
15.mysql中in和exists的区别
in关键字确定给定的值是否与子查询或列表中的值相匹配。in在查询的时候,首先查询子查询的表,然后将内表和外表做一个笛卡尔积,然后按照条件进行筛选。所以相对内表比较小的时候,in的速度较快。exists关键字指定一个子查询,检测行的存在。遍历循环外表,然后看外表中的记录有没有和内表的数据一样的。匹配上就将结果放入结果集中。in 与 exists 的区别mysql中的in语句是把外表和内表作hash 连接,而exists语句是对外表作loop循环,每次loop循环再对内表进行查询。一直大家都认为exists比in语句的效率要高,这种说法其实是不准确的。这个是要区分环境的。1. 如果查询的两个表大小相当,那么用in和exists差别不大。2. 如果两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in。3. not in 和not exists:如果查询语句使用了not in(索引失效),那么内外表都进行全表扫描,没有用到索引;而not extsts的子查询依然能用到表上的索引。所以无论那个表大,用not exists都比not in要快。