饿汉式: 对象预先加载,线程是安全的,在类创建好的同时对象生成,调用获得对象实例的方法反应速度快,代码简练。
懒汉式: 对象延迟加载,效率高,只有在使用的时候才实例化对象,但若设计不当线程会不安全,代码相对于饿汉式复杂,
第一次加载类对象的时候反应不快。
单例模式:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
两种方式的对比:
坏处:对象加载时间过长。
好处:饿汉式是线程安全的
线程不安全。--->修改
饿汉式1:
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank instance = new Bank();
//3.提供公共的静态的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
饿汉式2:使用了静态代码块
class Order{
//1.私有化类的构造器
private Order(){
}
//2.声明当前类对象,没初始化
//4.此对象也必须声明为static的
private static Order instance = null;
static{
instance = new Order();
//3.声明public、static的返回当前类对象的方法
public static Order getInstance(){
return instance;
}
}
懒汉式:
class Bank {
private Bank() {
}
private static Bank instance = null;
public static Bank getInstance()
{
if (instance == null) {
//同步代码块,解决线程安全问题
synchronized (Bank.class)
{
if (instance == null)
{
instance = new Bank();
}
}
}
return instance;
}
}
int[] arr = new int[]{43,32,76,-98,0,64,33,-21,32,99};
//冒泡排序
for(int i = 0;i < arr.length - 1;i++){
for(int j = 0;j < arr.length - 1 - i;j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + "\t");
}
JVM
Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。
JRE
Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包
如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
JDK
Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等
JVM&JRE&JDK关系图
所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行。
实现原理:Java程序是通过java虚拟机在系统平台上运行的,只要该系统可以安装相应的java虚拟机,该系统就可以运行java程序。
简单易学(Java语言的语法与C语言和C++语言很接近)
面向对象(封装,继承,多态)
平台无关性(Java虚拟机实现平台无关性)
支持网络编程并且很方便(Java语言诞生本身就是为简化网络编程设计的)
支持多线程(多线程机制使应用程序在同一时间并行执行多项任务)
健壮性(Java语言的强类型机制、异常处理、垃圾的自动收集等)
安全性
字节码:Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。
采用字节码的好处:
Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行。
Java源代码--->编译器--->jvm可执行的Java字节码(即虚拟指令)--->jvm--->jvm中解释器--->机器可执行的二进制机器码--->程序运行。
一个程序中可以有多个类,但只能有一个类是主类。在Java应用程序中,这个主类是指包含main()方法的类。应用程序的主类不一定要求是public类,但小程序的主类要求必须是public类。主类是Java程序执行的入口点。
类:抽象的、概念上的内容。
对象:实实在在存在的一个个体。在生活中,就是摸得着,在编程中,在内存真正的创建了一个对象,占据了一定的内存空间,这就叫实实在在。
对象是类的一个实例。对象是由类派生出来的。(New出来的)
举一个开发的例子:
比如,当我们与控制台去交互时,需要造、需要提供一个类,这个API给我们提供好了,叫Scanner类,这就是一个类的概念,我们真正执行的时候,需要创建这个Scanner的对象,通过这个Scanner的对象去操作功能方法,去完成我们和控制台的交互。
权限修饰符可用来修饰的结构说明:
具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public
1、在类中的位置不同
成员变量:在类中方法外面
局部变量:在方法或者代码块中,或者方法的声明上(即在参数列表中)
2、在内存中的位置不同
成员变量:在堆中(方法区中的静态区)
局部变量:在栈中
3、生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用或者代码块的执行而存在,随着方法的调用完毕或者代码块的执行完毕而消失
4、初始值
成员变量:默认初始值
局部变量:没默认初始值,使用之前需要赋值,否则编译器会报错
规则:
重载:我们可以在同一个类当中,可以声明多个方法名相同,形参列表不同的方法,彼此之间就构成重载。构造器也可以重载。
重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。
子类返回类型小于等于父类方法返回类型, 子类抛出异常小于等于父类方法抛出异常, 子类访问权限大于等于父类方法访问权限。
== :运算符
1.可以使用在基本数据类型变量和引用数据类型变量中
2.如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
补充: == 符号使用时,必须保证符号左右两边的变量类型一致。
equals:
说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
static final 用来修饰属性:全局常量
throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws 属于异常处理的一种方式,声明在方法的声明处。
finally 一定会执行,即使是 catch 中 return 了,catch 中的 return 会等 finally 中的代码执行完之后,才会执行。
实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
构造函数:抽象类可以有构造函数;接口不能有。
实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
访问修饰符:接口中的方法默认使用 public 修饰, public abstract;public static final
抽象类中的方法可以是任意访问修饰符
String(JDK1.0):不可变的字符序列;底层使用char[]存储
StringBuffer(JKD1.0):可变的字符序列、效率低、线程安全;底层使用char[]存储
StringBuilder(JDK5.0):可变的字符序列、效率高、线程不安全;底层使用char[]存储
注意:作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改变其值。
相同点:都与字符串相关的,底层使用char[]存储
扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组.底层创建了一个长度是16的数组。
默认情况下,扩容为原来容量的2倍 + 2,同时将原数组的元素复制到新的数组中.
开发中,若需要频繁的对字符串进行修改,就不要择String,
应根据线程安全情况择StringBuffer或StringBuilder
StringBuffer:当涉及多线程;线程安全问题,推荐使用
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
wait() 、 notify()、notifyAll()
final、finally、finalize三者的区别?
前两个是关键字,finalize是方法名
类似:
throw 和 throws
Collection 和 Collections
String 、StringBuffer、StringBuilder
ArrayList 、 LinkedList
HashMap 、LinkedHashMap
一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。
进程:
线程:
进程可以细化为多个线程。
每个线程,拥有自己独立的:栈、程序计数器
多个线程,共享同一个进程中的结构:方法区、堆。
1.继承Thread类
2.实现Runnable接口
3.实现Callable接口
4.通过Executors工具类创建线程池
runnable 没有返回值,callable 可以拿到有返回值,callable 可以看作是 runnable 的补充。
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
synchronized:同步代码块、同步方法
Lock锁:实例化ReentrantLock,调用lock()、unlock()方法
synchronized 与 Lock 的对比
Lock是显示锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
Lock只有代码块锁,synchronized有代码块锁和方法锁
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
使用的优先顺序:
Lock —> 同步代码块(已经进入了方法体,分配了相应资源 ) —> 同步方法(在方法体之外)
3.利弊
同步的方式,解决了线程的安全问题。—好处
操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。
volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。
volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。
volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。volatile不会造成线程的阻塞;
volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
相同:二者都可以解决线程安全问题
不同:synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(lock(),同时结束同步也需要手动的实现(unlock())
synchronized 早期的实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大,但是在 Java 6 中对 synchronized 进行了非常多的改进。
主要区别如下:
atomic 主要利用 CAS (Compare And Wwap) 和 volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。
线程的状态:
1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
2.不同点:
notifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。
start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次
run():将此线程执行的操作声明在run()中
start():① 启动当前线程 ② 调用当前线程的run()
ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,
而不会影响其它线程所对应的副本。一种以空间换时间的做法 , 在每个Thread 里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了
ThreadLocal 的经典使用场景是数据库连接和 session 管理等
Java 容器分为 Collection 和 Map 两大类,其下又有很多子类,如下所示:
/ConcurrentHashMap
同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
|----Collection接口:单列集合,用来存储一个一个的对象
|----List接口:存储有序的、可重复的数据。 -->“动态”数组,替换原的数组
|----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
|----LinkedList:线程不安全;对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
|----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
ArrayList:jdk 7情况下,在造对象的时候,就已经把底层数组实例化了,底层创建了长度是10的Object[]数组elementData
jdk 8中ArrayList的变化:当调用add()方法时候,才把底层数组创建好,延迟了数组的创建,节省内存空间
数组容量不够,则扩容。默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中(造新的)
Vector的源码分析:JDK7和JDK8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。
集合添加的对象,所在的类要重写equals()方法
我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。
使用两个接口中的任何一个:Comparable 或 Comparator
自然排序:使用Comparable接口,重写了compareTo(obj)方法
像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。
定制排序:使用Comparator接口,重写compare(Object o1,Object o2)方法
如果是Srtring、包装类、Date就可以直接使用排序方法compareTo。若是自定义类需要重写compareTo(obj)方法
Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。
综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。
Array 是数组,必须指定数组的数据类型及数组长度,即数组中存放的元素个数固定并且类型相同;
ArrayList 动态数组,长度动态可变,会自动扩容
Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。
Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。
Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。
HashSet 线程不安全的;可以存储null值,HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,New一个HashSet,就相当于new了一个HashMap,HashSet当调用add()添加元素的时候,相当于把元素放到HashMap的key中,所有的key指向同一个value。因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 存储无序的不可重复的值。
4.1 HashMap在JDK7中实现原理:(重点!)
HashMap map = new HashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[] table。
...可能已经执行过多次put...
map.put(key1,value1):
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1(Entry)添加成功。 ----情况1
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
如果equals()返回false:此时key1-value1添加成功。----情况3
如果equals()返回true:使用value1替换value2。
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
在不断的添加过程中,会涉及到扩容问题,当超出临界值12(且要存放的位置非空)时,扩容。
默认的扩容方式:扩容为原来容量的2倍,并将原的数据复制过来。4.2 HashMap在jdk8中相较于jdk7在底层实现方面的不同:
new HashMap():底层没创建一个长度为16的数组
jdk 8底层的数组是:Node[],而非Entry[]
首次调用put()方法时,底层创建长度为16的数组
jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
4.1 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
4.2 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64 时 —>添加使用红黑树
此时此索引位置上的所有数据改为使用红黑树存储。便于查找,效率高。
红黑树:有序的,二叉排序数;小的放左边,大的放右边。
*当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 < 64 时 —>选择扩容
对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。
Vector、Hashtable、Stack 都是线程安全的,而像 HashMap 则是非线程安全的,不过在 JDK 1.5 之后随着 Java. util. concurrent 并发包的出现,它们也有了自己对应的线程安全类,比如 HashMap 对应的线程安全类就是 ConcurrentHashMap。
1.遍历Collection的两种方式:
① 使用迭代器Iterator ② foreach循环(或增强for循环)
hasNext():判断是否还有下一个元素
next():①指针下移 ②将下移以后集合位置上的元素返回
Iterator 的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出异常
序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
使用ObjectOutputStream实现
反序列化ObjectInputStream过程:将磁盘文件中的对象还原为内存中的一个java对象
使用ObjectInputStream实现
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,
或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
要求:要求类必须实现序列化接口,并且序列化和反序列化必须保持序列化的ID一致
按类型来分:字节流、字符流
按照流的流向分:输入流、输出流
流的角色:节点流、处理流
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
虽然流很多,但是有4个抽象类基类中派生出来的
同步:一定要等任务执行完了,得到结果,才执行下一个任务。 异步:不等任务执行完,直接执行下一个任务。
JSP 是 servlet 技术的扩展,本质上就是 servlet 的简易方式。servlet 和 JSP 最主要的不同点在于,servlet 的应用逻辑是在 Java 文件中,并且完全从表示层中的 html 里分离开来,而 JSP 的情况是 Java 和 html 可以组合成一个扩展名为 JSP 的文件。JSP 侧重于视图,servlet 主要用于控制逻辑。
JSP 有 9 大内置对象:
session 的工作原理是客户端登录完成之后,服务器会创建对应的 session,session 创建完之后,会把 session 的 id 发送给客户端,客户端再存储到浏览器中。这样客户端每次访问服务器时,都会带着 sessionid,服务器拿到 sessionid 之后,在内存找到与之对应的 session 这样就可以正常工作了。
Session 技术,底层其实是基于 Cookie 技术来实现的
可以用,session 只是依赖 cookie 存储 sessionid,如果 cookie 被禁用了,可以使用 url 中添加 sessionid 的方式保证 session 能正常使用。
XSS 攻击:即跨站脚本攻击,它是 Web 程序中常见的漏洞。原理是攻击者往 Web 页面里插入恶意的脚本代码(css 代码、Javascript 代码等),当用户浏览该页面时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的,如盗取用户 cookie、破坏页面结构、重定向到其他网站等。
预防 XSS 的核心是必须对输入的数据做过滤处理。
CSRF:Cross-Site Request Forgery(中文:跨站请求伪造),可以理解为攻击者盗用了你的身份,以你的名义发送恶意请求,比如:以你名义发送邮件、发消息、购买商品,虚拟货币转账等。
防御手段:
301:永久重定向。
302:暂时重定向。
它们的区别是,301 对搜索引擎优化(SEO)更加有利;302 有被提示为网络拦截的风险。
200 表示请求成功
302 表示请求重定向
404 表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误)
500 表示服务器已经收到请求,但是服务器内部错误(代码错误)
forward 是转发 和 redirect 是重定向:
①转发的特点: 转发地址栏路径不变、 转发只能访问当前服务器下的资源、转发是一次请求,可以使用request对象来共享数据。②重定向的特点:地址栏发生变化、重定向可以访问其他站点(服务器)的资源、重定向是两次请求,不能使用request对象来共享数据
tcp 和 udp 是 OSI 模型中的运输层中的协议。tcp 提供可靠的通信传输,而 udp 则常被用于让广播和细节控制交给应用的通信传输。
两者的区别大致如下:
如果采用两次握手,那么只要服务器发出确认数据包就会建立连接,但由于客户端此时并未响应服务器端的请求,那此时服务器端就会一直在等待客户端,这样服务器端就白白浪费了一定的资源。若采用三次握手,服务器端没有收到来自客户端的再此确认,则就会知道客户端并没有要求建立请求,就不会浪费服务器的资源。
tcp 粘包可能发生在发送端或者接收端:
get 请求会被浏览器主动缓存,而 post 不会。
get 传递参数有大小限制,而 post 没有。
get请求:请求行,请求头;post请求:请求行,请求头,空行,请求体(发送给服务器的数据)
get 的参数会明文限制在 url 上,post 不会,post 参数传输更安全。
get和post的区别:
get数据放在url上,即HTTP协议头上;相对不安全,传输快,传输的数据量有限,不能用于文件上传;
post把数据放在HTTP的请求体内(request body);安全,传输慢,传输的数据量可以认为是无限的,可以用于文件上传;
实现跨域有以下几种方案:
jsonp:JSON with Padding,它是利用script标签的 src 连接可以访问不同源的特性,加载远程返回的“JS 函数”来执行的。
答:主要有①MyISAM,是5.5版本之前的默认存储引擎,支持表级锁,不支持事务和外键,并发效率较低,读取数据快,更新数据慢。适合以读操作为主,并且对并发性要求较低的应用。②InnoDB,MySQL目前的默认存储引擎,支持行级锁、事务和外键,并发效率好。适合对事务的完整性和并发性、数据的准确性要求比较高,增删操作多的应用。③Memory,所有的数据都保存在内存中,访问速度快,一旦服务关闭数据将丢失。适合更新不太频繁的数据量小的表用来快速得到访问结果。④Archive、Federated等。
MySQL官方对索引定义:索引(index)是帮助MySQL高效获取数据的数据结构(有序)。索引就类似于书本的目录,是存储引擎用于提高数据库表访问速度的一种数据结构。数据是存储在磁盘上的,查询数据时,如果没有索引,会加载所有的数据到内存,依次进行检索,读取磁盘次数较多。有了索引,就不需要加载所有的数据,因为B+树的高度一般是在2~4层,最多需要读取2-4次磁盘,查询速度提高。
答:①优势:提高数据检索的效率,降低数据库的IO成本。通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。②劣势:实际上索引也是一张表,该表中保存了主键与索引字段,并指向实体类的记录,所以索引列也是要占用空间的。 虽然索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE。因为更新表时,MySQL 不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。
答:①BTREE 索引 : 最常见的索引类型,大部分索引都支持 B 树索引。②HASH 索引:只有Memory引擎支持 , 使用场景简单 。③R-tree 索引(空间索引):空间索引是MyISAM引擎的一个特殊索引类型,相对于BTREE的优势在于范围查找。④Full-text (全文索引) :全文索引也是MyISAM的一个特殊索引类型,主要用于全文索引,InnoDB从Mysql5.6版本开始支持全文索引。
答:①BTree又叫多路平衡搜索树,一颗m叉的BTree特性如下: 树中每个节点最多包含m个孩子。 除根节点与叶子节点外,每个节点至少有[ceil(m/2)]个孩子。若根节点不是叶子节点,则至少有两个孩子。所有的叶子节点都在同一层。每个非叶子节点由n个key与n+1个指针组成,其中[ceil(m/2)-1] <= n <= m-1。②B+Tree为BTree的变种,B+Tree与BTree的区别为: n叉B+Tree最多含有n个key,而BTree最多含有n-1个key。
B+Tree的叶子节点保存所有的key信息,依key大小顺序排列。所有的非叶子节点都可以看作是key的索引部分。③由于B+Tree只有叶子节点保存key信息,查询任何key都要从root走到叶子。所以B+Tree的查询效率更加稳定。
MySql索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。
①单值索引 :即一个索引只包含单个列,一个表可以有多个单列索引。
②唯一索引 :索引列的值必须唯一,但允许有空值。
③复合索引 :即一个索引包含多个列。
答:①对查询频次较高,数据量较大的表创建索引。②限制索引数量:对于增删改操作较多的表,如果索引过多将需要很高的维护代价,降低操作效率,增加操作耗时。③利用最左前缀:如果索引字段值过长,会降低索引的执行效率。④删除不常用索引。⑤使用唯一索引,区分度越高,效率越高。⑤使用短索引,如果索引值很长则占用磁盘变大,会影响效率。⑥为常作为查询条件、经常需要排序、分组和联合操作的字段建立索引。⑦尽量扩展现有索引,联合索引的效率高于多个独立索引。
①复合索引未使用最左列索引时或跳跃使用时失效,例如以name,age和sex字段建立索引,只使用age和sex或只使用name和sex时索引失效。
②在索引上进行运算或函数操作时索引失效。
③数字字符没有加单引号索引失效,因为MySQL查询优化器会自动进行类型转换。
④LIKE以%开头的查询索引失效,%在前时执行计划更倾向于使用全表扫描。
⑤OR的前后没有同时使用索引时索引失效。
⑥当全表扫描比使用索引速度更快时会使用全表扫描。
①读未提交,一个事务会读取到另一个事务没有提交的数据,存在脏读、不可重复读、幻读的问题。
②读已提交,一个事务可以读取到另一个事务已经提交的数据,解决了脏读的问题,存在不可重复读、幻读的问题。
③可重复读,MySQL默认的隔离级别,在一次事务中读取同一个数据结果是一样的,可以解决脏读,不可重复读。
④可串行化,每次读都需要获得表级共享锁,读写互相阻塞,效率低,解决了脏读,不可重复读,幻读问题。
①脏读,一个事务中会读取到另一个事务中还没有提交的数据,读到的是其他事务“更新”的数据;如果另一事务最终回滚了数据,那么所读取到的数据就是无效的。
②不可重复读,一个事务中可以读取到另一个事务中已经提交的数据,一个事务多次读取同一数据,结果不一样。
③幻读,一个事务读取了其他事务还没有提交的数据,当另一个事务在表中插入了一些新数据时再次读取表时会多出几行,如同出现了幻觉。
①Atomicity表示原子性,事务中的所有操作都是不可分割的原子单位,要么全部成功,要么全部失败。
②Consistency表示一致性,无论正常执行还是异常退出,事务执行前后数据的完整性必须保持一致,比如转账前后双方的总金额是不变的。
③Isolation表示隔离性,并发操作中不同事务是互相隔离的,之间不会互相影响。
④Durability表示持久性,事务操作完成后数据就会被持久化修改到永久存储中。
答:①主从复制是指一台服务器充当主数据库服务器,另外一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到从服务器中。对于多级复制,数据库服务器既可充当主机也可充当从机。②MySQL主从复制的基础是主服务器对数据库修改二进制记录,从服务器通过主服务器的二进制日志自动执行更新。
①按操作类型可以分为:读锁(共享锁S)、写锁(排它锁X)。
读锁:对同一份数据,多个读操作可以同时进行而不会互相影响。写锁:当前操作没有完成之前,会阻塞其他读锁和写锁。
②按操作粒度分为:表锁、行锁、页锁。表锁指对当前操作的整张表加锁,实现简单,资源消耗较少。行锁指对某行数据加锁,是一种排它锁。③页锁的锁定粒度介于行锁和表锁之间,一次锁定相邻的一组记录。
MyISAM 只支持表锁,InnoDB 支持表锁和行锁,默认为行锁。
答:①视图(View)是一种虚拟存在的表。视图并不在数据库中实际存在,它的数据来自于表,通过执行时动态生成。通俗的讲,视图就是一条SELECT语句执行后返回的结果集。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。
1、简化sql语句
2、提高了sql的重用性
3、保护基表的数据,提高了安全性
说明:都类似于java中的方法,将一组完成特定功能的逻辑语句包装起来,对外暴露名字
好处:
1、sql语句简单
2、提高重用性
3、减少了和数据库服务器连接的次数,提高了效率
②两者的区别在于存储过程没有返回值,函数必须有返回值
答:①触发器是与表有关的数据库对象,指在 insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性 , 日志记录 , 数据校验等操作 。②使用别名 OLD 和 NEW 来引用触发器中发生变化的记录内容。现在触发器还只支持行级触发,不支持语句级触发。
答:① 连接层:最上层是一些客户端和链接服务,包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于 TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。②服务层:第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如 过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定表的查询的顺序,是否利用索引等, 最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存,如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。③ 引擎层:存储引擎层, 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。④存储层:数据存储层, 主要是将数据存储在文件系统之上,并完成与存储引擎的交互。
①在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。以下是几种常用的存储引擎的使用环境。
②InnoDB : 是Mysql的默认存储引擎,用于事务处理应用程序,支持行级锁和外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询以外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。InnoDB存储引擎除了有效的降低由于删除和更新导致的锁定, 还可以确保事务的完整提交和回滚,对于类似于计费系统或者财务系统等对数据准确性要求比较高的系统,InnoDB是最合适的选择。
③MyISAM : 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
④MEMORY:将所有数据保存在RAM中,在需要快速定位记录和其他类似数据环境下,可以提供几块的访问。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,其次是要确保表的数据可以恢复,数据库异常终止后表中的数据是可以恢复的。MEMORY表通常用于更新不太频繁的小表,用以快速得到访问结果。⑤MERGE:用于将一系列等同的MyISAM表以逻辑方式组合在一起,并作为一个对象引用他们。MERGE表的优点在于可以突破对单个MyISAM表的大小限制,并且通过将不同的表分布在多个磁盘上,可以有效的改善MERGE表的访问效率。这对于存储诸如数据仓储等VLDB环境十分合适。
答:①查看SQL执行频率。②定位低效率执行SQL。可以通过以下两种方式:慢查询日志 : 通过慢查询日志定位那些执行效率较低的 SQL 语句。show processlist
: 慢查询日志在查询结束以后才记录,所以在应用反映执行效率出现问题的时候查询慢查询日志并不能定位问题,可以使用show processlist
命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化。③通过以上步骤查询到效率低的 SQL 语句后,可以通过 EXPLAIN或者 DESC命令获取 MySQL如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。④Mysql从5.0.37版本开始增加了对 show profiles
和 show profile
语句的支持。show profiles
能够在做SQL优化时帮助我们了解时间都耗费到哪里了。⑤MySQL5.6提供了对SQL的跟踪trace, 通过trace文件能够进一步了解为什么优化器选择A计划, 而不是选择B计划。打开trace , 设置格式为 JSON,并设置trace最大能够使用的内存大小,避免解析过程中因为默认内存过小而不能够完整展示。
数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。
B-树,这里的 B 表示 balance( 平衡的意思),B-树是一种多路自平衡的搜索树(B树是一颗多路平衡查找树)
它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。
B-树有如下特点:
B+树是B-树的变体,也是一种多路搜索树, 它与 B- 树的不同之处在于:
1.B+树内节点不存储数据,所有 data 存储在叶节点导致查询时间复杂度固定为 log n。而B-树查询时间复杂度不固定,与 key 在树中的位置有关,最好为O(1)。
2. B+树叶节点两两相连可大大增加区间访问性,可使用在范围查询等,而B-树每个节点 key 和 data 在一起,则无法区间查找。
3.B+树更适合外部存储。由于内节点无 data 域,每个节点能索引的范围更大更精确
B树和B+树都是多叉树,是改变二叉树结构的高度较大进行优化的(树的高度较大不适合存储海量数据);但两者之间也有不同,Mysql选择B+树作为存储的数据结构。
B+树能显著减少IO次数,提高效率
B+树的查询效率更加稳定,因为数据放在叶子节点
B+树能提高范围查询的效率,因为叶子节点指向下一个叶子节点
组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
不同虚拟机的运行时数据区可能略微有所不同,但都会遵从 Java 虚拟机规范, Java 虚拟机规范规定的区域分为以下 5 个部分:
堆是用来存放对象实例的,是垃圾收集器管理的主要区域,因此也被称为GC堆。
堆可以细分为:新生代(Eden、From Survivor、To Survivor)和老年代
队列和栈都是被用来预存储数据的。
队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。
栈和队列很相似,但它运行对元素进行后进先出进行检索。
1、启动类加载器,负责加载JAVA_HOME/lib中的类库;
2、扩展类加载器,负责加载JAVA_HOME/lib/ext中的类库;
3、应用程序类加载器,也称系统类加载器,负责加载用户类路径上指定的类库;
4、其他类加载器
②类加载器之间的层次关系叫做双亲委派模型,要求除了顶层的启动类加载器外其余的类加载器都应当有自己的父类加载器。一个类收到类加载请求后会层层找父类加载器去尝试加载,因此所有的加载请求最终都会被传送到顶层的启动类加载器,只有当父类加载器反馈自己无法完成加载时子加载器才会尝试自己去加载。③双亲委派模型的好处是保障类加载的唯一性和安全性,例如加载rt.jar包中的java.lang.Object,无论哪一个类加载最终都会委托给启动类加载器,这样就保证了类加载的唯一性。如果存在包名和类名都相同的两个类,那么该类就无法被加载。
类装载分为以下 5 个步骤:
一般有两种方法来判断:
CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。
CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。
新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。
分代回收器有两个分区:新生代和老年代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。
新生代使用的是复制算法,新生代里有 3 个分区:Eden、From Survivor、To Survivor,它们的默认占比是 8:1:1,它的执行流程如下:
每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。
老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。
JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。
①IOC即控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理,作用是降低代码的耦合度。IOC底层原理:XML解析,工厂模式,反射。
②DI即依赖注入,也就是注入属性,是IOC的一种具体实现方式。假设一个Car类需要Engine的对象,那么一般需要new一个Engine,利用IOC就是只需要定义一个私有的Engine引用变量,容器会在运行时创建一个Engine的实例对象并将引用自动注入给变量。
③AOP面向切面,不通过修改源代码方式,在主干功能里面添加新功能。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得
业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(1)Spring对bean进行实例化
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(4)bean 可以使用了(对象获取到了)
(5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
当有bean 的后置处理器,bean 生命周期有七步
(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
可以注入的数据类型有基本数据类型、String、Bean、以及集合等复杂数据类型
构造方法注入,通过constructor-arg
标签实现
set 属性注入,通过property
标签实现
注解方式注入,@Autowired根据类型注入,@Qualifier根据名称进行注入,
@Resource可以根据类型注入,可以根据名称注入,@Value注入普通类型属性
spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
实际上大部分时候 spring bean 无状态的(比如 dao 类),所以某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。
spring 支持 5 种作用域,如下:
Web 环境下的作用域:
1、Spring 提供 IOC 容器实现两种方式:(两个接口)
(1)BeanFactory:IOC 容器基本实现,是一个Factory接口,是 Spring 内部的使用接口,不提供开发人员进行使用
加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
(2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
加载配置文件时候就会把在配置文件对象进行创建,一般推荐使用ApplicationContext。
2、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
普通 bean:在配置文件中定义 bean 类型就是返回类型
工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
就是你定义一个工厂 然后就可以加工东西了 不用频繁的去xml文件中定义
FactoryBean是一个Bean接口,是一个可以生产或者装饰对象的工厂Bean,可以通过实现该接口自定义的实例化Bean的逻辑
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:
ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
ISOLATIONREADUNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
ISOLATIONREADCOMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
ISOLATIONREPEATABLEREAD:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。
不可重复读 :是指在一个事务内,多次读同一数据。
幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。
①springMVC是一种基于Java实现的mvc设计模型的轻量级Web层框架,作用包括:获取请求参数、调用业务层 、进行请求响应。
②mvc全名是model view controller模型视图控制器,model指数据模型,JavaBean的类,用来封装数据;view指jsp,html等用来展示数据给用户的界面;controller是整个流程的控制器,用来接收用户请求以及进行数据校验等功能。
①在pom.xml中导入以下jar包:org.springframework下的spring-context)、spring-web、spring-webmvc、javax.servlet下的servlet-api、javax.servlet.jsp下的jsp-api以及测试用的junit包。②创建一个springmvc.xml的springconfig配置文件,开启包扫描,注册视图解析器,配置视图的前缀和后缀。③在web.xml中配置核心控制器,servlet和servlet-mapping的映射等。
作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
作用:根据请求的url、method等信息查找Handler(将请求和控制器方法映射),即控制器方法
作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理
作用:通过HandlerAdapter对处理器(控制器方法)进行执行
作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView
作用:将模型数据通过页面展示给用户
(1)用户发送请求到前端控制器。
(2)中央调度器将请求转给处理器映射器。
(3)处理器映射器获取能处理该请求的处理器对象,并将其放到处理器执行链中,返回给中央调度器。
(4)中央调度器根据处理器执行链中的处理器对象找到能够处理该请求的处理器适配器。
(5)处理器映射器根据URL找到需要调用的处理器(Controller)。
(6)Controller 进行业务逻辑处理后,得到一个ModelAndView,将其返回给处理器适配器。
(7)处理器适配器再将这个ModelAndView返回给中央调度器。
(8)中央调度器将ModelAndView传给视图解析器。
(9)视图解析器获取ModelAndView中的数据、解析ModelAndView中的视图、通过前缀和后缀拼接视图的完整路径,再将数据渲染到 这个视图上,封装为一个视图对象,并将其返回给中央调度器。
(10)中央调度器根据这个视图对象调用具体的视图,形成响应对象。中央调度器响应浏览器。
将 http 请求映射到相应的类/方法上。
@Autowired 它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作,通过@Autowired 的使用来消除 set/get 方法。
@RequestParam是将请求参数和控制器方法的形参创建映射关系
@RequestHeader是将请求头信息和控制器方法的形参创建映射关系
@CookieValue是将cookie数据和控制器方法的形参创建映射关系,就一个值JSESSIONID
@RequestBody:用于标识控制器方法形参,标识的形参就可以获取请求体。
@ResponseBody:用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
@RestController:@Controller+@ResponseBody,当前控制器所有的控制器方法,所有的返回值都是作为响应浏览器的响应体存在
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
postHandle:控制器方法执行之后执行postHandle()
afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()
#{}
是预编译处理,${}
是字符替换。 在使用 #{}
时,MyBatis 会将 SQL 中的 #{}
替换成“?”,配合 PreparedStatement 的 set 方法赋值,这样可以有效的防止 SQL 注入,保证程序的运行安全。
分页方式:逻辑分页和物理分页。
逻辑分页: 使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。
物理分页: 自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式。
MyBatis 支持延迟加载,设置 lazyLoadingEnabled=true 即可。
延迟加载的原理的是调用的时候触发加载,而不是在初始化的时候就加载信息。延迟加载就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。
Mybatis的缓存分为:
①一级缓存:指的是Mybatis中SqlSession对象的缓存,当我们执行完查询结果后,查询的结果会同时存入到SqlSession为我们提供的一块区域中,该区域的结构是一个Map,当我们再次查询同样数据时,Mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。当SqlSession对象消失时,Mybatis的一级缓存也就消失了。一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
②二级缓存:二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
开启二级缓存数据查询流程:二级缓存 -> 一级缓存 -> 数据库。
缓存更新机制:当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
MyBatis 有三种基本的Executor执行器:
分页插件的基本原理是使用 MyBatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 SQL,然后重写 SQL,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。
spring boot 是为 spring 服务的,是用来简化新 spring 应用的初始搭建以及开发过程的。
spring boot 核心的两个配置文件:
配置文件有 . properties 格式和 . yml 格式,它们主要的区别是书法风格不同。
. properties 配置如下:
spring. RabbitMQ. port=5672
. yml 配置如下:
spring:
RabbitMQ:
port: 5672
. yml 格式不支持 @PropertySource 注解导入。
Springboot是通过main方法下的SpringApplication.run方法启动的,启动的时候他会调用refshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式注册bean,
而它是通过启动类的SpringBootApplication注解进行开始解析的,标注这个类是一个springboot的应用:启动类下的所有资源被导入,SpringBootApplication里面有三个注解@Springbootconfiguration说明主函数他也是一个配置类(核心配置类);@ComponentScan指定扫描哪些包,扫描当前主启动类同级的包;@EnableAutoConfiguration根据EnableAutoConfiguration开启自动化配置,里面又有两个注解,@AutoConfigurationPackage自动配置包,给容器中导入一系列组件,只有在主函数所在的包下才导入,这就解释了为什么默认的包路径是Main所在的包下;@importSelect自动导入选择器选择性的导入,根据loadFactoryNames根据classpash路径以MATA-INF/spring.factorces(自动配置核心文件)所在的位置来加载一个文件,就是spring-boot-autoconfigure所有的自动配置类都在里面。下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解(@Conditional),最终会按需配置。先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器,只有满足条件装配规则的才会被配置。