java知识总结

JAVA

基础

java中SFunction<T, ?>
在Java中,SFunction field表示一个泛型函数式接口,其中T为输入类型,?表示输出类型可以为任意类型。该接口通常用于实现高阶函数,例如将一个函数作为参数传递给另一个函数。
接口补偿:
接口补偿机制,是指在软件开发过程中,当两个或多个软件模块之间的接口出现问题时,通过一定的补偿措施来解决问题的机制。这种机制可以有效地提高软件开发的效率和质量,减少因接口问题而导致的软件开发延误和成本增加。
在实际操作中,例如调用B服务的api接口异常时,需要对异常进行日志记录并进行相应的补偿处理。此外,“事务补偿”和“重试”都可以被视为是“补偿”的一种形式。前者是一个逆向操作,用于撤销之前的操作,而后者则是一个正向操作,用于重新执行操作。
方法:
1.定义注解切面重试方案;
模板方法;切面方式、、定时任务、消息队列重试、成熟的重试组件spring-retry
**重试机制:**当某个节点出现错误时,可以通过多次重试来尝试重新发送数据,直到成功为止。
**异常捕获机制:**通过捕获异常信息,及时发现和处理错误,避免错误的传播和扩大化。
数据备份机制:在分布式系统中,进行数据备份是非常重要的,可以通过多个节点进行数据备份,保证在某个节点出现故障时,数据可以从备份节点中恢复。
数据一致性机制:在分布式系统中,保证数据一致性是非常关键的,可以通过分布式一致性算法(如Paxos、Raft)来保证数据的一致性和可靠性。
事务回滚机制:当某些操作出现错误时,可以通过事务回滚机制将数据恢复到操作前的状态,保证数据的准确性和完整性。

1、JDK 和 JRE 有什么区别?

JDK(Java Development Kit),Java开发工具包

JRE(Java Runtime Environment),Java运行环境

JDK中包含JRE,JDK中有一个名为jre的目录,里面包含两个文件夹bin和lib(核心类库),bin就是JVM,lib就是JVM工作所需要的类库。

2、== 和 equals 的区别是什么?

对于基本类型,==比较的是值;

对于引用类型,==比较的是地址;

equals不能用于基本类型的比较;

如果没有重写equals,equals就相当于==;

如果重写了equals方法,equals比较的是对象的内容;

3、final 在 java 中有什么作用?

(1)用来修饰一个引用

如果引用为基本数据类型,则该引用为常量,该值无法修改;
如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象或数组的地址的引用不能修改。
如果引用时类的成员变量,则必须当场赋值,否则编译会报错。
(2)用来修饰一个方法

当使用final修饰方法时,这个方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承。

(3)用来修饰类

当用final修改类时,该类成为最终类,无法被继承。

比如常用的String类就是最终类。

4、java 中的 Math.round(-1.5) 等于多少?

Math提供了三个与取整有关的方法:ceil、floor、round

(1)ceil:向上取整;

Math.ceil(11.3) = 12;

Math.ceil(-11.3) = 11;

(2)floor:向下取整;

Math.floor(11.3) = 11;

Math.floor(-11.3) = -12;

(3)round:四舍五入;

加0.5然后向下取整。

Math.round(11.3) = 11;

Math.round(11.8) = 12;

Math.round(-11.3) = -11;

Math.round(-11.8) = -12;

5、String 属于基础的数据类型吗?

不属于。

八种基本数据类型:byte、short、char、int、long、float、double、boolean。

6、String str="i"与 String str=new String(“i”)一样吗?

String str="i"会将起分配到常量池中,常量池中没有重复的元素,如果常量池中存中i,就将i的地址赋给变量,如果没有就创建一个再赋给变量。

String str=new String(“i”)会将对象分配到堆中,即使内存一样,还是会重新创建一个新的对象。

7、如何将字符串反转?

将对象封装到stringBuilder中,调用reverse方法反转。

8、String 类的常用方法都有那些?

(1)常见String类的获取功能

length:获取字符串长度;
charAt(int index):获取指定索引位置的字符;
indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引;
substring(int start):从指定位置开始截取字符串,默认到末尾;
substring(int start,int end):从指定位置开始到指定位置结束截取字符串;

(2)常见String类的判断功能

equals(Object obj): 比较字符串的内容是否相同,区分大小写;
contains(String str): 判断字符串中是否包含传递进来的字符串;
startsWith(String str): 判断字符串是否以传递进来的字符串开头;
endsWith(String str): 判断字符串是否以传递进来的字符串结尾;
isEmpty(): 判断字符串的内容是否为空串"";

(3)常见String类的转换功能

byte[] getBytes(): 把字符串转换为字节数组;
char[] toCharArray(): 把字符串转换为字符数组;
String valueOf(char[] chs): 把字符数组转成字符串。valueOf可以将任意类型转为字符串;
toLowerCase(): 把字符串转成小写;
toUpperCase(): 把字符串转成大写;
concat(String str): 把字符串拼接;

(4)常见String类的其他常用功能

replace(char old,char new) 将指定字符进行互换
replace(String old,String new) 将指定字符串进行互换
trim() 去除两端空格
int compareTo(String str) 会对照ASCII 码表 从第一个字母进行减法运算 返回的就是这个减法的结果,如果前面几个字母一样会根据两个字符串的长度进行减法运算返回的就是这个减法的结果,如果连个字符串一摸一样 返回的就是0。

9、new String(“a”) + new String(“b”) 会创建几个对象?6个

对象1:new StringBuilder()

对象2:new String(“a”)

对象3:常量池中的"a"

对象4:new String(“b”)

对象5:常量池中的"b"

深入剖析:StringBuilder中的toString():

对象6:new String(“ab”)

强调一下,toString()的调用,在字符串常量池中,没有生成"ab"
总结:String str1 = new String(“a”) + new String(“b”);

// 例str1=当a和b在常量池不存在总共新建6个;

  1. 对象1和2:如果字符串常量池中不存在字符串,则新建一个对象,存在字符串则不创建;(对象还是在堆中创建,字符串常量池只放引用)

​ 2.对象3和4 new String()会创建一个对象;

10.String StringBuffer StringBuilder的区别是什么?

1**.可变性** String 内部是final修饰,类是不可变的,每次修改String 值的时候,都会产生一个新的对象;而StringBuffer StringBuilder是一个字符串可变的类,不会产生新对象。

2.安全性:String 是final修饰,是一个不可变得类,所以他是线程安全的;而StringBuffer内部方法都是使用synchronized修饰,所以也是线程安全;StringBuilder线程不安全,多线程的情况下需要操作字符串变更使用StringBuffer;

3.性能方面:String 是final修饰,因为不可变,做字符串或者拼接的时候需要创建新的对象并分配内容,需要消耗更多的内存资源;接着StringBuffer比String性能要好,因为字符可变,不会创建新对象,从而节省资源,所以速度更快;最后是StringBuilder,它的性能最快,以为没有加锁,所以性能方面比StringBuilder更快。

4.存储方面:String存储在字符串常量池;而StringBuffer StringBuilder是存储在内存空间的。

最后补充一点:StringBuffer StringBuilder都继承AbstractStringBuilder

11.怎样声明一个类不会被继承,什么场景下会用?

​ 如果一个类被fnal修饰,此类不可以有子类,不能被其它类继承,如果一个中的所有方法都没有重写的需要,当前类没有子类也罢,就可以使用final修饰类。Math类

*12.接口和抽象类有什么区别?

(1)接口

接口使用interface修饰;
接口不能实例化;
类可以实现多个接口;

①java8之前,接口中的方法都是抽象方法,省略了public abstract。

②java8之后;接口中可以定义静态方法,静态方法必须有方法体,普通方法没有方法体,需要被实现;

3.如果使用@FunctionalInterface修饰,则这个函数接口必需有且只有一个抽象方法;可以有多个静态方法;可以有多个default方法(默认方法)。

(2)抽象类、

抽象类使用abstract修饰;
抽象类不能被实例化;
抽象类只能单继承;
抽象类中可以包含抽象方法和非抽象方法,非抽象方法需要有方法体;
如果一个类继承了抽象类,
①如果实现了所有的抽象方法,子类可以不是抽象类;
②如果没有实现所有的抽象方法,子类仍然是抽象类。

1. 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
2. 接口中除了 static、final 变量,不能有其他变量,而抽象类中则不一定。
3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展
多个接口。
4. 接口方法默认修饰符是 public,抽象方法可以有 public、protected 和 default 这些修饰符(抽象
方法就是为了被重写所以不能使用 private 关键字修饰!)。
5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
```
1. 在 jdk 7 或更早版本中,接口里面只能有常量变量和抽象方法。这些接口方法必须由选择实现接口的类实现。
2. jdk8 的时候接口可以有默认方法功能。
3. Jdk 9 在接口中引入了私有方法和私有静态方法。
```

*13.Java对象有那些特征?

封装(Encapsulation):
封装是指将数据(属性)和操作数据的方法(行为)包装在一个类中,对外部隐藏类的实现细节,只提供公共的访问方式。这样可以保护类的内部数据不被外部直接访问和修改,提高了代码的安全性和可维护性。

继承(Inheritance):
继承是指一个类(子类)可以继承另一个类(父类)的属性和方法,子类可以直接使用父类的属性和方法,而不需要重新编写。这样可以实现代码的复用,提高开发效率。同时,子类还可以覆盖或扩展父类的方法,以满足特定的需求。

  • 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法
  • 子类是无法访问,只是拥有
  • 子类可以拥有自己属性和方法,即子类可以对父类进行扩展
  • 子类可以用自己的方式实现父类的方法。

多态:
指同一个实体同时具有多种形式。多态性的实现需要满足三个关键条件:继承、重写和向上转型。首先,多态的存在离不开类的继承关系,这包括子类对父类或接口的继承。其次,子类对父类中的某些方法进行重新定义,即重写,这样在调用这些方法时就会调用子类的方法。最后,向上转型是指将父类或接口的引用指向子类的对象,这样这个引用就能够调用父类和子类的方法。

父类引用指向子类对象,父类对象类型不可变,子类引用类型可变;如:Animal a = new Cat();
多态中,编译看左边,运行看右边;引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;

14.java 中 IO 流分为几种?

(1)按流划分,可以分为输入流和输出流;

(2)按单位划分,可以分为字节流和字符流;

(3)按照流的角色划分为节点流和处理流。

信息的最小存储单元都是字节

字节流:inputStream、outputStream;

字符流:reader、writer;

InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。

OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
缓冲流:
1.缓冲流,也被称为高效流,是Java IO流中的一种,主要用来提高数据的读写速度和减少IO次数。它分为字节缓冲流和字符缓冲流两种类型。
2.在创建流对象的时候,缓冲流会创建一个内置默认大小的缓冲区数组,这样在读取或者写入数据的时候,就可以直接在缓冲区中进行,从而减少了对物理设备的访问次数,提高了效率
3.缓冲流的构造方法包括:BufferedInputStream、BufferedOutputStream用于字节缓冲流;BufferedReader、BufferedWriter用于字符缓冲流。
序列化流:
1.在Java中,序列化流主要用于将对象转换为字节流,从而可以保存到磁盘上,或通过网络进行传输,或者存储在内存中供以后使用。序列化和反序列化是对象的写入和读取过程。序列化是通过实现java.io.Serializable接口来完成的,该接口是一个标记接口,没有任何方法,只是用来标识类可以被序列化。
2.序列化流主要包括对象输出流ObjectOutputStream和对象输入流ObjectInputStream。其中,ObjectOutputStream用于将Java对象的基本数据类型和图形写入OutputStream;而ObjectInputStream则用于从InputStream中读取字节流,并将其转换回Java对象。

15.BIO、NIO、AIO 有什么区别?

(1)同步阻塞BIO

一个连接一个线程。

JDK1.4之前,建立网络连接的时候采用BIO模式,先在启动服务端socket,然后启动客户端socket,对服务端通信,客户端发送请求后,先判断服务端是否有线程响应,如果没有则会一直等待或者遭到拒绝请求,如果有的话会等待请求结束后才继续执行。

(2)同步非阻塞NIO

NIO主要是想解决BIO的大并发问题,BIO是每一个请求分配一个线程,当请求过多时,每个线程占用一定的内存空间,服务器瘫痪了。

JDK1.4开始支持NIO,适用于连接数目多且连接比较短的架构,比如聊天服务器,并发局限于应用中。

一个请求一个线程。

(3)异步非阻塞AIO

一个有效请求一个线程。

JDK1.7开始支持AIO,适用于连接数目多且连接比较长的结构,比如相册服务器,充分调用OS参与并发操作。

16.Files的常用方法都有哪些?

exist
createFile
createDirectory
write
read
copy
size
delete
move

17.什么是反射?

所谓反射,是java在运行时进行自我观察的能力,通过class、constructor、field、method四个方法获取一个类的各个组成部分

在Java运行时环境中,对任意一个类,可以知道类有哪些属性和方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于反射机制。

*18.什么是 java 序列化?什么情况下需要序列化?

序列化就是一种用来处理对象流的机制。将对象的内容流化,将流化后的对象传输于网络之间

序列化是通过实现serializable接口,该接口没有需要实现的方法,implement Serializable只是为了标注该对象是可被序列化的,

使用一个输出流(FileOutputStream)来构造一个ObjectOutputStream对象,

接着使用ObjectOutputStream对象的writeObejct(Object object)方法就可以将参数的obj对象到磁盘,需要恢复的时候使用输入流。

ObjectOutputStream采用默认的序列化方式,对对象的非transient的实例变量进行序列化。
ObjcetInputStream采用默认的反序列化方式,对对对象的非transient的实例变量进行反序列化。

序列化是将对象转换为容易传输的格式的过程。

例如,可以序列化一个对象,然后通过HTTP通过Internet在客户端和服务器之间传输该对象。在另一端,反序列化将从流中心构造成对象。

一般程序在运行时,产生对象,这些对象随着程序的停止而消失,但我们想将某些对象保存下来,这时,我们就可以通过序列化将对象保存在磁盘,需要使用的时候通过反序列化获取到。

对象序列化的最主要目的就是传递和保存对象**,保存对象的完整性和可传递性**。

譬如通过网络传输或者把一个对象保存成本地一个文件的时候,需要使用序列化。

可序列化对象为什么要定义serialversionUID值?

SerialVersionUid,简言之,其目的是序列化对象版本控制,有关各版本反序列化时是否兼容。如果在新版本中这个

值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassException异常。如果修改较小,比如仅仅是增加

一个属性,我们希望向下兼容,老版本的数据都能保留,那就不用修改;如果我们删除一个属性,或者更了类

继承关系,必然不兼容旧数据,这时就应该手动更新版本号,即SerialVersionUid。

说白了就是为了一个兼容性判断

19.为什么要使用克隆?如何实现对象克隆?深拷贝和浅拷贝区别是什么?

1)什么要使用克隆?

想对一个对象进行复制,又想保留原有的对象进行接下来的操作,这个时候就需要克隆了。

(2)如何实现对象克隆?

实现Cloneable接口,重写clone方法;
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆。
BeanUtils,apache和Spring都提供了bean工具,只是这都是浅克隆。

(3)深拷贝和浅拷贝区别是什么?

浅拷贝:仅仅克隆基本类型变量,不克隆引用类型变量;
深克隆:既克隆基本类型变量,又克隆引用类型变量;

实现深克隆方法:

1.使用开源工具Json工具:Gson的使用;

2.使用序列化流

3.使用重写后的clone方法

20.throw 和 throws 的区别?

(1)throw

作用在方法内,表示抛出具体异常,由方法体内的语句处理;
一定抛出了异常;

(2)throws

作用在方法的声明上,表示抛出异常,由调用者来进行异常处理
可能出现异常,不一定会发生异常;

21.final、finally、finalize 有什么区别?

final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写

finally用于抛异常,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。

finalize方法用于垃圾回收。

一般情况下不需要我们实现finalize,当对象被回收的时候需要释放一些资源,比如socket链接,在对象初始化时创建,整个生命周期内有效,那么需要实现finalize方法,关闭这个链接。

但是当调用finalize方法后,并不意味着gc会立即回收该对象,所以有可能真正调用的时候,对象又不需要回收了,然后到了真正要回收的时候,因为之前调用过一次,这次又不会调用了,产生问题。所以,不推荐使用finalize方法。

小结:需要释放资源的有:文件流要释放;数据库连接资源要释放;在结合使用hibernate时,session要释放

22.常见的异常类有哪些?

RuntimeException

NullPointerException:空指针异常;

IndexOutOfBoundsException:数组下角标越界异常;

ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出此异常;

NumberFormatException:数字格式转换异常;
IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数
ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。

ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常;

非RuntimeException异常

NoSuchMethodException:无法找到某一方法时,抛出;

NoSuchFieldException(属性不存在异常)

SQLException:数据库相关的异常;

IOException:当发生某种IO异常时抛出;

FileNotFoundException:打开文件失败时抛出;

ClassNotFoundException(找不到类)

25.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

会执行,在return返回之前执行;

*26.hashcode是什么?有什么作用?

哈希码(hashcode)Java中Object有一个方法,是Java中一个用于标识对象的唯一标识符。它由对象的内部地址、对象的类和对象属性信息计算得出,保证了在程序执行期间,只要对象的属性值不发生改变,该对象的哈希码值就不会改变。

Java中的哈希码具有重要的用途,例如为哈希表提供支持。当equals方法判断两个对象相等时,它们的hashCode方法也必须返回相同的整数结果。这样的设计使得我们可以将对象存储在哈希表中,以优化查找性能。

值得注意的是,hashCode的常规约定是:在同一对象上多次调用hashCode方法时,必须一致地返回相同的整数值,前提是对象上equals比较所用的信息没有被修改。然而,从某一应用程序的一次执行到同一应用程序的另一次执行,这个整数无需保持一致。
public native int hashcode();

(1)hashcode()方法的作用:**哈希码的作用是确定该对象在哈希表中的索引位置。**Object 的 hashcode 方法是本地方法

hashcode()方法主要配合基于散列的集合一起使用,比如HashSet、HashMap、HashTable。

2)为什么要有hashCode?

我们以“ HashSet 如何检查重复”为例子来说明为什么要有 hashCode?

当你把对象加入 HashSet 时, HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时

也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode, HashSet 会假设对

象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals()方法来检查

hashcode 相等的对象是否真的相同。如果两者相同, HashSet 就不会让其加入操作成功。如果不同的

话,就会重新散列到其他位置。

3)为什么重写 equals 时必须重写 hashCode 方法?

如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都

返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆

盖过,则 hashCode 方法也必须被覆盖。

4)为什么两个对象有相同的 hashcode 值,它们也不一定是相等的?

因为 hashCode() 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越

容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的hashCode) 。
transient修饰符的作用是什么?它与序列化有何关系?
transient修饰符的主要作用是让某些被它修饰的成员属性变量在序列化过程中不被序列化。当一个类实现了Serializable接口进行序列化时,如果某个属性不需要被序列化,可以使用transient关键字进行修饰。
volatile修饰符的作用是什么?它与原子性有何关系?
Volatile是Java中的一个关键字,是一个变量修饰符,主要用于修饰会被不同线程访问和修改的变量。当一个变量被volatile修饰,那么这个变量的变化对其他线程来说是可见的,即一个线程修改了该变量的值,其它线程能够立即看到修改的值。
虽然volatile关键字不能保证操作的原子性,但它仍然被广泛用于提高程序的并发性能。因为volatile关键字相比于synchronized(synchronized通常称为重量级锁),它更轻量级,不会引发线程上下文的切换和调度。所以,在不需要事务完整性(原子性)、持久性或者有序性的情况下,可以使用volatile来提高性能。

27.java 中操作字符串都有哪些类?它们之间有什么区别?

(1)String

String是不可变对象,每次对String类型的改变时都会生成一个新的对象。

(2)StringBuilder

线程不安全,效率高,多用于单线程。

(3)StringBuffer

线程安全,由于加锁的原因,效率不如StringBuilder,多用于多线程。

不频繁的字符串操作使用String,操作频繁的情况不建议使用String。

StringBuilder > StringBuffer > String。

28.java 中都有哪些引用类型?

(1)强引用

Java中默认声明的就是强引用,比如:

Object obj = new Object();
obj = null;

只要强引用存在,垃圾回收器将永远不会回收被引用的对象。如果想被回收,可以将对象置为null;

(2)软引用(SoftReference)

内存足够的时候,软引用不会被回收,只有在内存不足时,系统才会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会跑出内存溢出异常。

byte[] buff = new byte[1024 * 1024];
SoftReference<byte[]> sr = new SoftReference<>(buff);

(3)弱引用(WeakReference)

进行垃圾回收时,弱引用就会被回收。

(4)**虚引用(**PhantomReference)

​ 虚引用PhantomReference作用:跟踪垃圾回收器收集对象的活动,在GC的过程中,GC会将虚引用放到引用队列中;当程序员调用ReferenceQueue.pull()方法的时候,会将虚引用对象变成不活动(Inactive)状态。后续将被GC回收。

(5)引用队列(ReferenceQueue)

引用队列可以与软引用、弱引用、虚引用一起配合使用。

当垃圾回收器准备回收一个对象时,如果发现它还有引用,就会在回收对象之前,把这个引用加入到引用队列中。

程序可以通过判断引用队列中是否加入了引用,来判断被引用的对象是否将要被垃圾回收,这样可以在对象被回收之前采取一些必要的措施。

29.在 Java 中,为什么不允许从静态方法中访问非静态变量?

静态变量属于类本身,在类加载的时候就会分配内存,可以通过类名直接访问;
非静态变量属于类的对象,只有在类的对象产生时,才会分配内存,通过类的实例去访问;
静态方法也属于类本身,但是此时没有类的实例,内存中没有非静态变量,所以无法调用
————————————————

30.说说Java Bean的命名规范

JavaBean 类必须是一个公共类,并将其访问属性设置为 public
JavaBean 类必须有一个空的构造函数:类中必须有一个不带参数的公用构造器,此构造器也应该通过调用各个特性的设置方法来设置特性的缺省值。
一个javaBean类不应有公共实例变量,类变量都为private
持有值应该通过一组存取方法(getXxx 和 setXxx)来访问:对于每个特性,应该有一个带匹配公用 getter 和 setter 方法的专用实例变量。
属性为布尔类型,可以使用 isXxx() 方法代替 getXxx() 方法。

通常属性名是要和 包名、类名、方法名、字段名、常量名作出区别的:

首先:必须用英文,不要用汉语拼音

(1)包(package)

用于将完成不同功能的类分门别类,放在不同的目录(包)下,包的命名规则:将公司域名反转作为包名。比如www.sohu.com 对于包名:每个字母都需要小写。比如:com.sohu.test;该包下的Test类的全名是:com.sohu.Test.Java 。

如果定义类的时候没有使用package,那么java就认为我们所定义的类位于默认包里面(default package)。

(2)类

首字母大写,如果一个类由多个单词构成,那么每个单词的首字母都大写,而且中间不使用任何的连接符。尽量使用英文。如ConnectionFactory

(3)方法

首单词全部小写,如果一个方法由多个单词构成,那么从第二个单词开始首字母大写,不使用连接符。addPerson

(4)字段

与方法相同。如ageOfPerson

(5)常量

所有单词的字母都是大写,如果有多个单词,那么使用下划线链接即可。

如:public static final int AGE_OF_PERSON = 20; //通常加上static

\1. javabean属性命名尽量使用常规的驼峰式命名规则
\2. 属性名第一个单词尽量避免使用一个字母:如eBook, eMail。
\3. boolean属性名避免使用 “is” 开头的名称

————————————————

*31.什么是 Java 的内存模型?

在了解什么是 Java 内存模型之前,先了解一下为什么要提出 Java 内存模型。

之前提到过并发编程有三大问题

  1. CPU 缓存,在多核 CPU 的情况下,带来了可见性问题

  2. 操作系统对当前执行线程的切换,带来了原子性问题

  3. 译器指令重排优化,带来了有序性问题

为了解决并发编程的三大问题,提出了 JSR-133,新的 Java 内存模型,JDK 5 开始使用。

1.原子性
一个或多个操作,要么全部执行,要么全部不执行(执行的过程中是不会被任何因素打断的)。
2.可见性
只要有一个线程对共享变量的值做了修改,其他线程都将马上收到通知,立即获得最新值。
3.有序性
有序性可以总结为:在本线程内观察,所有的操作都是有序的;而在一个线程内观察另一个线程,所有操作都是无序的。

简单总结下

Java 内存模型是 JVM 的一种规范,定义了共享内存在多线程程序中读写操作行为的规范。
屏蔽了各种硬件和操作系统的访问差异,保证了 Java 程序在各种平台下对内存的访问效果一致
解决并发问题采用的方式:限制处理器优化和使用内存屏障
增强了三个同步原语(synchronized、volatile、final)的内存语义
定义了 happens-before 规则

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zskxQ50o-1685634781264)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230518144502710.png)]

————————————————

32.在 Java 中,什么时候用重载,什么时候用重写?

(1)重载是多态的集中体现,在类中,要以统一的方式处理不同类型数据的时候,可以用重载。

(2)重写的使用是建立在继承关系上的,子类在继承父类的基础上,增加新的功能,可以用重写。

(3)简单总结:
目的是提高程序的多样性和健壮性,以适配不同场景使用时,使用重载进行扩展;
目的是在不修改原方法及源代码的基础上对方法进行扩展或增强时,使用重写;

设计模式:

cglib实现动态代理,核心原理用的就是方法的重写;

Java的重载(overload) 最重要的应用场景就是构造器的重载,构造器重载后,提供多种形参形式的构造器,可以应对不同的业务需求,加强程序的健壮性和可扩展性,比如我们最近学习的Spring源码中的ClassPathXmlApplicationContext,它的构造函数使用重载一共提供了10个构造函数,这样就为业务的选择提供了多选择性。在应用到方法中时,主要是为了增强方法的健壮性和可扩展性,比如我们在开发中常用的各种工具类,比如我目前工作中的短信工具类SMSUtil, 发短信的方法就会使用重载,针对不同业务场景下的不同形参,提供短信发送方法,这样提高了工具类的扩展性和健壮性。
总结:重载必须要修改方法(构造器)的形参列表,可以修改方法的返回值类型,也可以修改方法的异常信息即访问权限;使用范围是在同一个类中,目的是对方法(构造器)进行功能扩展,以应对多业务场景的不同使用需求。提高程序的健壮性和扩展性。
 java的重写(override) 只要用于子类对父类方法的扩展或修改,但是在我们开发中,为了避免程序混乱,重写一般都是为了方法的扩展,比如在cglib方式实现的动态代理中,代理类就是继承了目标类,对目标类的方法进行重写,同时在方法前后进行切面织入。

总结:方法重写时,参数列表,返回值得类型是一定不能修改的,异常可以减少或者删除,但是不能抛出新的异常或者更广的异常,方法的访问权限可以降低限制,但是不能做更严格的限制。

(4)在里氏替换原则中,子类对父类的方法尽量不要重写和重载。

33.举例说明什么情况下会更倾向于使用抽象类而不是接口?

接口和抽象类都遵循”面向接口而不是实现编码”设计原则,它可以增加代码的灵活性,可以适应不断变化的需求。下面有几个点可以帮助你回答这个问题:在 Java 中,你只能继承一个类,但可以实现多个接口。所以一旦你继承了一个类,你就失去了继承其他类的机会了。

接口通常被用来表示附属描述或行为如: Runnable 、 Clonable 、 Serializable 等等,因此当你使用抽象类来表示行为时,你的类就不能同时是 Runnable 和 Clonable( 注:这里的意思是指如果把 Runnable 等实现为抽象类的情况 ) ,因为在 Java 中你不能继承两个类,但当你使用接口时,你的类就可以同时拥有多个不同的行为。

在一些对时间要求比较高的应用中,倾向于使用抽象类,它会比接口稍快一点。如果希望把一系列行为都规范在类继承层次内,并且可以更好地在同一个地方进行编码,那么抽象类是一个更好的选择。有时,接口和抽象类可以一起使用,接口中定义函数,而在抽象类中定义默认的实现。
————————————————

34.实例化对象有哪几种方式

new
clone()
通过反射机制创建
//用 Class.forName方法获取类,在调用类的newinstance()方法

Class<?> cls = Class.forName("com.dao.User");
User u = (User)cls.newInstance();

序列化反序列化

35.byte类型127+1等于多少

byte的范围是-128~127。

字节长度为8位,最左边的是符号位,而127的二进制为01111111,所以执行+1操作时,01111111变为10000000。

大家知道,计算机中存储负数,存的是补码的兴衰。左边第一位为符号位。

那么负数的补码转换成十进制如下:

一个数如果为正,则它的原码、反码、补码相同;一个正数的补码,将其转化为十进制,可以直接转换。

已知一个负数的补码,将其转换为十进制数,步骤如下:

先对各位取反;
将其转换为十进制数;
加上负号,再减去1;
例如10000000,最高位是1,是负数,①对各位取反得01111111,转换为十进制就是127,加上负号得-127,再减去1得-128;

总结:127+1的二进制码由01111111变成10000000,左边第一位为符号位,且最高位是1,是负数。

————————————————

36.自定义异常在生产中如何应用?

Java虽然提供了丰富的异常处理类,但是在项目中还会经常使用自定义异常,其主要原因是Java提供的异常类在某些情况下还是不能满足实际需球。例如以下情况:
1、系统中有些错误是符合Java语法,但不符合业务逻辑
2、在分层的软件结构中,通常是在表现层统一对系统其他层次的异常进行捕获处理。

37.Maven分模块开发、Maven依赖管理、Maven聚合、Maven继承

分模块开发意义

将原始模块按照功能拆分成若干个子模块,方便模块间的相互调用,接口共享;

当团队合作项目,做不同的功能模块时,一个模块要引用到另一个模块的功能,需要用到Maven分模块开发设计

​ (1)导入Maven_pojo模块的坐标;

​ (2)通过maven指令安装模块到本地仓库(install指令)

(2)依赖管理:

依赖指当前项目运行所需的jar,一个项目可以设置多个依赖

(2.1)依赖传递性:

若第一个模块依赖了第二个模块,而第二个模块又依赖了第三个模块 ,进而第一个模块可以引用第三个模块的功能。
直接依赖: 在当前项目中通过依赖配置建立的依赖关系
间接依赖: 被资源的资源如果依赖其他资源,当前项目间接依赖其他资源

依赖传递冲突问题

路径优先: 当依赖中出现相同的资源时,层级越深,优先级越低,层级越浅,优先级越高
声明优先:当资源在相同层级被依赖时,配置顺序靠前的覆盖配置顺序靠后的
特殊优先:当同级配置了相同资源的不同版本,后配置的覆盖先配置

在同一个层次上依赖不同版本的依赖,会根据先后配置,后配置的较为优先。

(2.3)可选依赖:(你现在的模块用的东西,不想给别人用,说得简单一点就是我隐藏我自己的依赖,别人知道和不知道我用过它)控制当前资源能不能被别人发现。

可选依赖是隐藏当前工程所依赖的资源,隐藏后对应的资源将不具有依赖传递性。

  <dependency>
            <groupId>com.lingnan</groupId>
            <artifactId>Maven_pojo</artifactId>
            <version>1.0-SNAPSHOT</version>
<!--            可选依赖是隐藏当前工程所依赖的资源,隐藏后对应的资源将不具有依赖传递性-->
            <optional>true</optional>
  </dependency>

(2.4)排除依赖:(用了别人的东西,发现有些东西不想用,你可以把它排除掉)主动断开依赖的资源,是隐藏当前资源对应的依赖关系, 我所依赖的工程所带的依赖,不用的情况下进行排除

  <!--    依赖dao运行-->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>Maven_Dao</artifactId>
            <version>1.0-SNAPSHOT</version>
<!--            排除依赖是隐藏当前资源对应的依赖关系 我所依赖的工程所带的依赖 不用的情况下进行排除-->
            <exclusions>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

设置exclusions属性,将要排除的依赖写在exclusion属性中即可。
在这里插入图片描述

| (按位或)与 ||(短路或) 和 &(按位与)&&(短路与)

| (按位或)与 ||(短路或) 都是表示或,区别在于 || 在遇到多个条件的时候,从左到右判断,只要当一个条件为true时,则认为整个判断为true,后面剩余的条件跳过判断。而 | 需要对所有条件进行判断为,有满足条件,则返回true。

&&(短路与)若第一个条件不满足,后面条件就不再判断。而&(按位与)要对所有的条件都进行判断

39. this和super关键字的作用

this是对象内部指代自身的引用,同时也是解决成员变量和局部变量同名问题;this可以调用成员变量,不能调用局部

变量;this也可以调用成员方法,但是在普通方法中可以省略this,在构造方法中不允许省略,必须是构造方法的第

一条语句。而且在静态方法当中不允许出现this关键字。

super代表对当前对象的直接父类对象的引用,super可以调用直接父类的成员变量(注意权限修饰符的影响,比如

不能访问private成员)

super可以调用直接父类的成员方法(注意权限修饰符的影响,比如不能访问private成员);super可以调用直接父

类的构造方法,只限构造方法中使用,且必须是第一条语句

40. 继承条件下构造方法的执行过程

继承条件下构造方法的调用规则如下:

情况1:如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构

造方法,则系统会默认先调用父类的无参构造方法。在这种情况下,写不写“super();”语句,效果是一样的。

情况2:如果子类的构造方法中通过super显式调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父

类无参构造方法。

情况3:如果子类的构造方法中通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则。

42. 说明内存泄漏和内存溢出的区别和联系,结合项目经验描述Java程序中如何检测?如何解决?

1、内存泄漏memory leak :

是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果

就是内存溢出。

2、内存溢出 out of memory :

指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存

储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出。

43.在Java中,如何跳出当前的多重嵌套循环?

在最外层循环前加一个标记如A:,然后用break A;可以跳出多重循环。

44.当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变

化后的结果,那么这里到底是值传递还是引用传递?

是值传递。

Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对

象的属性可以在被调用过程中被改变,但对象的引用是永远不会改变的。C++和C#中可以通过传引用或传输出参数来

改变传入的参数的值。

public class Test {
public static void main(String[] args) {
    A ab = new B();
    ab = new B();
}
}
class A{
    static{
        System.out.print("1");
    }
    public A(){
        System.out.print("2");
    }
}
class B extends A{
    static{
        System.out.print("a");
    }
    public B(){
        System.out.print("b");
    }
}

结果 : 1a2b2b

静态代码块只执行一次 , 在类初始化的时候 ,

构造方法会向上执行父级的

容器

1.Java 容器都有哪些?

(1)Collection

① set

HashSet、TreeSet

② list

ArrayList、LinkedList、Vector

(2)Map

HashMap、HashTable、TreeMap

2.Collection 和 Collections 有什么区别?

(1)Collection是最基本的集合接口,Collection派生了两个子接口list和set,分别定义了两种不同的存储方式。

(2)Collections是一个包装类,它包含各种有关集合操作的静态方法(对集合的搜索、排序、线程安全化等)。

此类不能实例化,就像一个工具类,服务于Collection框架。
————————————————

*3.list与Set区别

(1)List简介

实际上有两种List:一种是基本的ArrayList,其优点在于随机访问元素,另一种是LinkedList,它并不是为快速随机访问设计的,而是快速的插入或删除。
ArrayList:由数组实现的List。允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。
LinkedList :对顺序访问进行了优化,向List中间插入与删除的开销并不大。随机访问则相对较慢。

(2)Set简介

Set具有与Collection完全一样的接口,因此没有任何额外的功能。实际上Set就是Collection,只是行为不同。这是继承与多态思想的典型应用:表现不同的行为。Set不保存重复的元素(至于如何判断元素相同则较为负责)

Set : 存入Set的每个元素都必须是唯一的,因为Set不保存重复元素。

加入Set的元素必须定义equals()方法以确保对象的唯一性。

Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。
HashSet:为快速查找设计的Set。存入HashSet的对象必须定义hashCode()。
TreeSet: 保存次序的Set, 底层为树结构。使用它可以从Set中提取有序的序列。

(3)list与Set区别

① List,Set都是继承自Collection接口

② List特点:元素有放入顺序,元素可重复 ,

Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,

另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)

③ Set和List对比:

Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。

*4.ArrayList的自动扩容机制的实现原理?

ArrayList底层是一个数组结构的存储容器,默认情况下,数组的长度是10个,我们也可以在构建ArrayList的时候创建初始长度。
随着不断往ArrayList里面添加数据,当数据到达10个的时候,ArrayList里面没有足够的容量去存储后续的容量,ArrayList后续会触发自动扩容,创建一个新的数组,这个新数组容量是原有数组的1.5倍;
然后使用Arrays.copy()方法把老数据拷贝到新数组里面。然后在把新的元素添加到新的数组里面,从而完成一个动态扩容的过程。

5.HashMap 和 Hashtable 有什么区别?

  1. HashMap是线程不安全的,HashTable是线程安全的;
  2. HashMap中允许键和值为null,HashTable不允许;
  3. HashMap的默认容器是16,为2倍扩容,HashTable默认是11,为2倍+1扩容;

*6.说一下 HashMap 的实现原理?

(1)简介

HashMap基于map接口,元素以键值对方式存储,允许有null值,HashMap是线程不安全的。

(2)基本属性

初始化大小,默认16,2倍扩容;
负载因子0.75;

HashMap的默认长度为16,他的负载因子是0.75,所以当存储第13个元素的时候就会进行一个扩容。

初始化的默认数组;
size
threshold。阀值判断是否需要调整hashmap容量

(3)HashMap的存储结构

JDK1.7中采用数组+链表的存储形式。

HashMap采取Entry数组来存储key-value,每一个键值对组成了一个Entry实体,Entry类实际上是一个单向的链表结构,它具有next指针,指向下一个Entry实体,以此来解决Hash冲突的问题。

HashMap实现一个内部类Entry,重要的属性有hash、key、value、next。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f6YCHGiK-1685634781271)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230407134737434.png)]

JDK1.8中采用数据+链表+红黑树的存储形式。当链表长度超过阈值(8)时,将链表转换为红黑树。在性能上进一步得到提升。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MWR7EnCy-1685634781271)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230407134801046.png)]

7.set有哪些实现类?

(1)HashSet

HashSet是set接口的实现类,set下面最主要的实现类就是HashSet(也就是用的最多的),此外还有LinkedHashSet和TreeSet。
HashSet是无序的、不可重复的。通过对象的hashCode和equals方法保证对象的唯一性。

(2)TreeSet

TreeSet对元素进行排序的方式:
自然排序:如果 TreeSet 中的元素实现了 Comparable 接口,那么 TreeSet 会使用元素自身的 compareTo(Object obj) 方法来比较元素之间的大小关系,并按照升序排列
需要实现Comparable接口。
自定义排序:让集合本身具备比较功能,需要实现Comparator接口,并覆盖compare方法。

(3)LinkedHashSet

LinkedHashSet是一种有序的Set集合,即其元素的存入和输出的顺序是相同的。

8.说一下 HashSet 的实现原理?

HashSet底层实际上是HashMap(),底层数据结构为:Node[]数组 + 单链表 + 红黑树

HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value都是一个统一的对象PRESENT。

private static final Object PRESENT = new Object();

HashSet中add方法调用的是底层HashMap中的put方法,put方法要判断插入值是否存在,而HashSet的add方法,首先判断元素是否存在,如果存在则插入,如果不存在则不插入,这样就保证了HashSet中不存在重复值。

通过对象的hashCode和equals方法保证对象的唯一性。
————————————————

*ArrayList总结

  1. ArrayList使用数组存储元素,当数组长度不够的时候,会进行扩容,每次扩容为1.5倍。

  2. ArrayList添加元素到尾部,平均时间复杂度为O(1)。

  3. ArrayList添加元素到中间,平均时间复杂度为O(n)。

  4. ArrayList删除尾部的元素,平均时间复杂度为O(1)。

  5. ArrayList从中间删除元素比价慢,平均时间复杂度为O(n)。

  6. ArrayList支持随机访问,时间复杂度为O(1)。

    ArrayList是一种以数组实现的List,它实现了List, RandomAccess, Cloneable, Serializable接口。
    
    实现List接口表示它可以支持删除、添加和查找等操作。
    实现RandomAccess接口表示它可以支持随机访问(强调一点,并不是因为实现RandomAccess接口,ArrayList才支持随机访问。RandomAccess只是一个标记接口,接口RandomAccess中内容是空的,只是作为标记使用。)。
    实现了Cloneable接口表示ArrayList可以被克隆。
    实现了Serializable接口表示ArrayList可以被序列化。
    

9.ArrayList 和 LinkedList 的区别是什么?

ArrayList是动态数组的数据结构实现,查找和遍历的效率较高;

LinkedList 是双向链表的数据结构,增加和删除的效率较高;

10.如何实现数组和 List 之间的转换?

String[] arr = {"zs","ls","ww"};
    List<String> list = Arrays.asList(arr);
 
ArrayList<String> list1 = new ArrayList<String>();
list1.add("张三");
list1.add("李四");
list1.add("王五");
String[] arr1 = list1.toArray(new String[list1.size()]);

*11.ArrayList和LinkedList有哪些区别?

从插入 删除 访问和内存使用情况的角度讲?

相同点:ArrayList和LinkedList都实现了List接口,都实现了list一些方法;

不同点:

**底层:**ArrayList是基于索引的数据接口,它的底层是动态数组,

​ LinkeList的底层是双向链表结构;

插入删除数据时:

​ ArrayList插入和删除非尾部数组操作,速度非常慢,涉及到移动数组;

​ LinkeList使它支持高效的插入和删除操作,因为只需要移动链表的指针

查询:

ArrayList速度特别快,因为它基于索引(index)的数据结构,使用索引在数组中搜索和读取数据非常快,Array获取数据的时间复杂度是O(1);

LinkedList查询慢,因为查询数据的时候会移动链表,当链表长度较大时,查询速度会特别慢;

内存:

ArrayList 的每个索引的位置是实际的数据;

而 LinkedList 中的每个节点中存诸的是实际的数据和前后节点的位置(一个 LinkedList 实例存储了两个值: Node first 和 Node last 分别表示链表的起始节点和尾节点,每个 Node 实例存储了三个值: E item,Node first 和 Node last )

12.在 Queue 中 poll()和 remove()有什么区别?

(1)offer()和add()区别:

增加新项时,如果队列满了,add会抛出异常,offer返回false。

(2)poll()和remove()区别:

poll()和remove()都是从队列中删除第一个元素,为空时,remove抛出异常,poll返回null。

(3)peek()和element()区别:

peek()和element()用于查询队列头部元素,为空时element抛出异常,peek返回null。
————————————————

13.哪些集合类是线程安全的

Vector:就比Arraylist多了个同步化机制(线程安全)。
Stack:栈,也是线程安全的,继承于Vector。
Hashtable:就比Hashmap多了个线程安全。
ConcurrentHashMap:是一种高效但是线程安全的集合。

14.高并发中的集合有那些?

第一代线程安全集合类
Vector、Hashtable
是怎么保证线程安排的: 使用synchronized修饰方法
缺点:效率低下
第二代线程非安全集合类
ArrayList、HashMap
线程不安全,但是性能好,用来替代Vector、Hashtable
使用ArrayList、HashMap,需要线程安全怎么办呢?使用 Collections.synchronizedList(list); Collections.synchronizedMap(m);
底层使用synchronized代码块锁 虽然也是锁住了所有的代码,但是锁在方法里边,并所在方法外边性能可以理解为稍有提高吧。些竟进方法本身就要分配资源的
第三代线程安全集合类
在大量并发情况下如何提高集合的效率和安全呢?
java.util.concurrent.*
ConcurrentHashMap :
CopyOnWriteArrayList :
CopyOnWriteArraySet:注意 不是CopyOnWriteHashSet*
底层大都采用Lock锁 (1.8的ConcurrentHashMap不使用Lock锁)保证安全的同时,性能也很高。

15.迭代器 Iterator 是什么?

为了方便的处理集合中的元素,Java中出现了一个对象,该对象提供了一些方法专门处理集合中的元素.例如删除和获取集合中的元素.该对象就叫做迭代器(Iterator)。

16.Iterator 怎么使用?有什么特点?

Iterator 接口源码中的方法:

java.lang.Iterable 接口被 java.util.Collection 接口继承,java.util.Collection 接口的 iterator() 方法返回一个 Iterator 对象
next() 方法获得集合中的下一个元素
hasNext() 检查集合中是否还有元素
remove() 方法将迭代器新返回的元素删除
————————————————

17.Iterator 和 ListIterator 有什么区别?

(1)ListIterator 继承 Iterator

(2)ListIterator 比 Iterator多方法

(3)使用范围不同,Iterator可以迭代所有集合;ListIterator 只能用于List及其子类

18.怎么确保一个集合不能被修改?

我们很容易想到用final关键字进行修饰,我们都知道

final关键字可以修饰类,方法,成员变量,final修饰的类不能被继承,final修饰的方法不能被重写,final修饰的成员变量必须初始化值,如果这个成员变量是基本数据类型,表示这个变量的值是不可改变的,如果说这个成员变量是引用类型,则表示这个引用的地址值是不能改变的,但是这个引用所指向的对象里面的内容还是可以改变的。

那么,我们怎么确保一个集合不能被修改?首先我们要清楚,集合(map,set,list…)都是引用类型,所以我们如果用final修饰的话,集合里面的内容还是可以修改的。

我们可以做一个实验:

可以看到:我们用final关键字定义了一个map集合,这时候我们往集合里面传值,第一个键值对1,1;我们再修改后,可以把键为1的值改为100,说明我们是可以修改map集合的值的。

那我们应该怎么做才能确保集合不被修改呢?
我们可以采用Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的。他会报 java.lang.UnsupportedOperationException错。

同理:Collections包也提供了对list和set集合的方法。

Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)

Collections集合工具类下面有几个unmodifiable打头的方法,可以保证集合不被修改

————————————————

19.队列和栈是什么?有什么区别?

(1)队列先进先出,栈先进后出。

(2)遍历数据速度不同。

栈只能从头部取数据 也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性

队列则不同,他基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多。
————————————————

20.HasmMap和HashSet的区别

(1)先了解一下HashCode

Java中的集合有两类,一类是List,一类是Set。

List:元素有序,可以重复;

Set:元素无序,不可重复;

要想保证元素的不重复,拿什么来判断呢?这就是Object.equals方法了。如果元素有很多,增加一个元素,就要判断n次吗?

显然不现实,于是,Java采用了哈希表的原理。哈希算法也称为散列算法,是将数据依特定算法直接指定到一根地址上,初学者可以简单的理解为,HashCode方法返回的就是对象存储的物理位置(实际上并不是)。

这样一来,当集合添加新的元素时,先调用这个元素的hashcode()方法,就一下子能定位到他应该放置的物理位置上。如果这个位置上没有元素,他就可以直接存储在这个位置上,不用再进行任何比较了。如果这个位置上有元素,就调用它的equals方法与新元素进行比较,想同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题。这样一来实际上调用equals方法的次数就大大降低了,几乎只需要一两次。

简而言之,在集合查找时,hashcode能大大降低对象比较次数,提高查找效率

Java对象的equals方法和hashCode方法时这样规定的:

相等的对象就必须具有相等的hashcode。

如果两个对象的hashcode相同,他们并不一定相同。
如果两个对象的hashcode相同,他们并不一定相同。
如果两个Java对象A和B,A和B不相等,但是A和B的哈希码相等,将A和B都存入HashMap时会发生哈希冲突,也就是A和B存放在HashMap内部数组的位置索引相同,这时HashMap会在该位置建立一个链接表,将A和B串起来放在该位置,显然,该情况不违反HashMap的使用规则,是允许的。当然,哈希冲突越少越好,尽量采用好的哈希算法避免哈希冲突。

equals()相等的两个对象,hashcode()一定相等;equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。

(2)HashMap和HashSet的区别
在这里插入图片描述

JDK1.7 VS JDK1.8 比较

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BqTi3nfW-1685634781272)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230506172029711.png)]

并发

Java中的多线程具有六种状态:

初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
就绪(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法,此时线程处于就绪状态,等待CPU时间片的分配。
运行(RUNNING):线程获得CPU时间片后,执行run()方法中的代码,此时线程处于运行状态。
阻塞(BLOCKED):线程在执行过程中,需要等待某个条件满足才能继续执行,但是该条件暂时无法满足,此时线程处于阻塞状态。例如,线程A执行体中调用线程B的join()方法,则线程A被阻塞,直到线程B执行完为止,线程A才能得以继续执行。
等待(WAITING):线程在等待某个特定时间或者等待其他操作完成,此时线程处于等待状态。例如,Thread.sleep()方法可以使当前线程进入等待状态。
超时等待(TIMED_WAITING):线程在等待某个特定时间或者等待其他操作完成,但是超过了指定的时间限制仍未得到结果,此时线程处于超时等待状态。例如,Thread.sleep(long millis)方法可以使当前线程进入超时等待状态。
终止(TERMINATED):线程执行完毕或者异常终止,此时线程处于终止状态。

1.并发容器总结

ConcurrentHashMap: 线程安全的 HashMap,分段锁;

CopyOnWriteArrayList: 线程安全的 List,在读多写少的场合性能非常好,远远好于 Vector.

ConcurrentLinkedQueue: 高效的并发队列,使用链表实现。可以看做一个线程安全的LinkedList,这是一个非阻塞队列

BlockingQueue: 这是一个接口,JDK 内部通过链表、数组等方式实现了这个接口。表示阻塞队列,非常适合用于作为数据共享的通道。

ConcurrentSkipListMap: 跳表的实现。这是一个 Map,使用跳表的数据结构进行快速查找

ConcurrentHashMap

在 ConcurrentHashMap中,无论是操作还是操作都能保证很高的性能:在进行操作时**(几乎)不需要加锁**,而在写操作时

通过锁分段技术只对所操作的段加锁而不影响客户端对其它段的访问。

2.Java8开始ConcurrentHashMap,为什么舍弃分段锁?

ConcurrentHashMap的原理是引用了内部的 Segment ( ReentrantLock ) 分段锁,保证在操作不同段 map 的时候, 可以并发执行, 操作同段 map 的时候,进行锁的竞争和等待。从而达到线程安全, 且效率大于 synchronized

但是在 Java 8 之后, JDK 却弃用了这个策略,重新使用了 synchronized(重量级锁)+CAS(乐观锁)。

弃用原因

通过 JDK 的源码和官方文档看来, 他们认为的弃用分段锁的原因由以下几点:

加入多个分段锁浪费内存空间
生产环境中, map 在放入时竞争同一个锁的概率非常小,分段锁反而会造成更新等操作的长时间等待
为了提高 GC 的效率
新的同步方案

既然弃用了分段锁, 那么一定由新的线程安全方案, 我们来看看源码是怎么解决线程安全的呢?(源码保留了segment 代码, 但并没有使用)。
————————————————

3.ConcurrentHashMap(JDK1.8)为什么要使用synchronized而不是如ReentranLock这样的可重入锁?

我想从下面几个角度讨论这个问题:

(1)锁的粒度

首先锁的粒度并没有变粗,甚至变得更细了。每当扩容一次,ConcurrentHashMap的并发度就扩大一倍

(2)Hash冲突

JDK1.7中,ConcurrentHashMap从过二次hash的方式(Segment -> HashEntry)能够快速的找到查找的元素。在1.8中通过链表加红黑树的形式弥补了put、get时的性能差距。
JDK1.8中,在ConcurrentHashmap进行扩容时,其他线程可以通过检测数组中的节点决定是否对这条链表(红黑树)进行扩容,减小了扩容的粒度,提高了扩容的效率。

下面是我对面试中的那个问题的一下看法。

为什么是synchronized,而不是ReentranLock

(1**)减少内存开销**

假设使用可重入锁来获得同步支持,那么每个节点都需要通过继承AQS来获得同步支持。但并不是每个节点都需要获得同步支持的,只有链表的头节点(红黑树的根节点)需要同步,这无疑带来了巨大内存浪费。

(2)获得JVM的支持

可重入锁毕竟是API这个级别的,后续的性能优化空间很小。
synchronized则是JVM直接支持的,JVM能够在运行时作出相应的优化措施:锁粗化、锁消除、锁自旋等等。这就使得synchronized能够随着JDK版本的升级而不改动代码的前提下获得性能上的提升。
————————————————

*4.concurrentHashMap和HashTable有什么区别?

concurrentHashMap融合了hashmap和hashtable的优势,hashmap是不同步的,但是单线程情况下效率高,hashtable是同步的同步情况下保证程序执行的正确性。

concurrentHashMap锁的方式是细粒度的。concurrentHashMap将hash分为16个桶(默认值),诸如get、put、remove等常用操作只锁住当前需要用到的桶

concurrentHashMap的读取并发,因为读取的大多数时候都没有锁定,所以读取操作几乎是完全的并发操作,只是在求size时才需要锁定整个hash

而且在迭代时,concurrentHashMap使用了不同于传统集合的快速失败迭代器的另一种迭代方式,弱一致迭代器。在这种方式中,当iterator被创建后集合再发生改变就不会抛出ConcurrentModificationException,取而代之的是在改变时new新的数据而不是影响原来的数据,iterator完成后再讲头指针替代为新的数据,这样iterator时使用的是原来的数据。
————————————————

CopyOnWriteArrayList

ReentrantReadWriteLock 读写锁的思想非常类似,也就是读读共享、写写互斥、读写互斥、写读互斥。JDK 中提供了 CopyOnWriteArrayList 类比相比于在读写锁的思想又更进一步。为了将读取的性能发挥到极致, **CopyOnWriteArrayList 读取是完全不用加锁的,并且更厉害的是:写入也不会阻塞读取操作。只有写入和写入之间需要进行同步等待。**这样一来,读操作的性能就会大幅度提升。

5.ConcurrentLinkedQueue

Java 提供的线程安全的 Queue 可以分为阻塞队列非阻塞队列,其中阻塞队列的典型例子是

BlockingQueue,非阻塞队列的典型例子是 ConcurrentLinkedQueue,在实际应用中要根据实际需要

选用阻塞队列或者非阻塞队列。 阻塞队列可以通过加锁来实现,非阻塞队列可以通过 CAS 操作实现。

6.ConcurrentSkipListMap

跳表是一种可以用来快速查找的数据结构,有点类似于平衡树。它们都可以对元素进行快速的查找

最低层的链表维护了跳表内所有的元素每上面一层链表都是下面一层的子集。跳表内的所有链表的元素都是排序的。查找时,可以从顶级链表开始找。一旦**发现被查找的元素大于当前链表中的取值,就会转入下一层链表继续找。**这也就是说在查找过程中,搜索是跳跃式的。如上图所示,在跳表中查找元素 18。原来需要遍历 18 次,现在只需要 7 次即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zpUqqbg-1685634781272)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230412155419598.png)]

*7.请谈谈 ReadWriteLock 和 StampedLock(升级读写锁)

ReadWriteLock包括两种子锁

(1)ReadWriteLock

ReadWriteLock 可以实现多个读锁同时进行,但是读与写和写于写互斥,只能有一个写锁线程在进行

(2)StampedLock(升级版读写锁)

StampedLock是Jdk在1.8提供的一种读写锁,相比较ReentrantReadWriteLock性能更好,因为ReentrantReadWriteLock在读写之间是互斥的,使用的是一种悲观策略,在读线程特别多的情况下,会造成写线程处于饥饿状态,虽然可以在初始化的时候设置为true指定为公平,但是吞吐量又下去了,而StampedLock是提供了一种乐观策略,更好的实现读写分离,并且吞吐量不会下降。

StampedLock包括三种锁:

(1)写锁writeLock:

writeLock是一个独占锁写锁,当一个线程获得该锁后,其他请求读锁或者写锁的线程阻塞, 获取成功后,会返回一个stamp(凭据)变量来表示该锁的版本,在释放锁时调用unlockWrite方法传递stamp参数。提供了非阻塞式获取锁tryWriteLock

(2)悲观读锁readLock:

readLock是一个共享读锁,在没有线程获取写锁情况下,多个线程可以获取该锁。如果有写锁获取,那么其他线程请求读锁会被阻塞。悲观读锁会认为其他线程可能要对自己操作的数据进行修改,所以需要先对数据进行加锁,这是在读少写多的情况下考虑的。请求该锁成功后会返回一个stamp值,在释放锁时调用unlockRead方法传递stamp参数。提供了非阻塞式获取锁方法tryWriteLock。

(3)tryOptimisticRead乐观读锁:

tryOptimisticRead相对比悲观读锁,在操作数据前并没有通过CAS设置锁的状态如果没有线程获取写锁,则返回一个非0的stamp变量,获取该stamp后在操作数据前还需要调用validate方法来判断期间是否有线程获取了写锁如果是返回值为0则有线程获取写锁,如果不是0则可以使用stamp变量的锁来操作数据。由于tryOptimisticRead并没有修改锁状态,所以不需要释放锁。这是读多写少的情况下考虑的,不涉及CAS操作,所以效率较高,在保证数据一致性上需要复制一份要操作的变量到方法栈中,并且在操作数据时可能其他写线程已经修改了数据,而我们操作的是方法栈里面的数据,也就是一个快照,所以最多返回的不是最新的数据,但是一致性得到了保证。
————————————————

8.线程的run()和start()有什么区别?

每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,run()方法称为线程体。通过调用Thread类的start()方法来启动一个线程。

start() 方法用于启动线程,run() 方法用于执行线程的运行时代码run() 可以重复调用,而 start() 只能调用一次

start()方法来启动一个线程,真正实现了多线程运行。调用start()方法无需等待run方法体代码执行完毕,可以直接继续执行其他的代码; 此时线程是处于就绪状态,并没有运行。 然后通过此Thread类调用方法run()来完成其运行状态run()方法运行结束, 此线程终止。然后CPU再调度其它线程。

run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的。 如果直接调用run(),其实就相当于是调用了一个普通函数而已,直接待用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方法。
————————————————

9.为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?

这是另一个非常经典的 java 多线程面试问题,而且在面试中会经常被问到。很简单,但是很多人都会答不上来!

new 一个 Thread,线程进入了新建状态。调用 start() 方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。

而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。

总结: 调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。

————————————————

*10、Synchronized 用过吗,其原理是什么?

(1)可重入性

synchronized的锁对象中有一个计数器(recursions变量)会记录线程获得几次锁

可重入的好处:
可以避免死锁
可以让我们更好的封装代码;
synchronized是可重入锁,每部锁对象会有一个计数器记录线程获取几次锁在执行完同步代码块时,计数器的数量会-1,直到计数器的数量为0,就释放这个锁。

	在java内部,同一线程在调用自己类中其他synchronized方法/块或调用父类的synchronized方法/块都不会阻碍该线程的执行,就是说同一线程对同一个对象锁是可重入的,而且同一个线程可以获取同一把锁多次,也就是可以多次重入。
	其实现方法是为每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁。

(2)不可中断性

一个线程获得锁后,另一个线程想要获得锁,必须处于阻塞或等待状态,如果第一个线程不释放锁,第二个线程会一直阻塞或等待,不可被中断;
synchronized 属于不可被中断;
Lock StampedLock包括三种锁
Lock tryLock方法是可中断的;

————————————————

*11.JVM 对 Java 的原生锁做了哪些优化?

(1)自旋锁.

在线程进行阻塞的时候,先让线程自旋等待一段时间,可能这段时间其它线程已经解锁,这时就无需让线程再进行阻塞操作了。

自旋默认次数是10

(2)自适应自旋锁

自旋锁的升级,自旋的次数不再固定,由前一次自旋次数和锁的拥有者的状态决定

(3)锁消除

锁消除是指JIT在运行时分析到使用了锁的同步代码在实际运行时不可能存在共享数据被竞争的情况,对锁进行去除。例如如果一个局部变量在方法内部不可能被外部引用,那么它就不需要加锁控制,可以去掉锁。

在动态编译同步代码块的时候,JIT编译器借助逃逸分析技术来判断锁对象是否只被一个线程访问,而没有其他线程,这时就可以取消锁了。

4、锁粗化

当JIT编译器发现一系列的操作都对同一个对象反复加锁解锁,甚至加锁操作出现在循环中,此时会将加锁同步的范围粗化到整个操作系列的外部。

锁粒度:不要锁住一些无关的代码。

锁粗化:可以一次性执行完的不要多次加锁执行。

————————————————

12、为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?

Java中,任何对象都可以作为锁,并且wait(),notify()等方法用于等待对象的锁或者唤醒线程,在 Java 的线程中并没有可供任何对象使用的锁,所以任意对象调用方法一定定义在Object类中。

wait(), notify()和 notifyAll()这些方法在同步代码块中调用

有的人会说,既然是线程放弃对象锁,那也可以把wait()定义在Thread类里面啊,新定义的线程继承于Thread类,也不需要重新定义wait()方法的实现。然而,这样做有一个非常大的问题,**一个线程完全可以持有很多锁,你一个线程放弃锁的时候,到底要放弃哪个锁?**当然了,这种设计并不是不能实现,只是管理起来更加复杂。

综上所述,wait()、notify()和notifyAll()方法要定义在Object类中。

  • 总结:IllegalMonitorStateException,如果我们不通过同步环境(synchronized context)调用这几个方法,系统将抛出此异常
  • 由于wait(), notify()和notifyAll()都是对象的方法,因此需要在同步方法或者同步块中被调用,以确保线程之间的同步和协作。

————————————————

13、Thread 类中的 yield 方法有什么作用?

yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中

结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

————————————————

14、为什么说 Synchronized 是非公平锁?

当锁被释放后,任何一个线程都有机会竞争得到锁,这样做的目的是提高效率,但缺点是可能产生线程饥饿现象
————————————————

15、为什么说 Synchronized 是一个悲观锁?乐观锁的实现原理又是什么?什么是 CAS,它有什么特性?

Synchronized的并发策略是悲观的,不管是否产生竞争,任何数据的操作都必须加锁。

乐观锁的核心是CAS,CAS包括内存值、预期值、新值,只有当内存值等于预期值时,才会将内存值修改为新值。

*16、乐观锁一定就是好的吗?

乐观锁认为对一个对象的操作不会引发冲突,所以每次操作都不进行加锁,只是在最后提交更改时验证是否发生冲突,如果冲突则再试一遍,直至成功为止,这个尝试的过程称为自旋

乐观锁没有加锁,但乐观锁引入了ABA问题,此时一般采用版本号进行控制
也可能产生自旋次数过多问题,此时并不能提高效率,反而不如直接加锁的效率高
只能保证一个对象的原子性,可以封装成对象再进行CAS操作
————————————————

*17、请尽可能详尽地对比下 Synchronized 和 ReentrantLock 的异同。

(1)相似点

它们都是阻塞式的同步,也就是说一个线程获得了对象锁,进入代码块,其它访问该同步块的线程都必须阻塞在同步代码块外面等待,而进行线程阻塞和唤醒的代码是比较高的。

(2)功能区别

Synchronized是java语言的关键字,是原生语法层面的互斥,需要JVM实现;ReentrantLock 是JDK1.5之后提供的API层面的互斥锁需要lock和unlock()方法配合try/finally代码块来完成
Synchronized使用较ReentrantLock 便利一些;
锁的细粒度和灵活性:ReentrantLock强于Synchronized;

(3)性能区别

Synchronized引入偏向锁,自旋锁之后,两者的性能差不多,在这种情况下,官方建议使用Synchronized。

① Synchronized

Synchronized会在同步块的前后分别形成monitorenter和monitorexit两个字节码指令。

在执行monitorenter指令时,首先要尝试获取对象锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象锁,把锁的计数器+1,相应的执行monitorexit时,计数器-1,当计数器为0时,锁就会被释放。如果获取锁失败,当前线程就要阻塞,知道对象锁被另一个线程释放为止。

② ReentrantLock

ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有如下三项:

等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized避免出现死锁的情况。通过lock.lockInterruptibly()来实现这一机制;
公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁是非公平锁ReentrantLock默认也是非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好;
锁绑定多个条件,一个ReentrantLock对象可以同时绑定多个对象。

ReentrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像Synchronized要么随机唤醒一个线程,要么唤醒全部线程
————————————————

*18、ReentrantLock 是如何实现可重入性的?

1)什么是可重入性

一个线程持有锁时,当其他线程尝试获取该锁时,会被阻塞;而这个线程尝试获取自己持有锁时,如果成功说明该锁是可重入的,反之则不可重入。

(2)synchronized是如何实现可重入性

synchronized关键字经过编译后,会在同步块的前后分别形成monitorenter(监控输入)和monitorexit(退出监控)两个字节码指令。每个锁对象内部维护一个计数器,该计数器初始值为0,表示任何线程都可以获取该锁并执行相应的方法。根据虚拟机规范要求,在执行monitorenter指令时,首先要尝试获取对象的锁,如果这个对象没有被锁定,或者当前线程已经拥有了对象的锁,把锁的计数器+1,相应的在执行monitorexit指令后锁计数器-1,当计数器为0时,锁就被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到对象锁被另一个线程释放为止。()

(3)ReentrantLock如何实现可重入性

ReentrantLock使用内部类Sync来管理锁,所以真正的获取锁是由Sync的实现类控制的。Sync有两个实现,分别为NonfairSync(非公公平锁)和FairSync(公平锁)。Sync通过继承AQS实现,在AQS中维护了一个private volatile int state来计算重入次数,避免频繁的持有释放操作带来的线程问题。
(5)代码分析

当一个线程在获取锁过程中,先判断state的值是否为0,如果是表示没有线程持有锁,就可以尝试获取锁。
当state的值不为0时,表示锁已经被一个线程占用了,这时会做一个判断current==getExclusiveOwnerThread(),这个方法返回的是当前持有锁的线程,这个判断是看当前持有锁的线程是不是自己,如果是自己,那么将state的值+1,表示重入返回即可。
————————————————

19、什么是锁消除和锁粗化?

(1)锁消除:降低性能损耗

锁消除就是虚拟机根据一个对象是否真正存在同步情况,若不存在同步情况,则该对象的访问无需经过加锁解锁的操作。

比如StringBuffer的append方法,因为append方法需要判断对象是否被占用,而如果代码不存在锁竞争,那么这部分的性能消耗是无意义的。于是虚拟机在即时编译的时候就会将上面的代码进行优化,也就是锁消除。

(2)锁粗化:降低性能损耗

锁的请求、同步、释放都会消耗一定的系统资源,如果高频的锁请求反而不利于系统性能的优化,锁粗化就是把多次的锁请求合并成一个请求,扩大锁的范围,降低锁请求、同步、释放带来的性能损耗。

————————————————

*20、跟 Synchronized 相比,可重入锁 ReentrantLock 其实现原理有什么不同?

(1)都是可重入锁

(2)ReentrantLock内部是实现了Sync,Sync继承于AQS抽象类。Sync有两个实现,一个是公平锁,一个是非公平锁,通过构造函数定义。AQS中维护了一个state来计算重入次数,避免频繁的持有释放操作带来的线程问题。

(3)ReentrantLock只能定义代码块,而Synchronized可以定义方法和代码块

4、Synchronized是JVM的一个内部关键字;ReentrantLock是一个类,JDK1.5之后引入的一个API层面的互斥锁

5、Synchronized实现自动的加锁、释放锁,ReentrantLock需要手动加锁和释放锁,中间可以暂停

6、Synchronized由于引进了偏向锁和自旋锁,所以性能上和ReentrantLock差不多,但操作上方便很多,所以优先使用Synchronized。
————————————————

*21、那么请谈谈 AQS 框架是怎么回事儿?

​ AQS就是AbstractQueuedSynchronizer抽象类,它提供了一个FIFO队列,可以看成是一个实现同步锁的核心组件。AQS其实就是JUC包下的一个基类,JUC下的很多内容都是基于AQS实现了部分功能,比如ReentrantLock,ThreadPoolExecutor,阻塞队列,CountDownLatch,Semaphore,CyclicBarrier等等都是基于AQS实现。
首先AQS中提供了一个由volatile修饰,并且采用CAS方式修改的int类型的state变量
其次AQS中维护了一个双向链表,有head,有tail,并且每个节点都是Node对象

(2)AQS的两种功能:独占锁和共享锁

(3)AQS的内部实现

​ AQS的实现依赖内部的同步队列,也就是FIFO的双向队列,如果当前线程竞争锁失败,那么AQS会把当前线程以及等待状态信息构造成一个Node加入到同步队列中,同时再阻塞该线程。获取锁的线程释放锁以后,会从队列中唤醒一个阻塞的节点(线程)。

————————————————

22、AQS 对资源的共享方式?

AQS定义两种资源共享方式

(1)Exclusive(独占锁)

只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁:

公平锁:按照线程在队列中的排队顺序先到者先拿到锁
非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的
(2)Share(共享锁)

多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatch、 CyclicBarrier、ReadWriteLock 我们都会在后面讲到。

ReentrantReadWriteLock 可以看成是组合式,因为ReentrantReadWriteLock也就是读写锁允许多个线程同时对某一资源进行读。

不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源 state 的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。
————————————————

23、如何让 Java 的线程彼此同步?
  1. synchronized
  2. volatile:修饰常量放在内存中;
  3. ReentrantLock:可重入锁
  4. 使用局部变量实现线程同步
24、你了解过哪些同步器?请分别介绍下。

(1)Semaphore信号量同步器

特征:经典的信号量,通过计数器控制对共享资源的访问

(2)CountDownLatch 闭锁同步器

特征:必须发生指定数量的事件后才可以继续运行(比如赛跑比赛,裁判喊出3,2,1之后大家才同时跑)

(3)CyclicBarrier循环屏障同步器

特征:适用于只有多个线程都到达预定点时才可以继续执行(比如斗地主,需要等齐三个人才开始)(比如斗地主,需要等齐三个人才开始)

(4)交换器(Exchanger)同步器

(5)Phaser相位器同步器

*25、Java 中的线程池是如何实现的?

创建一个阻塞队列来容纳任务,在第一次执行任务时创建足够多的线程,并处理任务,之后每个工作线程自动从任务队列中获取线程,直到任务队列中任务为0为止,此时线程处于等待状态,一旦有工作任务加入任务队列中,即刻唤醒工作线程进行处理,实现线程的可复用性。

线程池一般包括四个基本组成部分:

(1)线程池管理器

用于创建线程池,销毁线程池,添加新任务

(2)工作线程

线程池中线程,可循环执行任务,在没有任务时处于等待状态

(3)任务队列

用于存放没有处理的任务,一种缓存机制

(4)任务接口

每个任务必须实现的接口,供工作线程调度任务的执行,主要规定了任务的开始和收尾工作,和任务的状态
————————————————

*26、创建线程池的几个核心构造参数?
// Java线程池的完整构造函数
public ThreadPoolExecutor(
  int corePoolSize, // 线程池长期维持的最小线程数,即使线程处于空闲(Idle)状态,也不会回收。
  int maximumPoolSize, // 线程数的上限
  long keepAliveTime, // 线程最大生命周期,除核心线程外的空闲线程会被销毁。
  TimeUnit unit, //空闲线程存活时间单位,keepAliveTime的计量单位                                
  BlockingQueue<Runnable> workQueue, //任务队列。当线程池中的线程都处于运行状态,而此时任务数量继续增加,则需要一个容器来容纳这些任务,这就是任务队列。新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。
  ThreadFactory threadFactory, // 线程工厂。定义如何启动一个线程,可以设置线程名称,并且可以确认是否是后台线程等。
  RejectedExecutionHandler handler // 拒绝任务处理器。由于超出线程数量和队列容量而对继续增加的任务进行处理的程序。
)
*27、线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗?

线程池中的线程是在第一次提交任务submit时创建

创建线程的方式有继承Thread和实现Runnable,重写run方法,start开始执行,wait等待,sleep休眠,shutdown停止。

(1)newSingleThreadExecutor:单线程池。

顾名思义就是一个池中只有一个线程在运行,该线程永不超时,而且由于是一个线程,当有多个任务需要处理时,会将它们放置到一个无界阻塞队列中逐个处理。
(2)newCachedThreadPool:缓冲功能的线程

建立了一个线程池,而且线程数量是没有限制的(当然,不能超过Integer的最大值),新增一个任务即有一个线程处理,或者复用之前空闲的线程,或者重亲启动一个线程,但是一旦一个线程在60秒内一直处于等待状态时(也就是一分钟无事可做),则会被终止。

(3)newFixedThreadPool:固定线程数量的线程池

在初始化时已经决定了线程的最大数量,若任务添加的能力超出了线程的处理能力,则建立阻塞队列容纳多余的任务

(4) *newScheduledThreadPool 创建一个定长线程池,*支持定时及周期性任务执行

*28、volatile 关键字的作用

对于可见性,Java 提供了 volatile 关键字来保证可见性和禁止指令重排。 volatile 提供 之前发生(happens-before) 的保证,确保一个线程的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

从实践角度而言,volatile 的一个重要作用就是和 CAS 结合,保证了原子性,详细的可以参见 java.util.concurrent.atomic 包下的类,比如 AtomicInteger。

volatile 常用于多线程环境下的单次操作(单次读或者单次写)。
————————————————

29、既然 volatile 能够保证线程间的变量可见性,是不是就意味着基于 volatile 变量的运算就是并发安全的?

volatile修饰的变量在各个线程的工作内存中不存在一致性的问题(在各个线程工作的内存中**,volatile修饰的变量也会存在不一致的情况,但是由于每次使用之前都会先刷新主存中的数据到工作内存,执行引擎看不到不一致的情况**,因此可以认为不存在不一致的问题),但是java的运算并非原子性的操作,导致volatile在并发下并非是线程安全的。
————————————————

*30、ThreadLocal 是什么?有哪些使用场景?

ThreadLocal 是一个本地线程副本变量工具类,在每个线程中都创建了一个 ThreadLocalMap 对象,简单说 ThreadLocal 就是一种以空间换时间的做法每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。通过这种方式,避免资源在多线程间共享

原理:线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java提供ThreadLocal类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险

经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。

ThreadLocal是一种多线程隔离机制,它提供了在多线程环境下对共享变量访问的安全性,在多线程访问共享变量场景中,一般解决办法是对共享变量枷锁,从而保证在同一时刻只有一个线程,能够对共享变量进行一个更新,而加锁会带来性能下降。所以ThreadLocal采用了一种空间换时间的设计思想,也就是在每一个线程里面,都有一个容器来存储共享变量的副本,然后每个线程只对自己的变量副本去做更新操作就可以,这样既解决了线程安全问题又避免了多线程竞争加锁的开销。共享变量的副本是存储在Thread类里面的一个成员变量叫ThreadLocalMap里面的,后续对副本的操作都是从ThreadLocalMap里面进行变更操作,不会影响全局共享变量的值。
	ThreadLocal可以基于副本隔离机制,来保证共享变量修改的安全性,使用场景很多,如下:
1.线程的上下文传递,在扩线程的调用场景中,可以使用ThreadLocal在不修改方法签名的情况下传递线程的上下文信息;比如在框架和中间件里面把请求的相关信息(用户信息,请求id等)存储在ThreadLocal里面,在后续的请求链中,可以方便的去访问这些信息;
2.数据库连接管理,在使用数据库连接池的情况下,可以把数据库的连接存储在ThreadLocal里面,这样每个线程可以独立管路自己的数据库连接,避免线程之间的竞争和冲突,比如Mybatis中的SqlSession对象就使用了ThreadLocal来存储当前线程的数据库会话信息
3.事务管理,在需要手动管理事务的场景中,可以使用ThreadLocal来存储事务的上下文,每个线程可以独立控制自己的事务,保证事务的隔离性,在Spring中的TransactionSynchronizationManager类就使用了ThreadLocal来存储,事务相关的上下文信息。
使用ThreadLocal的时候要注意使用规范,否则会出现内存泄漏的问题。

————————————————

31、请谈谈 ThreadLocal 是怎么解决并发安全的?

在java程序中,常用的有两种机制来解决多线程并发问题,一种是sychronized方式,通过锁机制,一个线程执行时,让另一个线程等待,是以时间换空间的方式来让多线程串行执行。而另外一种方式就是ThreadLocal方式,通过创建线程局部变量,以空间换时间的方式来让多线程并行执行。两种方式各有优劣,适用于不同的场景,要根据不同的业务场景来进行选择。

在spring的源码中,就使用了ThreadLocal来管理连接,在很多开源项目中,都经常使用ThreadLocal来控制多线程并发问题,因为它足够的简单,我们不需要关心是否有线程安全问题,因为变量是每个线程所特有的。
————————————————

32、很多人都说要慎用 ThreadLocal,谈谈你的理解,使用 ThreadLocal 需要注意些什么?

ThreadLocal 变量解决了多线程环境下单个线程中变量的共享问题,使用名为ThreadLocalMap的哈希表进行维护(key为ThreadLocal变量名,value为ThreadLocal变量的值);

使用时需要注意以下几点:

线程之间的threadLocal变量是互不影响的,
使用private final static进行修饰,防止多实例时内存的泄露问题
线程池环境下使用后将threadLocal变量remove掉或设置成一个初始值

————————————————

33、为什么代码会重排序?

在执行程序时,为了提供性能处理器和编译器常常会对指令进行重排序,但是不能随意重排序,不是你想怎么排序就怎么排序,它需要满足以下两个条件:

  • 在单线程环境下不能改变程序运行的结果;
  • 存在数据依赖关系的不允许重排序

需要注意的是:重排序不会影响单线程环境的执行结果,但是会破坏多线程的执行语义。
————————————————

34、什么是自旋

很多 synchronized 里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都加锁可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。既然 synchronized 里面的代码执行得非常快,不妨让等待锁的线程不要被阻塞,而是在 synchronized 的边界做忙循环,这就是自旋。如果做了多次循环发现还没有获得锁,再阻塞,这样可能是一种更好的策略。

————————————————

35、多线程中 synchronized锁升级的原理是什么?

synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。

锁的升级的目的:锁升级是为了降低锁带来的性能消耗。在 Java 6 之后优化 synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗
————————————————

36.Lock 接口(Lock interface)是什么?对比同步它有什么优势?

Lock 接口比同步方法和同步块提供了更具扩展性的锁操作。他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。

它的优势有:

(1)可以使锁更公平

(2)可以使线程在等待锁的时候响应中断

(3)可以让线程尝试获取锁,并在无法获取锁的时候立即返回或者等待一段时间

(4)可以在不同的范围,以不同的顺序获取和释放锁

37.如何设计实现一个集群环境下的分布式单例模式?

​ 1.如何实现跨进程级别实现单例?

​ 2.如何在任何时候保证只有一个进程访问这个实例?

​ 首先我们可以把单例对象序列化保存到文件里面,然后再把这个文件存储到外部共享文件存储组件里面;

​ 各个进程在使用这个单例对象的时候,先从外部共享存储中读取到内存,并且反序列化成对象来使用;

​ 使用完成以后,对象序列化以后存储回外部共享存储组件中,并显示的把这个对象从本地内存中删除。

​ 接着引入锁,一个进程在获取到分布式锁以后,才能访问共享单例对象,使用完以后在释放分布式锁

Zookeeper,Etcd, Redis等都可以实现分布式锁。使用的时候加分布式锁,用完后释放分布式锁

38、Java 如何实现多线程之间的通讯和协作?

可以通过中断和共享变量的方式实现线程间的通讯和协作

比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作

Java中线程通信协作的最常见的两种方式:

1、syncrhoized加锁的线程的Object类的wait()/notify()/notifyAll()

2、ReentrantLock类加锁的线程的Condition类的await()/signal()/signalAll()

线程间直接的数据交换:

通过管道进行线程间通信:1)字节流;2)字符流

————————————————

redis

redis单线程为什么那么快,有哪些线程模型?

​ redis 核心就是 如果我的数据全都在内存里,我单线程的去操作 就是效率最高的,为什么呢,因为

多线程的本质就是 CPU 模拟出来多个线程的情况,这种模拟出来的情况就有一个代价,就是上下文的切

对于一个内存的系统来说,它没有上下文的切换就是效率最高的。redis 用 单个CPU 绑定一块内存

的数据,然后针对这块内存的数据进行多次读写的时候,都是在一个CPU上完成的,所以它是单线程处

理这个事。在内存的情况下,这个方案就是最佳方案。

IO模型维度的特征

lO模型使用了多路复用器,在linux系统中使用的是EPOLL类似netty的BOSS,WORKER使用一个EventLoopGroup(threads=1)单线程的Reactor模型,每次循环取socket中的命令然后逐一操作,可以保证socket中的指令是按顺序的,不保证不同的socket也就是客户端的命令的顺序性命令操作在单线程中顺序操作,没有多线程的困扰不需要锁的复杂度,在操作数据上相对来说是原子性质的

架构设计模型

自身的内存存储数据,读写操作不设计磁盘IO
redis除了提供了Value具备类型还为每种类型实现了一些操作命令实现了计算向数据移动,而非数据想计算移动,这样在IO的成本上有一定的优势且在数据结构类型上,丰富了一些统计类性,读写操作中,写操作会0(1)负载度更新length类属性,使得读操作。

redis简介

​ 非关系型数据库,将数据放在某个内存中,单线程去操作,以为单线程不涉及到上下文的切换,读写速度非常快。默认16个数据库;
​ 五大数据类型,String(最常用,一个value可以保存512M)hash(对象,如用户信息)、list(最新消息列表)、
​ set(求交集、并集、差集等操作;共同关注、共同喜好、共同好友)、
​ zset(sort set排名榜)(三种特殊类型:GEO(地图位置)、hyperLogLog(数据计算,统计)、BitMap(用户周活跃、统计活跃用户、用户在线状态、用户签到))

Redis.conf
NETWORK 网络配置 
	绑定ip地址 端口号
通用配置
	守护进程、端口、绑定的主机地址、指定日志记录级别、日志配置、设置数据库的数量、快照设置、设置密码、持久化方式
Redis的持久化
Rdb

​ 在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
​ Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
​ Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量,环境变量,程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。
​ Rdb 保存的是 dump.rdb 文件。

AOF

以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。Aof保存的是 appendonly.aof 文件
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚
watch key1 key2 … #监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则
事务被打断 ( 类似乐观锁 )
multi # 标记一个事务块的开始( queued )
exec # 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
discard # 取消事务,放弃事务块中的所有命令
unwatch # 取消watch对所有key的监控
若在事务队列中存在命令性错误(类似于java编译性错误),则执行EXEC命令时,所有命令都不会执行
若在事务队列中存在语法性错误(类似于java的1/0的运行时异常),则执行EXEC命令时,其他正确命令会被执行,错误命令抛出异常。
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis主从复制
配从库不配主库,从库配置:
配置 slaveof 主库ip 主库端口 # 配置主从
Info replication # 查看信息

redis持久化机制: RDB和AOF

Redis 持久化
Redis 提供了不同级别的持久化方式
RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.
·AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大,
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式
你也可以同时开启两种持久化方式,在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据.因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.最重要的事情是了解RDB和AOF持久化方式的不同,让我们以RDB持久化方式开始:

RDB的优点

​ RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用干数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。
​ RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3 (可能加密),非常适用于灾难恢复。
​ RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能。
​ 与AOF相比.在恢复大的数据集的时候,RDB方式会更快一些。

RDB的缺点

如果你希望在redis意外停止工作(例如电源中断的情况下丢失的数据最少的话,那么RDB不适合你.虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据。
RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候**,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求**.如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。

AOF的优点

使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略: 无fsn每秒fsync,每次写的时候fsync.使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),旦出现故障,你最多丢失1秒的数据。
AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题。
Redis 可以在AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。而一旦新AOF 文件创建完毕,Redis 就会从日 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
·AOF 文件有序地保存了对数据库执行的所有写入操作,这些写入操作以 Redis 协议的格式保存,因此AOF[文件的内容非常容易被人读懂,对文件进行分析 (parse) 也很轻松。导出(export) AOF 文件也非常简单: 举个例子,如果你不小心执行了 FLUSHALL 命令,但只要 AOF 文件未被重写,那么只要停止服务器移除 AOF 文件未尾的 FLUSHALL 命令,并重启 Redis,就可以将数据集恢复到 FLUSHALL 执行之前的状态。

AOF的缺点

对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下,每秒 fsync 的性能依然非常高,而关闭 fsync 可以让 AOF 的速度和 RDB 一样快,即使在高负荷之下也是如此。不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间 (latency) 。

redis的过期键有哪些删除策略
过期精度

在 Redis 2.4 及以前版本,过期期时间可能不是十分准确,有0-1秒的误差

从 Redis 2.6 起,过期时间误差缩小到0-1毫秒

过期和持久

Keys的过期时间使用Unix时间戳存储(从Redis 2.6开始以毫秒为单位)。这意味着即使RedIs实例不可用,时间也是直在流逝的。
要想过期的工作处理好,计算机必须采用稳定的时间。如果你将RDB文件在两台时钟不同步的电脑间同步,有趣的事会发生(所有的 kevs装载时就会过期)
即使正在运行的实例也会检查计算机的时钟,例如如果你设置了一个key的有效期是1000秒,然后设置你的计算机时间为未来2000秒,这时key会立即失效,而不是等1000秒之后。

Redis如何淘汰过期的keys

Redis keys过期有两种方式: 被动和主动方式
当一些客户端尝试访问它时,key会被发现并主动的过期。
当然,这样是不够的,因为有些过期的keys,永远不会访问他们。无论如何,这些keys应该过期,所以定时随机测试设置keys的过期时间。所有这些过期的keys将会从密钥空间删除具体就是Redis每秒10次做的事情:
1.测试随机的20个keys进行相关过期检测。
2.删除所有已经过期的keys。
3.如果有多于25%的keys过期,重复步奏1.
这是一个平凡的概率算法基本上的假设是,我们的样本是这个密钥控件,并且我们不断重复过期检测,直到过期的keys的百分百低于25%,这意味着,在任何给定的时刻,最多会清除1/4的过期keys。

在复制AOF文件时如何处理过期

为了获得正确的行为而不牺牲一致性,当一个key过期,DEL 将会随着OF文字一起合成到所有附加的slaves。在master实例中,这种方法是集中的,并且不存在一致性错误的机会。
然而,当slaves连接到master时,不会独立过期keys (会等到master执行DEL命令),他们任然会在数据集里面存在,所以当slave当选为master时淘汰keys会独立执行,然后成为master。

扩展

绝对时间点过期
相对时间点过期
时钟轮算法

redis缓存如何回收?

回收策略

noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
allkeys-1ru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
volatile-1ru:尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放
a11keys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
volatile-tt1:回收在过期集合的键,并且优先回收存活时间(TTL) 较短的键,使得新添加的数据有空间存放
volatile-1fu: 从所有配置了过期时间的键中驱逐使用频率最少的键al1keys王 fu: 从所有键中驱逐使用频率最少的键

选择语言如果没有键满足回收的前提条件的话,策略volatile-lru,volatile-random以及volatile-tmmgnuevcuurr 左不多了
选择正确的回收策略是非常重要的,这取决于你的应用的访问模式,不过你可以在运行时进行相关的策略调整,并且监控缓存命中率和没命中的次数,通过RedisINFO命令输出以便调优。

一般的经验规则:

使用allkeys-lru策略: 当你希望你的请求符合一个幂定律分布,也就是说,你希望部分的子集元素将比其它其它元素被访问的更多。如果你不确定选择什么,这是个很好的选择。
使用allkeys-random: 如果你是循环访问,所有的键被连续的扫描,或者你希望请求分布正常(所有元素被访问的概率都差不多)。
使用volatile-ttl: 如果你想要通过创建缓存对象时设置TTL值,来决定哪些对象应该被过期。

allkeys-lru 和 volatile-random策略对于当你想要单一的实例实现缓存及持化一些键时很有用。不过一般运行两个实例是解决这个问题的更好方法。
为了键设置过期时间也是需要消耗内存的,所以使用allkeys-lru这种策略更加高效,因为没有必要为键取设置过期时间当内存有压力时.

回收进程如何工作
理解回收进程如何工作是非常重要的:

一个客户端运行了新的命令- 添加了新的数据。
Redis检查内存使用情况,如果大于maxmemory的限制,则根据设定好的策略进行回收。
一个新的命令被执行,等等。所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下.如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键》,不用多久内存限制就会被这个内存使用量超越。
redis集群方案有哪些
常见集群分类
  • 主从复制集群
  • 分片集群
redis有那些:
  • 主从复制集群,手动切换
  • 带有哨兵的HA的主从复制集群
  • 客户端实现路由索引的分片集群
  • 使用中间件代理层的分片集群
  • redis自身实现的cluster分片集群
redis事务是怎么实现的?

MULTI 、EXEC 、DISCARD 和 WATCH 是 Redis 事务相关的命令。事务可以一次执行多个命令,并且带有以下两个重要的保证:

事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行

EXEC 命令负责触发并执行事务中的所有命令:

如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行 EXEC ,那么事务中的所有命令都不会被执行。另一方面,如果客户端成功在开启事务之后执行 EXEC ,那么事务中的所有命令都会被执行。

当使用 AOF方式做持久化的时候, Redis 会使用单个 write(2) 命令将事务写入到磁盘中。

然而,如果 Redis 服务器因为某些原因被管理员杀死,或者遇上某种硬件故障,那么可能只有部分事务命令会被成功写入到磁盘中。

如果 Redis 在重新启动时发现 AOF 文件出了这样的问题,那么它会退出,并汇报一个错误。使用redis-check-aof程序可以修复这一问题: 它会移除AOF 文件中不完整事务的信息,确保服务器可以顺利启动。
从2.2版本开始,Redis 还可以通过乐观锁 (optimistic lock) 实现 CAS (check-and-set) 操作,具体信息请参考文档的后半部分。

事务中的错误

使用事务时可能会遇上以下两种错误:

事务在执行 EXEC 之前,入队的命令可能会出错。比如说,命今可能会产生语法错误(参数数量错误,参数名错误,等等),或者其他更严重的错误,比如内存不足(如果服务器使用 maxmemory 设置了最大内存限制的话)。命令可能在 EXEC 调用之后失败。举个例子,事务中的命令可能处理了错误类型的键,比如将列表命令用在了字符串键上面,诸如此类。

​ 对于发生在 EXEC 执行之前的错误,客户端以前的做法是检查命令入队所得的返回值: 如果命令入队时返回QUEUED ,那么入队成功:否则,就是入队失败。如果有命令在入队时失败,那么大部分客户端都会停止并取消这个事务。
​ 不过,从 Redis 2.6.5 开始,服务器会对命令入队失败的情况进行记录,并在客户端调用 EXEC 命令时,拒绝执行并自动放弃这个事务。
​ 在 Redis 2.6.5 以前,Redis 只执行事务中那些入队成功的命令,而忽略那些入队失败的命令。而新的处理方式则使得在流水线(pipeline)中包含事务变得简单,因为发送事务和读取事务的回复都只需要和服务器进行一次通讯。

​ 至于那些在 EXEC 命令执行之后所产生的错误,并没有对它们进行特别处理: 即使事务中有某个/某些命令在执行时产生了错误,事务中的其他命令仍然会继续执行.

为什么 Redis 不支持回滚 (roll back)

如果你有使用关系式数据库的经验,那么“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。
以下是这种做法的优点:

Redis 命令只会因为错误的语法而失败 (并且这些问题不能在入队时发现)或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。

因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

有种观点认为 Redis 处理事务的做法会产生 bug ,然而需要注意的是,在通常情况下,回滚并不能解决编程错误带来的问题。举个例子,如果你本来想通过 NCR 令将键的值加上1,却不小心加上了2,又或者对错误类型的键执行了 INCR, 回滚是没有办法处理这些情况的。

redis主从复制的原理是什么?
主从复制机制
当一个 master 实例和一个 slave 实例连接正常时, master 会发送一连串的命令流来保持对 slave 的更新,以便于将自身数据集的改变复制给 slave , : 包括客户端的写入、key 的过期或被逐出等等。
当 master 和 slave 之间的连接断开之后,因为网络问题、或者是主从意识到连接超时, save 重新连接上master 并会尝试进行部分重同步: 这意味着它会尝试只获取在断开连接期间内丢失的命令流。
slave 会请求进行全量重同步。这会涉及到一个更复杂的过程,例如 master 需要创建所当无法进行部分重同步时,有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到 slave。
主从复制的关注点
	Redis 使用异步复制,slave 和 master 之间异步地确认处理的数据量
	一个master 可以拥有多个 slave
	slave 可以接受其他 slave 的连接。除了多 slave 可以连接到同一个 master 之外, slave 之间也可以像层叠状的结构 (cascading-like structure) 连接到其他 slave 。自 Redis 4,0 起,所有的 sub-slave 将会master 收到完全一样的复制流。
	Redis 复制在 master 侧是非阻塞的。这意味着 master 在一个或多 save 进行初次同步或者是部分重同步时可以继续处理查询请求。
	复制在 slave 侧大部分也是非阻塞的。当 slave 进行初次同步时,它可以使用旧数据集处理查询请求,假设你在redis,conf 中配置了让 Redis 这样做的话。否则,你可以配置如果复制流断开, Redis slave 会返回一个error 给客户端。但是,在初次同步之后,旧数据集必须被删除,同时加载新的数据集。 slave 在这个短暂的时间窗口内(如果数据集很大,会持续较长时间),会阻寒到来的连接请求。自 Redis 4.0 开始,可以配置 Redis 使删除旧数据集的操作在另一个不同的线程中进行,但是,加载新数据集的操作依然需要在主线程中进行并且会阻塞 slave
复制既可以被用在可伸缩性,以便只读查询可以有多个 slave 进行(例如 O(N) 复杂度的操作可以被下放到 slave),或者仅用于数据安全。
可以使用复制来避免 master 将全部数据集写入磁盘造成的开销:一种典型的技术是配置你的 master Redis.conf以避免对磁盘进行持久化,然后连接一个 slave ,其配置为不定期保存或是启用 AOF。但是,这个设置必须小心处理因为重新启动的 master 程序将从一个空数据集开始: 如果一个 save 试图与它同步,那么这个 slave 也会被清空。
任何时候数据安全性都是很重要的,所以如果 master 使用复制功能的同时未配置持久化,那么自动重启进程这项应该被禁用。
缓存雪崩、缓存穿透、缓存击穿在实际中如何处理?
缓存穿透

缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

解决方案
  • 接口层增加校验:用户鉴权、参数校验(请求参数是否合法、请求字段是否不存在等等);
  • 缓存空值/缺省值:发生缓存穿透时,我们可以在Redis中缓存一个空值或者缺省值(例如,库存缺省值为0),这样就避免了把大量请求发送给数据库处理,保持了数据库的正常运行。
  • 最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
缓存击穿

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常"热点”的数据.这个时候,需要考虑一个问题:缓存被”击穿”的问题,这个和缓存雪的区别在于这里针对某-key缓存,前者则是很多key。
缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮.

解决方案
  1. 设置热点数据永不过期:不设置失效时间,有更新的话,需要更新缓存;
  2. 加互斥锁:单机可以使用synchronizedlock,分布式可以使用lua脚本。
缓存雪崩

缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。

解决方案

均匀过期:给热点数据设置不同的过期时间,给每个key的失效时间加一个随机值;
设置热点数据永不过期:不设置失效时间,有更新的话,需要更新缓存;
服务降级:指服务针对不同的数据采用不同的处理方式:
业务访问的是非核心数据,直接返回预定义信息、空值或者报错;
业务访问核心数据,则允许访问缓存,如果缓存缺失,可以读取数据库。

总结
数据库不存在,高并发,少量key穿透: 缓存不存在,

击穿:缓存不存在,数据库存在,高并发,少量key

雪崩: 缓存不存在,数据库存在,高并发,大量key

语义有些许差异,但是,都可以使用限流的互斥锁,保障数据库的稳定

88、jsp 和 servlet 有什么区别?

(1)servlet是服务器端的Java程序,它担当客户端和服务端的中间层。

(2)jsp全名为Java server pages,中文名叫Java服务器页面。一种动态网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者。

(3)JVM只能识别Java代码,不能识别JSP,JSP编译后变成了servlet,web容器将JSP的代码编译成JVM能够识别的Java类(servlet)。

(4)JSP有内置对象、servlet没有内置对象。
————————————————

89、jsp 有哪些内置对象?作用分别是什么?

JSP九大内置对象:

pageContext(页面上下文对象):页面上下文对象,相当于页面中所有功能的集合,通过它可以获取JSP页面的out、request、response、session、application对象。
request:请求对象
response(响应对象)
session(会话对象)
application(应用程序对象),application实现了用户间数据的共享,可存放全局变量,它开始于服务器启动,知道服务器关闭。
page(页面对象),就是JSP本身。
exception(异常对象):在JSP页面中处理异常。
out(输出对象):该对象是一个输出流对象,用于向页面输出内容。
config(配置对象):取得服务器的配置信息。
————————————————

90、forward 和 redirect 的区别?

forward是直接请求转发;redirect是间接请求转发,又叫重定向
forward,客户端和浏览器执行一次请求;redirect,客户端和浏览器执行两次请求
forward,经典的MVC模式就是forward;redirect,用于避免用户的非正常访问。(例如用户非正常访问,servlet就可以将HTTP请求重定向到登录页面)。
forward,地址不变;redirect,地址改变
forward常用方法:RequestDispatcher类的forward()方法;redirect常用方法:HttpServletRequest类的sendRedirect()方法。

91、说一下 jsp 的 4 种作用域?

application、session、request、page
————————————————

*92、session 和 cookie 有什么区别?

(1)存储位置不同

cookie在客户端浏览器;
session在服务器;
(2)存储容量不同

cookie<=4K,一个站点最多保留20个cookie;
session没有上线,出于对服务器的保护,session内不可存过多东西,并且要设置session删除机制;
(3)存储方式不同

cookie只能保存ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据
session中能存储任何类型的数据,包括并不局限于String、integer、list、map等;
(4)隐私策略不同

cookie对客户端是可见的,不安全;
session存储在服务器上,安全;
(5)有效期不同

开发可以通过设置cookie的属性,达到使cookie长期有效的效果;
session依赖于名为JESSIONID的cookie,而cookie JSESSIONID的过期时间默认为-1,只需关闭窗口该session就会失效,因而session达不到长期有效的效果;
(6)跨域支持上不同

cookie支持跨域;
session不支持跨域;
————————————————

93、如果客户端禁止 cookie 能实现 session 还能用吗?

一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。

如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。

但是可以通过其他方法在禁用 cookie 的情况下,可以继续使用session。

  1. 通过url重写,把 sessionid 作为参数追加的原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
  2. 服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
  3. 通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。

————————————————

94、什么是上下文切换?

多线程编程中一般线程的个数都大于 CPU 核心的个数,而一个 CPU 核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU 采取的策略是为每个线程分配时间片并轮转的形式。当一个线程的时间片用完的时候就会重新处于就绪状态让给其他线程使用,这个过程就属于一次上下文切换。

概括来说就是:当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。

上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。

Linux 相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。
————————————————

* 95、cookie、session、token

96、说一下 session 的工作原理?

当客户端登录完成后,会在服务端产生一个session,此时服务端会将sessionid返回给客户端浏览器。客户端将sessionid储存在浏览器的cookie中,当用户再次登录时,会获得对应的sessionid,然后将sessionid发送到服务端请求登录,服务端在内存中找到对应的sessionid,完成登录,如果找不到,返回登录页面。

————————————————

97、http 响应码 301 和 302 代表的是什么?有什么区别?

301和302状态码都表示重定向,当浏览器拿到服务器返回的这个状态码后悔自动跳转到一个新的URL地址。
301代表永久性重定向,旧地址被永久移除,客户端向新地址发送请求。
302代表暂时性重定向,旧地址还在,客户端继续向旧地址发送请求。
303代表暂时性重定向,重定向到新地址时,必须使用GET方法请求新地址。
307代表暂时性重定向,与302的区别在于307不允许从POST改为GET。
308代表永久性重定向,与301的区别在于308不允许从POST改为GET。
————————————————

98、简述 tcp 和 udp的区别?

TCP是传输控制协议,UDP是用户数据表协议;
TCP长连接,UDP无连接;
UDP、程序结构较简单,只需发送,无须接收;
TCP可靠,保证数据正确性、顺序性;UDP不可靠,可能丢数据;
TCP适用于少量数据,UDP适用于大量数据传输;
TCP速度慢,UDP速度快;
————————————————

99、tcp 为什么要三次握手,两次不行吗?为什么?

因为客户端和服务端都要确认连接,

①客户端请求连接服务端;

②针对客户端的请求确认应答,并请求建立连接;

③针对服务端的请求确认应答,建立连接;

两次无法确保A能收到B的数据;

100、OSI 的七层模型都有哪些?

在这里插入图片描述

101、get 和 post 请求有哪些区别?

get请求参数是连接在url后面的,而post请求参数是存放在requestbody内的;
get请求因为浏览器对url长度有限制,所以参数个数有限制,而post请求参数个数没有限制;
因为get请求参数暴露在url上,所以安全方面post比get更加安全
get请求只能进行url编码,而post请求可以支持多种编码方式
get请求参数会保存在浏览器历史记录内,post请求并不会;
get请求浏览器会主动隐藏cache,post并不会,除非主动设置;
get请求产生1个tcp数据包,post请求产生2个tcp数据包;
在浏览器进行回退操作时,get请求是无害的,而post请求则会重新请求一次
浏览器在发送get请求时会将header和data一起发送给服务器,服务器返回200状态码,而在发送post请求时,会先将header发送给服务器,服务器返回**100,**之后再将data发送给服务器,服务器返回200 OK;
————————————————

102、什么是 XSS 攻击,如何避免?

xss(Cross Site Scripting),即跨站脚本攻击,是一种常见于web应用程序中的计算机安全漏洞。指的是在用户浏览器上,在渲染DOM树的时候,执行了不可预期的JS脚本,从而发生了安全问题。

XSS就是通过在用户端注入恶意的可运行脚本,若服务端对用户的输入不进行处理,直接将用户的输入输出到浏览器,然后浏览器将会执行用户注入的脚本。 所以XSS攻击的核心就是浏览器渲染DOM的时候将文本信息解析成JS脚本从而引发JS脚本注入,那么XSS攻击的防御手段就是基于浏览器渲染这一步去做防御。只要我们使用HTML编码将浏览器需要渲染的信息编码后,浏览器在渲染DOM元素的时候,会自动解码需要渲染的信息,将上述信息解析成字符串而不是JS脚本,这就是我们防御XSS攻击的核心想法。

预防

1、获取用户的输入,不用innerHtml,用innerText.
2、对用户的输入进行过滤,如对& < > " ’ /等进行转义;
————————————————

103、什么是 CSRF 攻击,如何避免?

跨站请求伪造,通常缩写为 CSRF 或者 XSRF。是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任CSRF 利用的是网站对用户网页浏览器的信任

1、攻击细节

1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2.在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4.网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。

防御CSRF攻击:

目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并验证;在 HTTP 头中自定义属性并验证。

1)验证 HTTP Referer 字段

​ **根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。**在通常情况下,访问一个安全受限页面的请求来自于同一个网站,比如需要访问 http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,用户必须先登陆 bank.example,然后通过点击页面上的按钮来触发转账事件。这时,该转帐请求的 Referer 值就会是转账按钮所在的页面的 URL,通常是以 bank.example 域名开头的地址。而如果黑客要对银行网站实施 CSRF 攻击,他只能在他自己的网站构造请求,当用户通过黑客的网站发送请求到银行时,该请求的 Referer 是指向黑客自己的网站。因此,要防御 CSRF 攻击,银行网站只需要对于每一个转账请求验证其 Referer 值,如果是以 bank.example 开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。

(2)在请求地址中添加 token 并验证

​ **CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。**要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 ,这样就把 token 以参数的形式加入请求了。

该方法还有一个缺点是难以保证 token 本身的安全。

(3)在 HTTP 头中自定义属性并验证

​ **这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。**通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。

104、如何实现跨域?说一下 JSONP 实现原理?

1、jsonp原理详解——终于搞清楚jsonp是啥了

2、最流行的跨域方案cors

cors是目前主流的跨域解决方案,跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。

3、最方便的跨域方案Nginx

nginx是一款极其强大的web服务器,其优点就是轻量级、启动快、高并发。

现在的新项目中nginx几乎是首选,我们用node或者java开发的服务通常都需要经过nginx的反向代理。

反向代理的原理很简单,即所有客户端的请求都必须先经过nginx的处理,nginx作为代理服务器再讲请求转发给node或者java服务,这样就规避了同源策略。
————————————————

105、websocket应用的是哪个协议

WebSocket是一个允许Web应用程序(通常指浏览器)与服务器进行双向通信的协议HTML5的WebSocket API主要是为浏览器端提供了一个基于TCP协议实现全双工通信的方法

WebSocket优势: **浏览器和服务器只需要要做一个握手的动作,在建立连接之后,双方可以在任意时刻,相互推送信息。**同时,服务器与客户端之间交换的头信息很小。
————————————————

106、说一下 tcp 粘包是怎么产生的?

发送方需要等缓冲区满才能发送出去,造成粘包;
接收方不及时接收缓冲区的包,造成粘包;

107、请列举出在 JDK 中几个常用的设计模式?

1、单例模式

作用:保证类只有一个实例。

JDK中体现:Runtime类。2

2、静态工厂模式

作用:代替构造函数创建对象,方法名比构造函数清晰。

JDK中体现:Integer.valueOf、Class.forName

3、抽象工厂

作用:创建某一种类的对象。

JDK中体现:Java.sql包。

4、原型模式

clone();

原型模式的本质是拷贝原型来创建新的对象,拷贝是比new更快的创建对象的方法,当需要大批量创建新对象而且都是同一个类的对象的时候考虑使用原型模式

一般的克隆只是浅拷贝(对象的hash值不一样,但是对象里面的成员变量的hash值是一样的)。

有些场景需要深拷贝,这时我们就要重写clone方法,以ArrayList为例:
5、适配器模式

作用:使不兼容的接口相容

JDK中体现:InputStream、OutputStream。

6、装饰器模式

作用:为类添加新的功能,防止类继承带来的类爆炸。

JDK中体现:io类、Collections、List。

7、外观模式

作用:封装一组交互类,一直对外提供接口。

JDK中体现:logging包。

8、享元模式

作用:共享对象、节省内存

JDK中体现:Integer.valueOf、String常量池。

9、代理模式

作用:

(1)透明调用被代理对象,无须知道复杂实现细节;

(2)增加被代理类的功能;

JDK中体现:动态代理。

10、迭代器模式

作用:将集合的迭代和集合本身分离。

JDK中体现:Iterator

11、命令模式

作用:封装操作,使接口一致

JDK中体现:Runable、Callable、ThreadPoolExecutor。

————————————————

108、什么是设计模式?你是否在你的代码里面使用过任何设计模式?

1、什么是设计模式?

设计模式是解决软件开发某些特定问题而提出的一些解决方案,也可以理解为解决问题的一些固定思路。

通过设计模式可以帮助我们增强代码的可复用性、可扩展性、灵活性

我们使用设计模式的最终目的是实现代码的高内聚、低耦合

2、设计模式的七大原则

  1. 单一职责原则
  2. 接口隔离原则
  3. 依赖倒转原则
  4. 里式替换原则
  5. 开闭原则
  6. 迪米特法则
  7. 合成复用原则

3、你是否在你的代码里面使用过任何设计模式?

(1)单例模式

JDK种的runtime,Spring种的singeton。

(2)简单工厂模式

Spring的BeanFactory,根据传入一个唯一标识来获得bean对象。

(3)原型模式

clone()

(4)代理模式

Spring的AOP中,Spring实现AOP功能的原理就是代理模式,①JDK动态代理。②CGLIB动态代理,使用Advice(通知)对类进行方法级别的切面增强。

(5)装饰器模式

为类添加新的功能,防止类爆炸;

IO流、数据源包装,Spring中用到的装饰器模式表现在Wrapper。
————————————————

109、Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式

  1. 保证程序只有一个对象的实例,叫做单例模式;
  2. 内部类的方式实现单例模式,是线程安全的;
  3. 双重验证方式实现单例模式也是线程安全的;

110、在 Java 中,什么叫观察者设计模式(observer design pattern)?

1、观察者模式是一种一对多的依赖关系,让多个观察者同时监听某一主题对象。当这个主题对象发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

2、JAVA提供的对观察者模式的支持

在JAVA语言的java.util库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。
————————————————

111、使用工厂模式最主要的好处是什么?在哪里使用?

1、工厂模式好处

  • 良好的封装性、代码结构清晰;
  • 扩展性好,如果想增加一个产品,只需扩展一个工厂类即可;
  • 典型的解耦框架;

2、在哪里使用?

  • 需要生成对象的地方;
  • 不同数据库的访问;

————————————————

112、请解释自动装配模式的区别?

有五种自动装配的方式,可以用来指导 Spring 容器用自动装配方式来进行依赖注入。

1、no

默认的方式是不进行自动装配,通过显式设置 ref 属性来进行装配。

2、byName

通过参数名 自动装配,Spring 容器在配置文件中发现 bean的 autowire 属性被设置成 byname,之后容器试图匹配、装配和该 bean 的属性具有相同名字的 bean。

3、byType:

通过参数类型自动装配,Spring 容器在配置文件中发现 bean的 autowire 属性被设置成 byType,之后容器试图匹配、装配和该 bean 的属性具有相同类型的 bean。如果有多个 bean 符合条件,则抛出错误。

4、constructor

这个方式类似于 byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。

5、autodetect

首先尝试使用 自动检测(autodetect)来自动装配,如果无法工作,则使用 byType 方式。
————————————————

Spring

*Spring是如何处理线程安全问题?

在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了”时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了”空间换时间”的方式。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

@Component, @Controller, @Repository@Service有什么区别?

@Component: 这将java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller: 这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean 会自动导入到 loC 容器中

@Service: 此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
@Repository: 这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 loC容器,并使未经检查的异常有资格转换为 Spring DataAccessException。

@AutoWired和@Resoure有什么区别?

相同点方面如下:
@Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。不同点方面如下:
第一个提供方: @Autowired是由orgspringframework.beans.factory.annotation.Autowired提供,换句话说就是由Spring提供;@Resource是由javax.annotation.Resource提供,即2EE提供,需要]DK1.6及以上。

​ 第二个注入方式: @Autowired只按照byType 注入;@Resource默认按byName自动注入,也提供按照byType 注入;
​ 第三个属性: @Autowired按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。@Resource有两个中重要的属性: name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时,@Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

spring事务7种传播行为

spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
第一个propagation_required: 如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
第二个propagation_supports: 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
第三个propagation_mandatory: 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
第四个 propagation_requires_new: 创建新事务,无论当前存不存在事务,都创建新事务。
第五个 propagation_not_supported: 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
第六个 propagation_never: 以非事务方式执行,如果当前存在事务,则抛出异常.
第七个 propagation_nested: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
事务的传播行为说的是当

spring 有五大隔离级别

spring 有五大隔离级别,默认值为 SOLATION DEFAULT (使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:
第一个isolation_default: 用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;

第二个isolation_read_uncommitted: 未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读) ;
第三个isolation_read_committed:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
第四个isolation_repeatable_read:可重复读;,保证多次读取同一个数据时,:其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据 (会造成幻读) ,MySQL 的默认级别;
第五个isolation_serializable: 序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读幻读。

什么是IOC容器?

控制反转即IoC(lnversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的”控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
Spring IOC 负责创建对象,管理对象 (通过依赖注入 (DI),装配对象,配置对象,并且管理这些对象的整个生命周期。

IOC有什么作用?

第一管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
第二解耦,由容器去维护具体的对象
第三托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器
程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的

spring 他是如何解决这个循环依赖的?

三级缓存

Spring Profiles

Spring Profiles 允许用户根据配置文件 (dev,test,prod 等)来注册 bean。因此,当应用程序在开发中运行时,只有某些 bean 可以加载,而在 PRODUCTION中,某些其他 bean 可以加载。假设我们的要求是 Swagger 文档仅适用于 QA环境,并且禁用所有其他文档。这可以使用配置文件来完成。Spring Boot 使得使用配置文件非常简单。

Spring如何结局跨域问题?

跨域可以在前端通过]SONP 来解决,但是]SONP 只可以发送 GET 请求,无法发送其他类型的请求,在 RESTful风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource sharing) 来解决跨域问题。这种解决方案并非 Spring Boot 特有的,在传统的 SSM 架中,就可以通过 CORS 来解决跨域问题,只不过之前我们是在XML 文件中配置 CORS,现在可以通过实现WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。

114、什么是 Spring 框架?Spring 框架有哪些主要模块?

Spring是一个控制反转和面向切面的容器框架。

Spring有七大功能模块:

1、Core

Core模块是Spring的核心类库,Core实现了IOC功能。

2、AOP

Apring AOP模块是Spring的AOP库,提供了面向切面编程(拦截器)机制,并提供常见的拦截器,供用户自定义和配置。

3、orm

提供对常用 对象关系映射框架的管理和支持,hibernate、mybatis等。

4、Dao

Spring提供对JDBC的支持,对JDBC进行封装。

5、Web

对Struts2的支持。

6、Context

Context模块提供框架式的Bean的访问方式,其它程序可以通过Context访问Spring的Bean资源,相当于资源注入

7、MVC

MVC模块为spring提供了一套轻量级的MVC实现,即Spring MVC。
————————————————

115、使用 Spring 框架能带来哪些好处?

1、容器

Spring是一个容器,**管理对象的生命周期和配置。**基于一个可配置原型prototype,你的bean可以使单利的,也可以每次需要时都生成一个新的实例。

2、控制反转IOC

Spring通过控制反转实现松耦合。

3、支持AOP

Spring提供对AOP的支持,它允许将一些通用任务,如安全、事务、日志等进行集中式处理,从而提高了程序的复用性。

4、轻量级框架

5、方便测试

Spring提供Junit4的支持,可以通过注解方便测试spring程序。

6、对Java中很多API进行了封装

7、方便集成各种优秀框架

如Struts、hibernate、mybstis。

8、支持声明式事务处理

只需通过配置就可以完成对事务的管理,而无须手动编程。
————————————————

117、什么是控制反转(IOC)?什么是依赖注入?

借助Spring实现具有依赖关系的对象之间的解耦。

对象A运行需要对象B,由主动创建变为IOC容器注入,这便是控制反转。

获得依赖对象的过程被反转了,获取依赖对象的过程由自身创建变为由IOC容器注入,这便是依赖注入。

118、BeanFactory 和 ApplicationContext 有什么区别?

1、BeanFactory是Spring的最底层接口,包含bean的定义,管理bean的加载,实例化,控制bean的生命周期,特点是每次获取对象时才会创建对象。

ApplicationContext是BeanFactory的子接口,拥有BeanFactory的全部功能,并且扩展了很多高级特性,每次容器启动时就会创建所有的对象。

2、BeanFactory通常以编程的方式被创建,ApplicationContext可以以声明的方式创建,如使用ContextLoader。————————————————

119、什么是 JavaConfig?

JavaConfig是Spring3.0新增的概念,就是以注解的形式取代Spring中繁琐的xml文件

JavaConfig结合了xml的解耦和java编译时检查的优点。

@Configuration,表示这个类是配置类;
@ComponentScan,相当于xml的<context:componentScan basepackage=>;
@Bean,相当于xml的;
@EnableWebMvc,相当于xml的mvc:annotation-driven;
@ImportResource,相当于xml的;
@PropertySource,用于读取properties配置文件;
@Profile,一般用于多环境配置,激活时可用@ActiveProfile(“dev”)注解;
————————————————

120、什么是 ORM 框架?

ORM(Object-relational mapping),对象关系映射。

是为了解决面向对象与关系型数据库存在的不匹配问题。

ORM框架的优点:

  1. 开发效率更高
  2. 数据访问更抽象、轻便
  3. 支持面向对象封装

121、Spring 有几种配置方式?

1、xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
    <bean id="jackma" class="com.tyq.dto.User">
        <property name="name" value="jackma" />
        <property name="age" value="55" />
        <property name="dog" ref="jm" />
     </bean>
 
    <bean id="jm" class="com.tyq.dto.Dog">
        <property name="name" value="jack" />
        <property name="breed" value="金毛" />
        <property name="age" value="2" />
    </bean>
</beans>

2、基于注解的方式

项目越来越大,基于xml配置太麻烦,Spring 2.x时代提供了声明bean的注解。

(1)Bean的定义

@Component、@Controller、@Service、@Repository。

(2)Bean的注入

@Autowire

3、基于Java的方式

Spring 3.x以后,可以通过Java代码装配Bean。

@Configuration
public class DemoConfig {
    @Bean
    public User zs(){
        return new User();
    }
    @Bean
    public Dog dog(){
        return  new Dog();
    }
    @Bean  //两个狗
    public Dog haqi(){
        return new Dog();
    }
}

122、请解释 Spring Bean 的生命周期?

  1. 通过构造器或工厂方法创建bean实例;
  2. 为bean的属性赋值;
  3. 调用bean的初始化方法;
  4. 使用bean;
  5. 当容器关闭时,调用bean的销毁方法;

*123、Spring Bean 的作用域之间有什么区别?

Spring容器中的bean可以分为5个范围:
singleton:这种bean范围是默认的,这种范围确保不管接受多少请求,每个容器中只有一个bean的实例,单例模式;
prototype:为每一个bean提供一个实例;
request:在请求bean范围内为每一个来自客户端的网络请求创建一个实例,在请求完毕后,bean会失效并被垃圾回收器回收;
session:为每个session创建一个实例,session过期后,bean会随之消失;该作用域仅在基于web的Spring.ApplicationContext情形下有效。
global-session: 在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于.web的Spring ApplicationContext情形下有效。

————————————————

124、如何在 Spring Boot 中禁用 Actuator 端点安全性?

默认情况下,所有敏感的HTTP端点都是安全的,只有具有00角色的用户才能访问它们。安全性是使用标准的HTTPServletRequest.isUserInRole方法实施的。我们可以使用management.security.enable = false来禁用安全性。只有在执行机构端点在防火墙后访问时,才建议禁用安全性。
————————————————

125、什么是 Spring inner beans?

在Spring框架中,无论何时bean被使用时,当仅被调用一个属性。可以将这个bean声明为内部bean。内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现。比如,在我们的应用程序中,一个Customer类引用了一个Person类,我们要做的是创建一个Person实例,然后再Customer内部使用。

package com;
 
public class Customer {
    private Person person;
}
 
class Person{
    private int id;
    private String name;
    private int age;
}
<bean id="CustomerBean" class="com.Customer">
	<property name="person">
		<bean class="com.person">
			<property name="id" value=1 />
			<property name="name" value="素小暖" />
			<property name="age" value=18 />
		</bean>
	</property>
</bean>

————————————————

*126、Spring 框架中的单例 Beans 是线程安全的么?

不是,Spring框架中的单例bean不是线程安全的。

spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。

实际上大部分时候 spring bean 无状态的(比如 dao 类),所有某种程度上来说 bean 也是安全的,但如果 bean有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton"变更为”prototype”,这样请求 bean 相当于 new Bean(了,所以就可以保证线程安全了。
有状态就是有数据存储功能。
无状态就是不会保存数据。
————————————————

127、请解释 Spring Bean 的自动装配?

Spring支持IOC,自动装配不用类实例化,直接从bean容器中取。

1、配置在xml中

2、@Autowired自动装配 ————————————————

128、如何开启基于注解的自动装配?

要使用 @Autowired,需要注册 AutowiredAnnotationBeanPostProcessor,可以有以下两种方式来实现:

引入配置文件中的<bean>下引入 <context:annotation-config>

<beans>
    <context:annotation-config />
</beans>

在bean配置文件中直接引入AutowiredAnnotationBeanPostProcessor

129、什么是 Spring Batch?

1、什么是spring batch?

spring batch是一个轻量级的、完善的批处理框架,它主要的目的在于帮助企业建立健壮、高效的批处理应用

spring batch是Spring的一个子项目,它使用java语言并基于spring框架作为基础开发,使得已经使用Spring框架的开发者或者是企业可以更加容易访问和利用企业服务。

spring batch提供了大量可重用的组件,包括了日志、追踪、事务、任务作业统计、任务重启、跳过、重复、资源管理。

对大数据量和高性能的批处理任务,spring batch同样提供了高级功能和特性来支持。

例如:分区功能、远程功能。

总的来说,spring batch可以支持简单的、复杂的和大数据量的批处理作业。

2、spring batch业务场景

周期性的提交批处理

把一个任务并行处理

消息驱动应用分级处理

大规模并行批处理

手工或调度使任务失败之后重新启动

有依赖步骤的顺序执行(使用工作流驱动扩展)

处理时跳过部分记录

成批事务:为小批量的或有的存储过程/脚本的场景使用
————————————————

130、spring mvc 和 struts 的区别是什么?

1、拦截机制的不同

Struts2是类级别的拦截,每次请求就会创建一个Action,和Spring整合时Struts2的ActionBean注入作用域是原型模式prototype,然后通过setter,getter吧request数据注入到属性。Struts2中,一个Action对应一个request,response上下文,在接收参数时,可以通过属性接收,这说明属性参数是让多个方法共享的。Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了,只能设计为多例。

SpringMVC是方法级别的拦截,一个方法对应一个Request上下文,所以方法直接基本上是独立的,独享request,response数据。而每个方法同时又何一个url对应,参数的传递是直接注入到方法中的,是方法所独有的。处理结果通过ModeMap返回给框架。在Spring整合时,SpringMVC的Controller Bean默认单例模式Singleton,所以默认对所有的请求,只会创建一个Controller,有应为没有共享的属性,所以是线程安全的,如果要改变默认的作用域,需要添加@Scope注解修改。

Struts2有自己的拦截Interceptor机制,SpringMVC这是用的是独立的Aop方式,这样导致Struts2的配置文件量还是比SpringMVC大。

2、底层框架的不同

Struts2采用Filter(StrutsPrepareAndExecuteFilter)实现,SpringMVC(DispatcherServlet)则采用Servlet实现。Filter在容器启动之后即初始化;服务停止以后坠毁,晚于Servlet。Servlet在是在调用时初始化,先于Filter调用,服务停止后销毁。

3、性能方面

Struts2是类级别的拦截,每次请求对应实例一个新的Action,需要加载所有的属性值注入,SpringMVC实现了零配置,由于SpringMVC基于方法的拦截,有加载一次单例模式bean注入。所以,SpringMVC开发效率和性能高于Struts2

4、配置方面

spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高。
————————————————

131、请举例解释@Required 注解?

@Required注解应用于bean属性的setter方法,它表明影响的bean属性在配置时必须放在XML配置文件中。

132、Spring常用注解

使用XML注解唯一需要注意的就是,必须开启注解的支持:

<context:component-scan base-package="com.guo"></context:component-scan>
<context:annotation-config/>
二、Spring的常用注解

1、给容器中注入组件
(1)包扫描+组件标注注解

@Component:泛指各种组件

@Controller、@Service、@Repository都可以称为@Component。

@Controller:控制层

@Service:业务层

@Repository:数据访问层

(2)@Bean,对应xml文件中的标签

导入第三方包里面的注解

(3)@Import

@Import(要导入到容器中的组件);

@ImportSelector:返回需要导入的组件的全类名数组;

3、@JsonIgnore

(1)作用

在json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。

(2)使用方法

一般标记在属性或者方法上,返回的json数据即不包含该属性。

4、初始化和销毁方法

(1)通过@Bean(initMethod=“init”,destoryMethod=“destory”)方法

(2)通过bean实现InitializingBean来定义初始化逻辑,DisposableBean定义销毁逻辑

5、Java配置类相关注解

@Configuration

声明当前类为配置类;

@Bean

注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式;

@ComponentScan

用于对Component进行扫描;

6、切面(AOP)相关注解

@Aspect 声明一个切面

@After 在方法执行之后执行(方法上)

@Before 在方法执行之前执行(方法上)

@Around 在方法执行之前与之后执行(方法上)

@PointCut 声明切点

在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持
————————————————

7、@Bean的属性支持
@Scope设置类型包括:

设置Spring容器如何新建Bean实例(方法上,得有@Bean)

① Singleton

(单例,一个Spring容器中只有一个bean实例,默认模式),

② Protetype

(每次调用新建一个bean),

③ Request

(web项目中,给每个http request新建一个bean),

④ Session

(web项目中,给每个http session新建一个bean),

⑤ GlobalSession

(给每一个 global http session新建一个Bean实例)
————————————————

8、@Value注解
9、环境切换

@Profile

指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件。

@Conditional

通过实现Condition接口,并重写matches方法,从而决定该bean是否被实例化。

10、异步相关

@EnableAsync

配置类中通过此注解开启对异步任务的支持;

@Async

在实际执行的bean方法使用该注解来声明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)

11、定时任务相关

@EnableScheduling

在配置类上使用,开启计划任务的支持(类上)

@Scheduled

来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)

12、Enable注解说明
  • @EnableAspectAutoProxy:开启对AspectJ自动代理的支持;
  • @EnableAsync:开启异步方法的支持;
  • @EnableScheduling:开启计划任务的支持;
  • @EnableWebMvc:开启web MVC的配置支持;
13、测试相关注解

@RunWith

运行器,Spring中通常用于对JUnit的支持

三、SpringMVC常用注解

1、@EnableWebMvc

在配置类中开启Web MVC的配置支持。

2、@Controller
3、@RequestMapping

用于映射web请求,包括访问路径和参数。

4、@ResponseBody

支持将返回值放到response内,而不是一个页面,通常用户返回json数据。

5、@RequestBody

允许request的参数在request体中,而不是在直接连接的地址后面。(放在参数前)

6、@PathVariable

用于接收路径参数,比如@RequestMapping(“/hello/{name}”)声明的路径,将注解放在参数前,即可获取该值,通常作为Restful的接口实现方法。

7、@RestController

该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。

8、@ControllerAdvice

全局异常处理
全局数据绑定
全局数据预处理
ControllerAdvice的常用场景

9、@ExceptionHandler

用于全局处理控制器里的异常。

10、@InitBinder

用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。

11、@ModelAttribute

(1)@ModelAttribute注释方法

12、@Transactional

@Transactional 注解放在类和方法级别时,表示所有该类的公共方法都配置相同的事务属性信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ncwQRPmX-1685634781273)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230505211530695.png)]

SpringMvc执行流程?

1. 用户通过浏览器发起 HttpRequest 请求到前端控制器 (DispatcherServlet)。
2. DispatcherServlet 将用户请求发送给处理器映射器 (HandlerMapping)。
3. 处理器映射器 (HandlerMapping)会根据请求,找到负责处理该请求的处理器,并将其封装为处理器执行链 返回 (HandlerExecutionChain) 给 DispatcherServlet
4. DispatcherServlet 会根据 处理器执行链 中的处理器,找到能够执行该处理器的处理器适配器(HandlerAdaptor)    --注,处理器适配器有多个
5. 处理器适配器 (HandlerAdaptoer) 会调用对应的具体的 Controller
6. Controller 将处理结果及要跳转的视图封装到一个对象 ModelAndView 中并将其返回给处理器适配器 (HandlerAdaptor)
7. HandlerAdaptor 直接将 ModelAndView 交给 DispatcherServlet ,至此,业务处理完毕
8. 业务处理完毕后,我们需要将处理结果展示给用户。于是DisptcherServlet 调用 ViewResolver,将 ModelAndView 中的视图名称封装为视图对象
9. ViewResolver 将封装好的视图 (View) 对象返回给 DIspatcherServlet
10. DispatcherServlet 调用视图对象,让其自己 (View) 进行渲染(将模型数据填充至视图中),形成响应对象 (HttpResponse)
11. 前端控制器 (DispatcherServlet) 响应 (HttpResponse) 给浏览器,展示在页面上。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zB8kzvl8-1685634781274)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522110936326.png)]

四、其它注解

1、@JsonIgnore

(1)作用

在json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。

135、如何防止表单重复提交

1、通过JavaScript屏蔽提交按钮(不推荐)

2、给数据库增加唯一键约束(简单粗暴)

3、利用Session防止表单重复提交(推荐)

4、使用AOP自定义切入实现

*136、Spring中都应用了哪些设计模式

1.第一个工厂模式: BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
2.第二个单例模式: Bean默认为单例模式。
3.第三个代理模式: Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
4.第四个模板方法: 用来解决代码重复的问题。比如. RestTemplate,]msTemplate,JpaTemplate.
5.第五个观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现-ApplicationListener。

6、装饰器模式:动态的给一个对象添加一些额外的功能。

Spring的ApplicationContext中配置所有的DataSource。这些DataSource可能是不同的数据库,然后SessionFactory根据用户的每次请求,将DataSource设置成不同的数据源,以达到切换数据源的目的。

在Spring中有两种表现:

一种是类名中含有Wrapper,另一种是类名中含有Decorator。
7、策略模式:是行为性模式,调用不同的方法,适应行为的变化 ,强调父类的调用子类的特性 。

getHandler是HandlerMapping接口中的唯一方法,用于根据请求找到匹配的处理器。————————————————

137、请举例说明如何在 Spring 中注入一个 Java Collection?

Spring注入有四种方式,

set注入;
构造器注入;
基于注解的注入;
xml配置文件注入;
想要注入java collection,就是注入集合类:

list
set
map
props:该标签支持注入键和值都是字符串类型的键值对。
list和set都使用value标签;map使用entry标签;props使用prop标签;
————————————————

*138、mybatis 中 #{}和 ${}的区别是什么?

  1. #{}带引号,${}不带引号;

  2. #{}可以防止SQL注入;

  3. ${}常用于数据库表名、order by子句;

  4. 一般能用#{}就不要使用${};

    (1)${}是properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换.

    (2) #{}是sql的参数占位符,Mybatis会将sgl中的#{}替换为?号,在sql执行前会使用PreparedStatement的参数设置方法,按序给sql的?号占位符设置参数值。使用#什可以有效的防止 SOL 注入,提高系统安全性。

139、mybatis 是否支持延迟加载?延迟加载的原理是什么?

1、mybatis 是否支持延迟加载?

延迟加载其实就是讲数据加载时机推迟,比如推迟嵌套查询的时机。

延迟加载可以实现先查询主表,按需实时做关联查询,返回关联表结果集,一定程度上提高了效率。

mybatis仅支持关联对象association和关联集合对象collection的延迟加载,association是一对一,collection是一对多查询,在mybatis配置文件中可以配置lazyloadingEnable=true/false。

2、延迟加载的原理是什么?

使用CGLIB为目标对象建立代理对象,当调用目标对象的方法时进入拦截器方法。

比如调用a.getB().getName(),拦截器方法invoke()发现a.getB()为null,会单独发送事先准备好的查询关联B对象的sql语句,把B查询出来然后调用a.setB(b),也是a的对象的属性b就有值了,然后调用getName(),这就是延迟加载的原理。

140、说一下 mybatis 的一级缓存和二级缓存?

一级缓存是session级别的缓存,默认开启,当查询一次数据库时,对查询结果进行缓存,如果之后的查询在一级缓存中存在,则无需再访问数据库;

二级缓存是sessionFactory级别的缓存,需要配置才会开启。当进行sql语句查询时,先查看一级缓存,如果不存在,访问二级缓存,降低数据库访问压力。

<settings>
		开启二级缓存
	<setting name = "cacheEnabled" value = "true" />
</settings>

————————————————

141、mybatis 有哪些执行器(Executor)?

1、mybatis有三种基本的Executor执行器:

(1)、SimpleExecutor 默认的执行器

每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

(2)、PauseExecutor

执行update或select,以sql做为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而且放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。

(3)、BatchExecutor

执行update,将所有sql通过addBatch()都添加到批处理中,等待统一执行executeBatch(),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

2、作用范围:

Executor的这些特点,都严格限制在SqlSession生命周期范围内。

3、Mybatis中如何指定使用哪一种Executor执行器?

在mybatis的配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。
————————————————

142、mybatis 和 hibernate 的区别有哪些?

1、两者最大的区别

针对简单逻辑,都有对应的代码生成工具,可以生成简单基本的dao层方法;

针对高级查询,mybatis要手动编写sql语句和resultMap,而hibernate有良好的映射机制;

2、开发难度对比

hibernate > mybatis

3、日志统计

hibernate有自己的日志统计功能,而mybatis需要借助log4j来记录日志。

4、数据库扩展比较

hibernate > mybatis

5、缓存机制比较

因为hibernate对查询对象有良好的管理机制,用户无需关心sql,所以使用二级缓存如果出现脏数据,系统会报错。

而mybatis,如果不能获取最新数据,应该避免缓存的使用,脏数据的出现会给系统的正常运行带来很大的隐患。

6、如何选择

mybatis需要编写sql和映射规则,工作量大于hibernate;
mybatis支持的工具也有限,不能像hibernate那样有许多插件可以帮助生成映射代码和关联关系;
对于性能要求不太苛刻的系统,比如管理系统、ERP等推荐hibernate;
对于性能要求高、响应快、灵活的系统,比如电商系统,推荐使用mybatis;
————————————————

144、mybatis一级缓存、二级缓存

1、一级缓存:指的是mybatis中sqlSession对象的缓存,当我们执行查询以后,查询的结果会同时存入sqlSession中,再次查询的时候,先去sqlSession中查询,有的话直接拿出,当sqlSession消失时,mybatis的一级缓存也就消失了,当调用sqlSession的修改、添加、删除、commit()、close()等方法时,会清空一级缓存。

2、二级缓存:指的是mybatis中的sqlSessionFactory对象的缓存,由同一个sqlSessionFactory对象创建的sqlSession共享其缓存,但是其中缓存的是数据而不是对象。当命中二级缓存时,通过存储的数据构造成对象返回。查询数据的时候,查询的流程是二级缓存 > 一级缓存 > 数据库。

3、如果开启了二级缓存,sqlSession进行close()后,才会把sqlSession一级缓存中的数据添加到二级缓存中,为了将缓存数据取出执行反序列化,还需要将要缓存的pojo实现Serializable接口,因为二级缓存数据存储介质多种多样,不一定只存在内存中,也可能存在硬盘中。

4、mybatis框架主要是围绕sqlSessionFactory进行的,具体的步骤:

定义一个configuration对象,其中包含数据源、事务、mapper文件资源以及影响数据库行为属性设置settings。
通过配置对象,则可以创建一个sqlSessionFactoryBuilder对象。
通过sqlSessionFactoryBuilder获得sqlSessionFactory实例。
通过sqlSessionFactory实例创建qlSession实例,通过sqlSession对数据库进行操作。

145、mybatis如何防止sql注入

注意:但凡是sql注入漏洞的程序,都是因为程序要接受来自客户端用户输入的变量或URL传递的参数,并且这个变量或参数是组成sql语句的一部分,对于用户输入的内容或传递的参数,我们应该要时刻保持警惕,这是安全领域里的【外部数据不可信任】的原则,纵观web安全领域的各种攻击方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到,就是变量的检测、过滤、验证下手,确保变量是开发者所预想的。

1、检查变量数据类型和格式

数据类型检查,sql执行前,要进行数据类型检查,如果是邮箱,参数就必须是邮箱的格式,如果是日期,就必须是日期格式;

只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。

如果上述例子中id是int型的,效果会怎样呢?无法注入,因为输入注入参数会失败。比如上述中的name字段,我们应该在用户注册的时候,就确定一个用户名规则,比如5-20个字符,只能由大小写字母、数字以及汉字组成,不包含特殊字符。此时我们应该有一个函数来完成统一的用户名检查。不过,仍然有很多场景并不能用到这个方法,比如写博客,评论系统,弹幕系统,必须允许用户可以提交任意形式的字符才行,否则用户体验感太差了。

2、过滤特殊符号

3、绑定变量,使用预编译语句

146、为什么要使用 hibernate?

hibernate对jdbc进行了封装,简化了JDBC的重复性代码;
hibernate对dao有一个封装类hibernateTemplate,可以继承它,实现简单的CRUD接口。
hibernate使用注解和配置文件,可以对实体类和映射文件进行映射;
hibernate有事务管理机制,保证了数据的安全性;
hibernate有一级缓存和二级缓存;
————————————————

150、什么是 Spring Boot?Spring Boot 有哪些优点?

1、Spring Boot简介

基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突、引用的不稳定性得到了解决。

2、Spring Boot 有哪些优点?

快速构建项目,可以选一些必要的组件;
对主流框架的无配置集成;
内嵌Tomcat容器,项目可独立运行;
删除了繁琐的xml配置文件;
极大地提高了开发和部署效率;
提供starter,简化maven配置;
3、SpringBoot有哪些缺点?

版本迭代速度快,一些模块改动很大;
由于无须配置,报错时很难定位;

————————————————

151、Spring Boot 中的监视器是什么?

监听器也叫listener,是servlet的监听器,可以用于监听web应用程序中某些对象的创建、销毁、增加、修改、删除等动作的发生,然后做出相应的响应处理。当范围对象的状态发生变化时,服务器自动调用监听器对象中的方法,常用于系统加载时进行信息初始化,统计在线人数和在线用户,统计网站的访问量。

配置监听器的方法:

通过@Component把监听器加入Spring容器中管理;
在application.properties中添加context.listener.classes配置;
在方法上加@EventListener注解;

————————————————

152、什么是 YAML?

YAML是JSON的一个超集,可以非常方便地将外部配置以层次结构形式存储起来。YAML可以作为properties配置文件的替代。

YAML使用的注意事项:

在properties文件中是以".“进行分割的,在yml中是用”.“进行分割的;
yml的数据格式和json的格式很像,都是K-V格式,并且通过”:"进行赋值;
每个冒号后面一定要加一个空格;

153、如何使用 Spring Boot 实现分页和排序?

使用Spring Data Jpa可以实现将可分页的传递给存储库方法。

————————————————

154、如何使用 Spring Boot 实现异常处理?

1、使用 @ExceptionHandler 注解处理局部异常(只能处理当前controller中的ArithmeticException和NullPointerException异常,缺点就是只能处理单个controller的异常)

2、使用 @ControllerAdvice + @ExceptionHandler 注解处理全局异常(value后面可以填写数组)

3、配置 SimpleMappingExceptionResolver 类处理异常(配置类)

4、实现 HandlerExceptionResolver 接口处理异常

————————————————

*SpringBoot Starter的工作原理

我个人理解SpringBoot就是由各种Starter组合起来的,我们自己也可以开发Starter在sprinBoot启动时由@SpringBootApplication注解会自动去maven中读取每个starter中的spring.factories文件,该文件里配置了所有需要被创建spring容器中的bean,并且进行自动配置把bean注入SpringContext中 //(SpringContext是Spring的配置文件)
————————————————

*SpringBoot的自动配置原理是什么

自动配置简单来说呢,就是将第三方的组件自动装载到IOC容器里面,不需要开发人员再去编写相关的配置,在SpringBoot应用里面呢只需要加上@SpringBootApplication注解就可以实现自动配置,SpringBootApplication它是一个复合注解,真正实现自动装配的注解是@EnableAutoConfiguration注解。自动装配的实现呢主要依靠三个核心的关键技术:
1)引入Starter,启动依赖组件的时候,这个组件里面必须包括@Configuration配置类,然后我需要通过Bean注解去声明需要装配到IOC容器里面的Bean对象
2)这个配置类是放在第三方的jar包里面,然后通过Spring Boot中约定大于配置的理念,去把配置类的全路径放在件META_INF/Spring.factories文件里面,SpringBoot就可以知道第三方jar包里面配置类的位置,它主要是依靠Spring里面的SpringFactorierLoader来完成的
3)SpringBoot拿到所有第三方jar包声明的配置类之后,再通过ImportSelector这样一个接口来实现对这些配置类的动态加载,从而去完成自动装配这样的一个动作。

主要是Spring Boot的启动类上的核心注解SpringBootApplication注解主配置类,有了这个主配置
类启动时就会为SpringBoot开启一个@EnableAutoConfiguration注解自动配置功能。
有了这个EnableAutoConfiguration的话就会:
从配置文件META_INF/Spring.factories加载可能用到的自动配置类
去重,并将exclude和excludeName属性携带的类排除
过滤,将满足条件(@Conditional)的自动配置类返回
————————————————

155、SpringBoot拦截器

Spring Boot中的拦截器用于在请求处理过程中对请求和响应进行预处理和后处理
具体来说,Spring Boot拦截器可以用于以下几个方面:

  1. 日志记录:可以在请求被处理之前和之后记录日志,用于监控和统计信息。
  2. 权限检查:在请求到达控制器(Controller)之前进行权限验证,如检查用户是否已登录。
  3. 性能监控:通过记录请求的开始和结束时间,计算请求的处理时间,以监控性能。
  4. 通用行为:在请求处理前读取Cookie信息并将用户对象放入请求中,方便后续流程使用。还可以提取Locale、Theme等信息。
  5. URL路径拦截:可以对特定的URL路径进行拦截,实现特定功能的开关控制。

只需自定义一个拦截器类,实现HandlerInterceptor接口并重写preHandlepostHandleafterCompletion方法即可。

preHandle:该方法在请求被Controller处理之前执行。它的主要作用是进行权限验证、记录请求开始时间和进行通用行为处理等。如果该方法返回false,那么请求将不会继续往下执行,直接返回。这通常用于拦截不需要进一步处理的请求。
postHandle:这个方法在Controller处理请求之后,但在DispatcherServlet渲染视图之前被调用。它可以用来对Controller处理后的ModelAndView对象进行操作,比如修改模型数据或者视图名称等。
afterCompletion:该方法在DispatcherServlet完成视图渲染之后执行,通常用于清理资源、记录请求结束时间等。需要注意的是,这个方法只有在preHandle方法返回true时才会被调用。

?155、单点登录

1、概念

单点登录SSO,说的是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。

2、单点登录的要点

存储信任;
验证信任;

3、实现单点登录的三种方式

(1)以cookie作为凭证

最简单的单点登录实现方式,是使用cookie作为媒介,存放用户凭证。

用户登录父应用之后,应用返回一个加密的cookie,当用户访问子应用的时候,携带上这个cookie,授权应用解密cookie进行校验,校验通过则登录当前用户。

缺点:

cookie不安全

通过加密可以保证安全性,但如果对方掌握了解密算法就完蛋了。

不能跨域实现免登

(2)通过JSONP实现

对于跨域问题,可以使用JSONP实现。用户在父应用中登录后,跟session匹配的cookie会存到客户端中,当用户需要登录子应用的时候,授权应用访问父应用提供的JSONP接口,并在请求中带上父应用域名下的cookie,父应用接收到请求,验证用户的登录状态,返回加密的信息,子应用通过解析返回来的加密信息来验证用户,如果通过验证则登录用户。

缺点:

这种方法虽然能解决跨域问题,但是治标不治本,没有解决cookie安全性的问题。

(3)通过页面重定向的方式

最后一种介绍的方式,是通过父应用和子应用来回重定向进行通信,实现信息的安全传递。

父应用提供一个GET方式的登录接口A(此时的父应用接口固定,攻击者无法去伪造),用户通过子应用重定向连接的方式访问这个接口,如果用户还没有登录,则返回一个登录页面,用户输入账号密码进行登录,如果用户已经登录了,则生成加密的token,并且重定向到子应用提供的验证token的接口B(此时的子应用接口固定,攻击者无法去伪造),通过解密和校验之后,子应用登录当前用户。

缺点:

这种方式较前面的两种方式,是解决了安全性和跨域的问题,但是并没有前面两种方式简单,安全与方便,本来就是矛盾的。

4、使用独立登录系统

一般来说,大型应用会把授权的逻辑和用户信息的相关逻辑独立成一个应用,称为用户中心。用户中心不处理业务逻辑,只是处理用户信息的管理以及授权给第三方应用。第三方应用需要登录的时候,则把用户的登录请求转发给用户中心进行处理,用户处理完毕后返回凭证,第三方应用验证凭证,通过后就登录用户。

5、sso(单点登录)与OAuth2.0(授权)的区别?

(1)sso(单点登录)

通常处理的是一个公司的不同应用间的访问登录问题,如企业应用有很多子系统,只需登录一个系统,就可以实现不同子系统间的跳转,而避免了登录操作;
通过cookie、jsonp、重定向来实现;

(2)OAuth2.0(授权)

解决的是服务提供方(如微信)给第三方应用授权的问题,简称微信登录;
是一种具体的协议,只是为用户资源的授权提供了一个安全的、开放的而又简易的标准,OAuth2.0(授权)为客户开发者开发web应用,桌面应用程序,移动应用及客厅设备提供特定的授权流程。
————————————————

156、Spring Boot比Spring多哪些注解

@SpringBootApplication

@SpringBootConfiguration

@ImportResource 将资源导入容器

@PropertySource :导入properties文件

@PropertySource 的集合

3、@NoRepositoryBean

一般用做父类的repository,有这个注解,spring不会去实例化该repository。

4、@Column

如果字段名和列名相同,则可以省略。

5、@Id

表示该属性为主键。

6、@Transient

表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性。

7、@Basic

@Basic 是实体类与数据库字段映射时最简单的类型。

8、@JsonIgnore

在实体类向前台返回数据时用来忽略不想传递给前台的属性或接口

9、@JoinColumn、@OneToOne、@OneToMany、@ManyToOne
导入配置文件

@PropertySource、@ImportResource、@Import

十、事务注解

@Transactional

十一、Spring Cloud

1、@EnableEurekaServer
用在springboot启动类上,表示这是一个eureka服务注册中心;

2、@EnableDiscoveryClient
用在springboot启动类上,表示这是一个服务,可以被注册中心找到;

3、@LoadBalanced
开启负载均衡能力;

4、@EnableCircuitBreaker
用在启动类上,开启断路器功能;

5、@HystrixCommand(fallbackMethod=”backMethod”)
用在方法上,fallbackMethod指定断路回调方法;

6、@EnableConfigServer
用在启动类上,表示这是一个配置中心,开启Config Server;

7、@EnableZuulProxy
开启zuul路由,用在启动类上;

8、@SpringCloudApplication
@SpringBootApplication
@EnableDiscovertyClient
@EnableCircuitBreaker
————————————————

157、打包和部署

Spring和Spring Boot都支持maven和Gradle通用打包管理技术。

Spring Boot相对Spring的一些优点:

提供嵌入式容器支持;
使用命令java -jar独立运行jar;
部署时可以灵活指定配置文件;
最近项目是分布式的项目,都是通过分项目打包部署,然后部署在docker中运行。
————————————————

158、Spring Boot如何访问不同的数据库

可以使用druidDataSource创建DataSource,然后通过jdbcTemplate执行sql。

159、查询网站在线人数

通过监听session对象的方式来实现在线人数的统计和在线人信息展示,并且让超时的自动销毁。

对session对象实现监听,首先必须继承HttpSessionListener类,该程序的基本原理就是当浏览器访问页面的时候必定会产生一个session对象,当关闭该页面的时候必然会删除session对象。所以每当产生一个新的session对象就让在线人数+1,当删除一个session对象就让在线人数-1。

还要继承一个HttpSessionAttributeListener,来实现对其属性的监听。分别实现attributeAdded方法,attributeReplace方法以及attributeRemove方法。

sessionCreated//新建一个会话的时候触发,也可以说是客户端第一次喝服务器交互时触发。
sessionDestroyed//销毁会话的时候,一般来说只有某个按钮触发进行销毁,或者配置定时销毁。
HttpSessionAttributeListener有三个方法需要实现
attributeAdded//在session中添加对象时触发此操作 笼统的说就是调用setAttribute这个方法时候会触发的
attributeRemoved//修改、删除session中添加对象时触发此操作  笼统的说就是调用 removeAttribute这个方法时候会触发的
attributeReplaced//在Session属性被重新设置时。

160、easyExcel如何实现

异步读取Excel数据
新建一个 ExcelModelListener 监听类出来,并且 继承AnalysisEventListener 类

package com.zh.oukele.listener;
 
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.zh.oukele.model.ExcelMode;
 
import java.util.ArrayList;
import java.util.List;
 
/***
 *  监听器
 */
public class ExcelModelListener extends AnalysisEventListener<ExcelMode> {
 
    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<ExcelMode> list = new ArrayList<ExcelMode>();
    private static int count = 1;
    @Override
    public void invoke(ExcelMode data, AnalysisContext context) {
        System.out.println("解析到一条数据:{ "+ data.toString() +" }");
        list.add(data);
        count ++;
        if (list.size() >= BATCH_COUNT) {
            saveData( count );
            list.clear();
        }
    }
 
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData( count );
        System.out.println("所有数据解析完成!");
        System.out.println(" count :" + count);
    }
 
    /**
     * 加上存储数据库
     */
    private void saveData(int count) {
        System.out.println("{ "+ count +" }条数据,开始存储数据库!" + list.size());
        System.out.println("存储数据库成功!");
    }
 
}

161、什么是 Swagger?你用 Spring Boot 实现了它吗?

Swagger是*用于生成、描述、调用和可视化RESTful风格的 Web 服务。*它使文档和服务器可视化更新;

当定义好Swagger后,可以调用服务端接口,来查看接口的返回值,验证返回数据的正确性;

Mysql

视图

是一种虚拟存在的表。视图就是一条SELECT语句执行后返回的结果集。

– 创建视图

create/update view 别名 sql查询语句

什么是索引?

是存储引擎用于快速找到记录的一种数据结构。索引优化应该是对查询性能优化最有效的手段,易将查询性能提高好几个数量级。

MySQL索引实现原理分析

​ 目前大部分数据库系统及文件系统都采用**B-Tree(B树)或其变种B+Tree(B+树)**作为索引结构。B+Tree是数据库系统实现索引的首选数据结构。在MySQL中,索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式是不同的。

MyISAM

MyISAM索引实现MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址,InnoDB 这种索引叫做非聚集索引

辅助索引

在 MyISAM 中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求 key 是唯一的,而辅助索引的 key 可以重复

InnoDB 索引实现

虽然 InnoDB 也使用 B+Tree 作为索引结构,但具体实现方式却与 MyISAM 截然不同。InnoDB 索引结构和数据是在一起的,InnoDB 这种索引叫做聚集索引

1. 第一个重大区别是 InnoDB 的数据文件本身就是索引文件。

MySQL的隔离级别有哪些?

MySQL定义了四种隔离级别,包括一些具体规则,用于限定事务内外哪些改变是可见的,哪些改变是不可见的。
低级别的隔离一般支持更高的并发处理,并且拥有更低的系统开销。

READ UNCOMMITTED 读取未提交内容

在这个隔离级别,**所有事务都可以“看到"未提交事务的执行结果。**在这种级别上,可能会产生很多问题,除非用户真的知道自己在做什么,并有很好的理由选择这样做。本隔离级别很少用于实际应用,因为它的性能也不必其他性能好多少,而别的级别还有其他更多的优点。读取未提交数据,也被称为“脏读”

READ COMMITTED 读已提交内容

大多数数据库系统的默认隔离级别(但是不是MySOL的默认隔离级别),满足了隔离的早先简单定义:一个事务开始时,只能“看见"已经提交事务所做的改变,一个事务从开始到提交前,所做的任何数据改变都是不可见的,除非已经提交。这种隔离级别也支持所谓的"不可重复读”。这意味着用户运行同一个语句两次,看到的结果是不同的,出现幻读

REPEATABLE READ 可重复读

MySQL数据库默认的隔离级别。当使用可重复读隔离级别时,在事务执行期间会锁定该事务以任何方式引用的所有行。因此,如果在同一个事务中发出同一个SELECT语句两次或更多次,那么产生的结果数据集总是相同的。因此,使用可重复读隔离级别的事务可以多次检索同一行集,并对它们执行任意操作,直到提交或回滚操作终止该事务。解决了幻读问题。

SERIALIZABLE 可串行么

该级别是最高级别的隔离级。它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简而言之SERIALIZABLE是在每个读的数据行上加锁。在这个级别,可能导致大量的超时Timeout和锁竞争Lock Contention现象,实际应用中很少使用到这个级别,但如果用户的应用为了数据的稳定性,需要强制减少并发的话,也可以选择这种隔离级。

**读取未提交(READ-UNCOMMITTED):**最低的隔离级别,允许读取尚未提交的数据变更,可能造成脏读、不可重复读、幻读。
**读取已提交(READ-COMMITTED):**允许读取并发事务已经提交的数据,可以避免脏读,但是可能造成不可重复、幻读。
**可重复读(REPEATABLE-READ):**对同一字段多次读取的结果都是一致的,除非本身事务修改,可以避免脏读和不可重复读,但是可能造成幻读。
**可串行化(SERIALIZABLE):**最高的隔离级别,完全服从ACID的隔离级别,所以的事务依次执行,可以避免脏读、不可重复读、幻读。
————————————————

1.脏读

脏读是指一个事务读取了未提交事务执行过程中的数据。当一个事务的操作正在多次修改数据,而在事务还未提交的时候,另外一个并发事务来读取了数据,就会导致读取到的数据并非是最终持久化之后的数据,这个数据就是脏读的数据。

2.不可重复读

不可重复读是指对于数据库中的某个数据,一个事务执行过程中多次查询返回不同查询结果,这就是在事务执行过程中,数据被其他事务提交修改了。
不可重复读同脏读的区别在于,脏读是一个事务读取了另一未完成的事务执行过程中的数据,而不可重复读是一个事务执行过程中,另一事务提交并修改了当前事务正在读取的数据。

3.虚读(幻读)

幻读是事务非独立执行时发生的一种现象,例如事务T1批量对一个表中某一列列值为1的数据修改为2的变更,但是在这时,事务T2对这张表插入了一条列值为1的数据,并完成提交。此时,如果事务T1查看刚刚完成操作的数据,发现还有一条列值为1的数据没有进行修改,而这条数据其实是T2刚刚提交插入的,这就是幻读。幻读和不可重复读都是读取了另一条已经提交的事务(这点同脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体 (比如数据的个数)。

1. Mysql创建索引

执行create table语句可以创建索引,也可以单独用create index或alter table来为表增加索引;对每个表进行索引,能极大****加速查询进程****

**索引类型:**PRIMARY KEY(主键索引); ALTER TABLE table_name ADD PRIMARY KEY ( column )

UNIQUE(唯一索引);INDEX(普通索引);FULLTEXT(全文索引);

多列索引

ALTER TABLE table_name ADD INDEX index_name ( column1, column2, column3 );

create (UNIQUE/FULLTEXT ) index 索引名 on 表名(索引列);

2. 数据库的优化

1.选用合适的数据属性:

数字:空间越小越好,能用整数就不要用小数;

字符串:能用数字就用数字,长度可固定就固定;

日期类型:用数字(毫秒值);

2.不要使用text,blob类型;(必要时则独立出一张表,用主键来对应,避免影响其他字段索引效率)

3.把字段定义为not null并且提供默认值;

4.将字段很多的表分解成多个表,数据过多纵向拆分.(分库分表,使用mycat中间件操作);

5.增加中间表,适当的将联合查询改为中间表查询;

6.适当增加冗余字段,提升查询的速度;

7.使用连接(JOIN)来代替子查询

8.使用联合(UNION)来代替手动创建的临时表

9.使用索引

10.优化查询语句

服务器优化(穷逼优化方案)

优化mysql参数:

索引缓冲区大小(key_buffer_size)。

表缓冲区大小(table_cache)。

查询缓冲区大小(query_cache_type)。

3. 数据库的读写分离

读库和写库数据一致。

写数据必须写到写库。

读数据都可以。

当一个事务时,连接只有一个,所以读写都会发生在主库中。

主从同步原理

主库将改变的数据记录到binary log(二进制日志)

从库拷贝日志到中继日志(relay log)

从库读取日志,改变数据

4. Sql语句的优化

1.添加索引(注意:经常作为查询条件(排序)的字段,不能经常修改)

2.不在数据库进行****运算****;

3.****不同类型****不可进行比较;

4.禁止使用****反向查询****,会导致全表扫描;

5…尽量不要使用****like****关键字查询(如果匹配字符串的第一个字符为”%”,索引不起作用);

6.不要使用****or关键字查询*,且or前后的两个条件中的列都是索引时,索引才会生效,否则索引不生效;推荐*使用in*或者union*;

7.查询条件中尽量****不使用函数;****

8.只要一行数据时使用 *LIMIT 10,5*(第一个值是第几行数据,后面是含有几条数据);

9.只取出自己需要的结果集,不要****select *****;

10.尽量不使用****子查询****;

5. 数据库中char和Varchar的区别,int的大小

char 的长度是****固定*的;
varchar的长度是
*可变****的。

在取数据的时候

char需要用trim()去除空格;
而varchar不需要。

6. Mysql分别库分表中间件(Mycat)

实现读写分离只需两步:

搭建Mysql主从复制环境

配置mycat读写分离策略

7. 原生JDBC的创建流程

加载驱动;

创建连接;

获取会话;

书写sql语句;

获取结果集;

关闭资源;

8. 数据库引擎区别

myIsam:*不支持事物*,外键,支持全文检索,*表级锁*。查询效率高

innodb:*支持*acid事物,*行级锁*,效率高。

mysql聚族和非聚族索引的区别是什么?

mysql的索引类型跟存储引擎是相关的,innodb存储引擎数据文件跟索引文件全部放在ibd文件中,而myisam的数据文件放在myd文件中,索引放在myi文件中,其实区分聚簇索引和非聚簇索引非常简单,只要判断数据跟索引是否存储在一起就可以了
innodb存储引擎在进行数据插入的时候,数据必须要跟索引放在一起,如果有主键就使用主键,没有主键就使用唯一键,没有唯一键就使用6字节的rowid,因此跟数据绑定在一起的就是聚簇索引,而为了避免数据几余存诸,其他的索引的叶子节点中存储的都是聚簇索引的key值,因此innodb中既有聚族索引也有非聚族索引,而myisam中只有非聚簇索引。

162、数据库的三范式是什么?

1、列不可再分;

2、每一行数据只做一件事,只与主键列相关,;

3、每个属性都与主键有直接关系,而不是间接关系;

三大范式只是设计数据库的基本理念,可以建立冗余较小结构合理的数据库。如果有特殊情结,当然要特殊对待,数据库设计最重要的是看需求和性能,需求>性能>表结构。

所以不能一味的追求三范式建立数据库。
————————————————

163、一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?

一般情况下,我们创建的表类型是InnoDB。

不重启MySQL,如果新增一条记录,id是8;
重启,ID是6;因为InnoDB表只把自增主键的最大ID记录在内存中,如果重启,已删除的最大ID会丢失。
如果表类型是MyISAM,重启之后,最大ID也不会丢失,ID是8

InnoDB必须有主键(建议使用自增主键,不用UUID,自增主键索引查询效率高)、支持外键、支持事务、支持行级锁。

系统崩溃后,MyISAM很难恢复;

综合考虑,优先选择InnoDB,MySQL默认也是InnoDB。
————————————————

164、如何获取当前数据库版本?
//MySQL,,mysql -v
select version();
//Oracle 
select * from v$version;
165、说一下 ACID 是什么?

ACID是数据库事务执行的四大基本要素,包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

1、原子性

整个事务中的所有操作,要么全部完成,要不全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被roolback回滚到事务开始前的状态,就像这个事务从未执行过一样。

2、一致性

事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。

3、隔离性

隔离状态执行事务,使他们好像是系统在给定时间内执行的唯一操作。

如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性确保每一个事务在系统中认为只有自己在使用系统。这种属性称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。

4、持久性

一个成功的事务将永久的改变系统的状态。

————————————————

166、char 和 varchar 的区别是什么?
  1. char的长度是固定的,varchar的长度的可变的;
  2. char的效率比varchar的效率高;
  3. char占用空间比varchar大,char在查询时需要使用trim
167、float 和 double 的区别是什么?

1、float 和 double 的区别是什么?

(1)内存中占有的字节数不同

单精度浮点数在内存中占有4个字节;

双精度浮点数在内存中占有8个字节;

(2)有效数字位数不同

单精度浮点数有效数字8位;

双精度浮点数有效数字16位;

(3)数值取值范围不同

单精度浮点数的表示范围:-3.40E+38~3.40E+38

双精度浮点数的表示范围:-1.79E+308~-1.79E+308

(4)在程序中处理速度不同

一般来说,CPU处理单精度浮点数的速度比双精度浮点数的速度快

如果不声明,默认小数是double类型,如果想用float,要进行强转;

2、例如

float f = 1.3;会编译报错,正确的写法是float f = (float)1.3;或者float a = 1.3f;(f或F都可以不区分大小写)

3、注意

float是八位有效数字,第七位会四舍五入;
————————————————

168、Oracle分页sql

Oracle分页通过加伪列

带排序的分页:先排序后加伪列
————————————————

169、数据库如何保证主键唯一性
1、主键约束

主键列上没有任何两行具有相同值(即重复值),不允许空(NULL);主键不能为空且数据不重复。

2、唯一性约束

保证一个字段或者一组字段里的数据都与表中其它行的对应数据不同。和主键约束不同,唯一性约束允许为null,但是只能有一行

3、唯一性索引

不允许具有索引值相同的行,从而禁止重复的索引和键值;

4、三者的区别

约束是用来检查数据的正确性;
索引是用来优化查询的;
创建唯一性约束会创建一个约束和一个唯一性索引;
创建唯一性索引只会创建一个唯一性索引;
主键约束和唯一性约束都会创建一个唯一性索引。
————————————————

170、如何设计数据库
1、数据库设计最起码要占用这个项目开发的40%以上的时间

2、数据库设计不仅仅停留在页面demo的表面

页面内容所需字段,在数据库设计中只是一部分,还有系统运转、模块交互、中转数据、表之间的联系等等所需要的字段,因此数据库设计绝对不是简单的基本数据存储,还有逻辑数据存储。

3、数据库设计完成后,项目80%的设计开发都要存在你的脑海中

每个字段的设计都要有他存在的意义,要清楚的知道程序中如何去运用这些字段,多张表的联系在程序中是如何体现的。

4、数据库设计时就要考虑效率和优化问题

数据量大的表示粗粒度的,会冗余一些必要字段,达到用最少的表,最弱的表关系去存储海量的数据。大数据的表要建立索引,方便查询。对于含有计算、数据交互、统计这类需求时,还有考虑是否有必要采用存储过程。

5、添加必要的冗余字段

像创建时间、修改时间、操作用户IP、备注这些字段,在每张表中最好都有,一些冗余的字段便于日后维护、分析、拓展而添加。

6、设计合理的表关联

若两张表之间的关系复杂,建议采用第三张映射表来关联维护两张表之间的关系,以降低表之间的直接耦合度

7、设计表时不加主外键等约束关联,系统编码阶段完成后再添加约束性关联

8、选择合适的主键生成策略

数据库的设计难度其实比单纯的技术实现难很多,他充分体现了一个人的全局设计能力和掌控能力,最后说一句,数据库设计,很重要,很复杂。

————————————————

171、性别是否适合做索引

区分度不高的字段不适合做索引,因为索引页是需要有开销的,需要存储的,不过这类字段可以做联合索引的一部分
————————————————

172、如何查询重复的数据

1、查询重复的单个字段(group by)

select 重复字段A, count(*) from 表 group by 重复字段A having count(*) > 1

2、查询重复的多个字段(group by)

select 重复字段A, 重复字段B, count(*) from 表 group by 重复字段A, 重复字段B having count(*) > 1

————————————————

173、数据库一般会采取什么样的优化方法?

1、选取适合的字段属性

为了获取更好的性能,可以将表中的字段宽度设得尽可能小。
尽量把字段设置成not null
执行查询的时候,数据库不用去比较null值
对固有字段类型,将他们定义为enum类型,enum类型被当做数值型数据来处理,而数值型数据被处理起来的速度要比文本类型块多。
2、使用join连接代替子查询

3、使用联合union来代替手动创建的临时表

注意:union用法中,两个select语句的字段类型要匹配,而且字段个数要相同。

4、事务

要么都成功,要么都失败。

可以保证数据库中数据的一致性和完整性。事务以begin开始,commit关键字结束。

如果出错,rollback命令可以将数据库恢复到begin开始之前的状态。

事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方式为用户提供一种安全的访问方式,这样就可以保证用户的操作不被其他的用户干扰。

5、锁定表

尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在大应用中。

由于在事务执行的过程中,数据库会被锁定,因此其它用户只能暂时等待直到事务结束。

有的时候可以用锁定表的方法来获得更好的性能,

共享锁:其它用户只能看,不能修改

lock table person in share mode;

对于通过lock table 命令主动添加的锁来说,如果要释放它们,只需发出rollback命令即可。

6、使用外键

锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性,这个时候可以使用外键。

7、使用索引

索引是提高数据库查询速度的常用方法,尤其是查询语句中包含max()、min()、order by这些命令的时候,性能提高更为显著。

一般来说索引应该建在常用于join、where、order by的字段上。尽量不要对数据库中含有大量重复的值得字段建立索引。

8、优化的查询语句

在索引的字段上尽量不要使用函数进行操作。

尽量不要使用like关键字和通配符,这样做法很简单,但却是以牺牲性能为代价的。

避免在查询中进行自动类型转换,因为类型转换也会使索引失效。

————————————————

174、索引怎么定义,分哪几种?
1.添加PRIMARY KEY(主键索引)
mysql>ALTER TABLE table_name ADD PRIMARY KEY ( column )
2.添加UNIQUE(唯一索引)
mysql>ALTER TABLE table_name ADD UNIQUE (column)
3.添加INDEX(普通索引)
mysql>ALTER TABLE table_name ADD INDEX index_name ( column )
4.添加FULLTEXT(全文索引)
mysql>ALTER TABLE table_name ADD FULLTEXT ( column)
5.添加多列索引
mysql>ALTER TABLE table_name ADD INDEX index_name ( column1, column2, column3 )

数据结构分类:B+tree索引、Hash索引、Full-text索引。
物理存储分类:聚集索引、非聚集索引(也叫二级索引、辅助索引)。
字段特性分类:主键索引(PRIMARY KEY)、唯一索引(UNIQUE)、普通索引(INDEX)、全文索引(FULLTEXT)。
字段个数分类:单列索引、联合索引(也叫复合索引、组合索引)。

bitmap:位图索引,位图索引特定于只有几个枚举值的情况,比如性别字段;
基于函数的索引

————————————————

175、mysql 的内连接、左连接、右连接有什么区别?
  1. 内连接,显示两个表中有联系的所有数据;
  2. 左链接,以左表为参照,显示所有数据,右表中没有则以null显示
  3. 右链接,以右表为参照显示数据,,左表中没有则以null显示
优化Mysql数据库的8个方法

1、创建索引

​ 对于查询占主要的应用来说,索引显得尤为重要。如果不加索引的话,那么查找任何哪怕只是一条特定的数据都会进行一次全表扫描,如果一张表的数据量很大而符合条件的结果又很少,那么不加索引会引起致命的性能下降。但是也不是什么情况都非得建索引不可,像数据量很大,很多数据类型都相同,比如性别可能就只有两个值,建索引不仅没什么优势,还会影响到更新速度,这被称为过度索引。

2、复合索引
比如有一条语句是这样的:select * from users where area=‘beijing’ and age=22;
如果我们是在area和age上分别创建单个索引的话,由于mysql查询每次只能使用一个索引,所以虽然这样已经相对不做索引时全表扫描提高了很多效率,但是如果在area、age两列上创建复合索引的话将带来更高的效率。如果我们创建了(area, age, salary)的复合索引,那么其实相当于创建了**(area,age,salary)、(area,age)、(area)三个索引**,这被称为最佳左前缀特性。因此我们在创建复合索引时应该将最常用作限制条件的列放在最左边,依次递减。

3、索引不会包含有NULL值的列
只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。

4、使用短索引
**对串列进行索引,如果可能应该指定一个前缀长度。**例如,如果有一个CHAR(255)的 列,如果在前10 个或20 个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

5、排序的索引问题
**mysql查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。**因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

6、like语句操作
一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。

7、不要在列上进行运算
select * from users where YEAR(adddate)<2007;
在每个行上进行运算,这将导致索引失效而进行全表扫描,因此我们可以改成
select * from users where adddate<‘2007-01-01’;

8、不使用NOT IN和<>操作
NOT IN和<>操作都不会使用索引将进行全表扫描。NOT IN可以NOT EXISTS代替,id<>3则可使用id>3 or id<3来代替。

SQL优化大总结-百万级数据库优化方案

1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引

2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,可以用其他值代替null。如:

select id from t where num is null

3.应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描

4.应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,如:

select id from t where num=10 or Name = 'admin'

可以这样查询:

select id from t where num = 10
union all
select id from t where Name = 'admin'

5.**in 和 not in 也要慎用,**否则会导致全表扫描,如:

select id from t where num in(1,2,3)

对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3

很多时候用 exists 代替 in 是一个好的选择

select num from a where num in(select num from b)

用下面的语句替换:

select num from a where exists(select 1 from b where num=a.num)

6.下面的查询也将导致全表扫描:

select id from t where name like ‘%abc%’

若要提高效率,可以考虑全文检索。

7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:

select id from t where num = @num

可以改为强制查询使用索引:

select id from t with(index(索引名)) where num = @num

8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where num/2 = 100

应改为:

select id from t where num = 100*2

9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:

select id from t where substring(name,1,3) = ’abc’       -–name以abc开头的id
select id from t where datediff(day,createdate,’2005-11-30′) = 0    -–‘2005-11-30’    --生成的id

应改为:

select id from t where name like 'abc%'
select id from t where createdate >= '2005-11-30' and createdate < '2005-12-1'

10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

13.Update 语句,如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志。

14.对于多张大数据量(这里几百条就算大了)的表JOIN,要先分页再JOIN,否则逻辑读会很高,性能很差。?

15.select count(*) from table;这样不带任何条件的count会引起全表扫描,并且没有任何业务意义,是一定要杜绝的。
16.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有 必要。

17.应尽可能的避免聚合索引数据列,因为 聚合(clustered )索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。

18.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连 接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

19.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

20.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

21.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。

  1. **避免频繁创建和删除临时表,以减少系统表资源的消耗。**临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件, 最好使用导出表。

23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时 间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。

28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。

29.尽量避免大事务操作,提高系统并发能力。

30.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

游标可以让我们可以对结果集中的每一条记录进行定位,并对指向的记录中的数据进行操作

索引失效的几种情况

1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)

2.对于多列索引,不是使用的第一部分,则不会使用索引

3.like查询以%开头

4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引

通过explain关键字看SQL语句的性能

分析sql语句步骤

1. 表的读取顺序如何
2. 数据读取操作有哪些操作类型
3. 哪些索引可以使用
4. 哪些索引被实际使用
5. 表之间是如何引用
6. 每张表有多少行被优化器查询
......

3.表的加载顺序等等,怎么查看呢?

其实MySQL自带的SQL分析神器Explain执行计划就能完成以上的事情!

只需要在SQL语句前加上explain关键字就可以查看执行计划,执行计划包括以下信息:id、select_type、table、partitions、type、possible_keys、key、key_len、ref、rows、filtered、Extra,总共12个字段信息。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MLuPCxfl-1685634781274)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230421004042160.png)]

一、id

SELECT识别符。这是SELECT的查询序列号。SQL执行的顺序的标识,SQL从大到小的执行。id列有以下几个注意点:

· id相同时,执行顺序由上至下

· id不同时,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行

根据原则,当id不同时,SQL从大到小执行,id相同则从上到下执行。

二、select_type

表示select查询的类型,用于区分各种复杂的查询,例如普通查询,联合查询,子查询等等。

SIMPLE

表示最简单的查询操作,也就是查询SQL语句中没有子查询、union等操作。

PRIMARY

当查询语句中包含复杂查询的子部分,表示复杂查询中最外层的 select。

SUBQUERY

当 select 或 where 中包含有子查询,该子查询被标记为SUBQUERY。

DERIVED

在SQL语句中包含在from子句中的子查询。

UNION

表示在union中的第二个和随后的select语句。

UNION RESULT

代表从union的临时表中读取数据。

MATERIALIZED

MATERIALIZED表示物化子查询,子查询来自视图。

三、table

表示输出结果集的表的表名,并不一定是真实存在的表,也有可能是别名,临时表等等。

四、partitions

表示SQL语句查询时匹配到的分区信息,对于非分区表值为NULL,当查询的是分区表则会显示分区表命中的分区情况

五、type

需要重点关注的一个字段信息,表示查询使用了哪种类型,在 SQL优化中是一个非常重要的指标,依次从优到差分别是:

system > const > eq_ref > ref > range > index > ALL

system和const

单表中最多有一条匹配行,查询效率最高,所以这个匹配行的其他列的值可以被优化器在当前查询中当作常量来处理。通常出现在根据主键或者唯一索引进行的查询,system是const的特例,表里只有一条元组匹配时(系统表)为system。

eq_ref

primary key 或 unique key 索引的所有部分被连接使用 ,最多只会返回一条符合条件的记录,所以这种类型常出现在多表的join询。

ref

相比eq_ref,不使用唯一索引,而是使用普通索引或者唯一性索引的部分前缀,可能会找到多个符合条件的行

range

使用索引选择行,仅检索给定范围内的行。一般来说是针对一个有索引的字段,给定范围检索数据,通常出现在where语句中使用 bettween…and、<、>、<=、in 等条件查询 。

index

扫描全表索引,通常比ALL要快一些。

六、possible_keys

表示在查询中可能使用到的索引来查找,别列出的索引并不一定是最终查询数据所用到的索引。

七、key

跟possible_keys有所区别,key表示查询中实际使用到的索引,若没有使用到索引则显示为NULL。

八、key_len

表示查询用到的索引key的长度(字节数)。如果单列索引,那么就会把整个索引长度计算进去,如果是联合索引,不是所有的列都用到,那么就只计算实际用到的列,因此可以根据key_len来判断联合索引是否生效

九、ref

显示了哪些列或常量被用于查找索引列上的值。常见的值有:const,func,null,字段名。

十、rows

mysql估算要找到我们所需的记录,需要读取的行数。可以通过这个数据很直观的显示 SQL 性能的好坏,一般情况下 rows 值越小越好

十一、filtered

返回结果的行占需要读到的行(rows列的值)的百分比一般来说越大越好

十二、Extra

表示额外的信息。此字段能够给出让我们深入理解执行计划进一步的细节信息。

Using index

说明在select查询中使用了覆盖索引。覆盖索引的好处是一条SQL通过索引就可以返回我们需要的数据。

Using where

查询时没使用到索引,然后通过where条件过滤获取到所需的数据。

Using temporary

表示在查询时,MySQL需要创建一个临时表来保存结果。临时表一般会比较影响性能,应该尽量避免。

有时候使用DISTINCT去重时也会产生Using temporary。

Using filesort

我们知道索引除了查询中能起作用外,排序也是能起到作用的,所以当SQL中包含 ORDER BY 操作,而且无法利用索引完成排序操作的时候,MySQL不得不选择相应的排序算法来实现,这时就会出现Using filesort,应该尽量避免使用Using filesort

总结

一般优化SQL语句第一步是要知道这条SQL语句有哪些需要优化的,explain执行计划就相当于一面镜子,能把详细的执行情况给开发者列出来。所以说善用explain执行计划,能解决80%的SQL优化问题。

explain的信息中,一般我们要关心的是type,看是什么级别,如果是在互联网公司一般需要在range以上的级别,接着关心的是Extra有没有出现filesort或者using template,一旦出现就要想办法避免,接着再看key使用的是什么索引,还有看filtered筛选比是多少。

B树和B+树的区别为什么Mysql使用B+树?
B树的特点:

1.节点排序,
2.一个节点可以存多个元素,多个元素也排序了。

B+树的特点:

1拥有B树的特点;
2.叶子节点之间有指针
3.非叶子节点上的元素在叶子节点上都冗余了,
也就是叶子节点中存储了所有的元素,并且排好顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AYXpdxfh-1685634781275)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230421224605237.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PTJ225tr-1685634781275)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230421224620772.png)]

红黑树性质

1)每个结点或是红色的,或是黑色的;
2)根结点是黑色的;
3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的;
4)如果一个结点是红色的,则它的两个子结点都是黑色的;
5)对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。
红黑树虽然本质上是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。

————————————————

MQ

176、RabbitMQ的使用场景有哪些?

1、解决异步问题

例如用户注册,发送邮件和短信反馈注册成功,可以使用RabbitMQ消息队列,用户无需等待反馈。

2、服务间解耦、消峰

订单系统和库存系统,中间加入RabbitMQ消息队列,当库存系统出现问题时,订单系统依旧能正常使用,降低服务间耦合度。

3、如秒杀系统

利用RabbitMQ的最大值,实现秒杀系统。
————————————————

177、RabbitMQ有哪些重要的角色?有哪些重要的组件?

1、RabbitMQ有哪些重要的角色?

客户端、RabbitMQ、服务端。

2、有哪些重要的组件?

(1)connectionFactory(连接管理器)

应用程序与RabbitMQ之间建立连接的管理器。

(2)Channel(信道)

消息推送使用的信道。

(3)RoutingKey(路由键)

用于把生产者的数据分配到交换机上。

(4)Exchange(交换机)

用于接受和分配消息。

(5)BindKey(绑定键)

用于把交换机的消息绑定到队列上

(6)Queue(队列)

用于存储生产者消息。
————————————————

178、RabbitMQ中 vhost 的作用是什么?

vhost可以理解为mini版的RabbitMQ,其内部均含有独立的交换机、绑定、队列,最重要的是拥有独立的权限系统,可以做到vhost范围内的用户控制。从RabbitMQ全局考虑,不同的应用可以跑在不同的vhost上,作为不同权限隔离的手段。
————————————————

JVM

gc垃圾回收默认的值是15,它可以在保证垃圾回收效率的前提下,尽可能的减少移动到年老代中的对象数量,我们也可以通过MaxTenuringThreshold这个参数来调整值,这个值设置有一定的限制,不能为负数和0,因为这样会导致所有对象直接进入到老年代,而不会放置在新生代中进行垃圾回收,而且这个值最大是15,且不能超过15。因为对象的GC年龄是存储在对象头里,对象头里面分4个bit位来存储,在二进制里面,4个bit位最大值就是15。

180、说一下 jvm 运行时数据区?

运行时数据区包括堆、方法区、栈、本地方法栈、程序计数器。

1、堆

堆解决的是对象实例存储的问题,垃圾回收器管理的主要区域。

2、方法区

方法区可以认为是堆的一部分,用于存储已被虚拟机加载的信息,常量、静态变量和静态方法、即时编译器编译后的代码。

3、栈

栈解决的是程序运行的问题,栈里面存的是栈帧,栈帧里面存的是局部变量表、操作数栈、动态链接、方法出口等信息。

(1)栈帧

每个方法从调用到执行的过程就是一个栈帧在虚拟机栈中入栈到出栈的过程。

(2)局部变量表

用于保存函数的参数和局部变量。

(3)操作数栈

操作数栈又称操作栈,大多数指令都是从这里弹出数据,执行运算,然后把结果压回操作数栈。

4、本地方法栈

与栈功能相同,本地方法栈执行的是本地方法,一个Java调用非Java代码的接口。

5、程序计数器(PC寄存器)

程序计数器中存放的是当前线程所执行的字节码的行数。JVM工作时就是通过改变这个计数器的值来选取下一个需要执行的字节码指令。
————————————————

181、什么是类加载器,类加载器有哪些?

1、什么是类加载器?

类加载器负责加载所有的类,其为所有被载入内存的类生成一个java.lang.Class实例对象。

2、类加载器有哪些?

JVM有三种类加载器:

(1)启动类加载器

该类没有父加载器,用来加载Java的核心类,启动类加载器的实现依赖于底层操作系统,属于虚拟机实现的一部分,它并不继承自java.lang.classLoader。

(2)扩展类加载器

它的父类为启动类加载器,扩展类加载器是纯java类,是ClassLoader类的子类,负责加载JRE的扩展目录。

(3)应用程序类加载器

它的父类为扩展类加载器,它从环境变量classpath或者系统属性java.lang.path所指定的目录中加载类,它是自定义的类加载器的父加载器。
————————————————

182、说一下类加载的执行过程?

当程序主动使用某个类时,如果该类还未被加载到内存中,JVM会通过加载、连接、初始化3个步骤对该类进行类加载。

1、加载

加载指的是将类的class文件读入到内存中,并为之创建一个java.lang.Class对象。

类的加载由类加载器完成,类加载器由JVM提供,开发者也可以通过继承ClassLoader基类来创建自己的类加载器。

通过使用不同的类加载器可以从不同来源加载类的二进制数据,通常有如下几种来源:

从本地文件系统加载
从jar包加载
通过网络加载
把一个Java源文件动态编译,并执行加载
2、连接

当类被加载之后,系统为之生成一个对应的Class对象,接着进入连接阶段,连接阶段负责将类的二进制数据合并到JRE中。

类连接又可分为三个阶段:

(1)验证

文件格式验证
元数据验证
字节码验证
符号引用验证

(2)准备

为类的静态变量分配内存,并设置默认初始值。

(3)解析

将类的二进制数据中的符号引用替换成直接引用。

3、初始化

为类的静态变量赋予初始值。
————————————————

183、JVM的类加载机制是什么?

JVM类加载机制主要有三种:

1、全盘负责

类加载器加载某个class时,该class所依赖的和引用其它的class也由该类加载器载入。

2、双亲委派

先让父加载器加载该class,父加载器无法加载时才考虑自己加载。

3、缓存机制

缓存机制保证所有加载过的class都会被缓存,当程序中需要某个class时,先从缓存区中搜索,如果不存在,才会读取该类对应的二进制数据,并将其转换成class对象,存入缓存区中。

这就是为什么修改了class后,必须重启JVM,程序所做的修改才会生效的原因。
————————————————

184、什么是双亲委派模型?

如果一个类收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器执行,如果父加载器还存在其父加载器,则进一步向上委托,依次递归,请求将最终到达顶层的启动类加载器,如果父类加载器可以完成父加载任务,就成功返回,如果父加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委派模型。

双亲委派模式的优势:

避免重复加载
考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委派模式传递到启动加载器,而启动加载器在核心Java API中发现同名的类,发现该类已经被加载,就不会重新加载网络传递的Integer类,而直接返回已加载过的Integer.class,这样可以防止核心API库被随意篡改。
————————————————

185、怎么判断对象是否可以被回收?

1、引用计数算法

(1)判断对象的引用数量

通过判断对象的引用数量来决定对象是否可以被回收;
每个对象实例都有一个引用计数器,被引用+1,完成引用-1;
任何引用计数为0的对象实例可以被当做垃圾回收;

(2)优缺点

优点:执行效率高,程序受影响较小;
缺点:无法检测出循环引用的情况,导致内存泄漏

2、可达性分析算法

通过判断对象的引用链是否可达来决定对象是否可以被回收

如果程序无法再引用该对象,那么这个对象肯定可以被回收,这个状态称为不可达。

那么不可达状态如何判断呢?

答案是GC roots,也就是根对象如果一个对象无法到达根对象的路径,或者说从根对象无法引用到该对象,该对象就是不可达的。

以下三种对象在JVM中被称为GC roots,来判断一个对象是否可以被回收。

(1)虚拟机栈的栈帧

每个方法在执行的时候,JVM都会创建一个相应的栈帧(操作数栈、局部变量表、运行时常量池的引用),当方法执行完,该栈帧就从栈中弹出,这样一来,方法中临时创建的独享就不存在了,或者说没有任何GC roots指向这些临时对象,这些对象在下一次GC的时候便会被回收。

(2)方法区中的静态属性

静态属性数据类属性,不属于任何实例,因此该属性自然会作为GC roots。这要这个class在,该引用指向的对象就一直存在,class也由被回收的时候。

class何时会被回收?

堆中不存在该类的任何实例
加载该类的classLoader已经被回收
该类的java.lang.class对象没有在任何地方被引用,也就是说无法通过反射访问该类的信息
(3)本地方法栈引用的对象
————————————————

186、说一下 jvm 有哪些垃圾回收算法?

1、对象是否已死算法

引用计数器算法
可达性分析算法

2、GC算法

(1)标记清除算法

如果对象被标记后进行清除,会带来一个新的问题–内存碎片化。如果下次有比较大的对象实例需要在堆上分配较大的内存空间时,可能会出现无法找到足够的连续内存而不得不再次触发垃圾回收。

(2)复制算法(Java堆中新生代的垃圾回收算法)

先标记待回收内存和不用回收内存;
将不用回收的内存复制到新的内存区域;
就的内存区域就可以被全部回收了,而新的内存区域也是连续的;
缺点是损失部分系统内存,因为腾出部分内存进行复制。

(3)标记压缩算法(Java堆中老年代的垃圾回收算法)

对于新生代,大部分对象都不会存活,所以复制算法较高效,但对于老年代,大部分对象可能要继续存活,如果此时使用复制算法,效率会降低。

标记压缩算法首先还是标记,将不用回收的内存对象压缩到内存一端,此时即可清除边界处的内存,这样就能避免复制算法带来的效率问题,同时也能避免内存碎片化的问题。

老年代的垃圾回收算法称为“Major GC”。
————————————————

?187、说一下 jvm 有哪些垃圾回收器?

189、新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

新生代回收器:Serial、ParNew、Parallel Scavenge

老年代回收器:Serial Old、Parallel Old、CMS

新生代回收器一般采用的是复制算法,复制算法效率较高,但是浪费内存;

老生代回收器一般采用标记清楚算法,比如最常用的CMS;

————————————————

190、详细介绍一下 CMS 垃圾回收器?

CMS 垃圾回收器是Concurrent Mark Sweep,是一种同步的标记-清除,CMS分为四个阶段:

初始标记,标记一下GC Root能直接关联到的对象,会触发“Stop The World”;
并发标记,通过GC Roots Tracing判断对象是否在使用中;
重新标记,标记期间产生对象的再次判断,执行时间较短,会触发“Stop The World”;
并发清除,清除对象,可以和用户线程并发进行;

————————————————

191、简述分代垃圾回收器是怎么工作的?

分代回收器分为新生代和老年代,新生代大概占1/3,老年代大概占2/3;

新生代包括Eden、From Survivor、To Survivor;

Eden区和两个survivor区的 的空间比例 为8:1:1 ;

垃圾回收器的执行流程:

把 Eden + From Survivor 存活的对象放入 To Survivor 区;
清空 Eden + From Survivor 分区,From Survivor 和 To Survivor 分区交换;
每次交换后存活的对象年龄+1,到达15,升级为老年代,大对象会直接进入老年代;
老年代中当空间到达一定占比,会触发全局回收,老年代一般采取标记-清除算法;
————————————————

Linux

Linux中一切皆文件(文件:读写执行(查看,创建,删除,移动,复制,编辑),权限(用户、用户组)。系统:(磁盘,进程))
关机指令为:shutdown

系统目录结构

1、一切皆文件
2、根目录 / ,所有的文件都挂载在这个节点下
/etc: 这个目录用来存放所有的系统管理所需要的配置文件和子目录。
/home:用户的主目录,在Linux中,每个用户都有一个自己的目录,一般该目录名是以用户的账号命名的。
/tmp:这个目录是用来存放一些临时文件的。用完即丢的文件,可以放在这个目录下,安装包!
/root:该目录为系统管理员,也称作超级权限者的用户主目录。
/opt:这是给主机额外安装软件所摆放的目录。比如你安装一个ORACLE数据库则就可以放到这个目录下。默认是空的。
/usr:这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下

常用的基本命令

​ 目录管理:绝对路径、相对路径
​ cd 命令 切换目录
​ ls (列出目录!)
​ pwd 显示当前用户所在的目录!
​ rmdir 删除目录
​ cp (复制文件或者目录)
​ rm (移除文件或者目录!)
​ mv 移动文件或者目录!重命名文件

基本属性

​ [ r ]代表可读(read)、[ w ]代表可写(write)、[ x ]代表可执行(execute)。
​ 第0位确定文件类型,第1-3位确定属主(该文件的所有者)拥有该文件的权限。第4-6位确定属组(所有者的同组用户)拥有该文件的权限,第7-9位确定其他用户拥有该文件的权限。
​ 第1、4、7位表示读权限,如果用"r"字符表示,则有读权限,如果用"-“字符表示,则没有读权限;
​ 第2、5、8位表示写权限,如果用"w"字符表示,则有写权限,如果用”-“字符表示没有写权限;
​ 第3、6、9位表示可执行权限,如果用"x"字符表示,则有执行权限,如果用”-"字符表示,则没有执行权

chgrp:更改文件属组

文件内容查看

cat 由第一行开始显示文件内容,用来读文章,或者读取配置文件啊,都使用cat名
tac 从最后一行开始显示,可以看出 tac 是 cat 的倒着写!
nl 显示的时候,顺道输出行号!
more 一页一页的显示文件内容,带余下内容的(空格代表翻页,enter 代表向下看一行, :f 行号)
less 与 more 类似,但是比 more 更好的是,他可以往前翻页! (空格下翻页,pageDown,pageUp键代表翻动页面!退出 q 命令,查找字符串 /要查询的字符向下查询,向上查询使用?要查询的字符串,n 继续搜寻下一个,N 上寻找!)
head 只看头几行 通过 -n 参数来控制显示几行!
tail 实时查看日志,只看尾巴几行 -n 参数 要查看几行!
ifconfig 命令查看网络配置!
Linux的链接分为两种:硬链接、软链接
​ 硬链接:A—B,假设B是A的硬链接,那么他们两个指向了同一个文件!允许一个文件拥有多个路径,用户可以通过这种机制建立硬链接到一些重要文件上,防止误删!
​ 软链接: 类似Window下的快捷方式,删除的源文件,快捷方式也访问不了!
​ 创建连接 ln 命令!

Vim 编辑器

命令模式(Command mode),输入模式(Insert mode)和底线命令模式(Last line mode)。
i 切换到输入模式,以输入字符。
x 删除当前光标所在处的字符。
​ : 切换到底线命令模式,以在最底一行输入命令。** 如果是编辑模式,需要先退出编辑模式!ESC

账号管理

​ 用户账号的添加、删除与修改。
useradd 命令 添加用户
​ 删除用户 userdel
​ 修改用户 usermod
​ 切换用户!
​ 1.切换用户的命令为:su username
​ 从普通用户切换到root用户,还可以使用命令:sudo su
$表示普通用户
#表示超级用户,也就是root用户
​ 用户口令的管理。
​ 用户组的管理。
​ 属主、属组
创建一个用户组 groupadd
删除用户组 groupdel
​ 修改用户组的权限信息和名字 groupmod -g -n
磁盘管理
df (列出文件系统整体的磁盘使用量) du(检查磁盘空间使用量!)
挂载:mount

进程管理

​ 1、在Linux中,每一个程序都是有自己的一个进程,每一个进程都有一个id号!
​ 2、每一个进程呢,都会有一个父进程!
​ 3、进程可以有两种存在方式:前台!后台运行!
​ 4、一般的话服务都是后台运行的,基本的程序都是前台运行的!
​ ps查看当前系统中正在执行的各种进程的信息!
kill-9进程的id 结束进程:杀掉进程

ElasticSearch

简介

es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。是一种非关系型数据库;具有高性能,高可用,易扩展,易维护的特性。

1、Elasticsearch和Solr比较

当实时建立索引的时候,solr会产生io阻塞,而es则不会,es查询性能要高于solr。
在不断动态添加数据的时候,solr的检索效率会变的低下,而es则没有变化
Solr利用zookeeper进行分布式管理,而es自身带有分布式系统管理功能。Solr一般都要部署到web服务器上,比如tomcat。启动tomcat的时候需要配置tomcat与solr的关联。【Solr 的本质 是一个动态web项目】
Solr支持更多的格式数据[xml,json,csv等],而es仅支持json文件格式。
Solr是传统搜索应用的有力解决方案,但是es更适用于新兴的实时搜索应用。
a) 单纯的对已有数据进行检索的时候,solr效率更好,高于es。
Solr官网提供的功能更多,而es本身更注重于核心功能,高级功能多有第三方插件。
————————————————

2、Elasticsearch 的基本概念:

(1)index 索引:索引类似于mysql 中的数据库,Elasticesearch 中的索引是存在数据的地方,包含了一堆有相似结构的文档数据。

(2)type 类型:类型是用来定义数据结构,可以认为是 mysql 中的一张表,type 是 index 中的一个逻辑数据分类

(3)document 文档:类似于 MySQL 中的一行,不同之处在于 ES 中的每个文档可以有不同的字段,但是对于通用字段应该具有相同的数据类型,文档是es中的最小数据单元,可以认为一个文档就是一条记录。

(4)Field 字段:Field是Elasticsearch的最小单位,一个document里面有多个field

(5)shard 分片:单台机器无法存储大量数据,es可以将一个索引中的数据切分为多个shard,分布在多台服务器上存储。有了shard就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。

(6)replica 副本:任何服务器随时可能故障或宕机,此时 shard 可能会丢失,通过创建 replica 副本,可以在 shard 故障时提供备用服务,保证数据不丢失,另外 replica 还可以提升搜索操作的吞吐量。

3、什么是倒排索引:

​ 在搜索引擎中,每个文档都有对应的文档 ID,文档内容可以表示为一系列关键词的集合,例如,某个文档经过分词提取了 20 个关键词,而通过倒排索引,可以记录每个关键词在文档中出现的次数和出现位置。也就是说,倒排索引是关键词到文档 ID的映射,每个关键词都对应着一系列的文件,这些文件中都出现了该关键词。

4、为什么要使用ES:

ES使用场景

1.搜索引擎:做商品、文档、新闻等搜索功能
2.日志分析:实时了解业务的性能情况
3.数据分析:获取有价值的信息
4.实时监控:监控系统的性能,数据变化,保证系统的正常运行。

ES优势

1.高性能:查询速度特别快;
2.高扩展:自带分布式,可以集群,主从部署。
3.实时分析:可以实时对数据进行查询
4.可靠性:支持数据备份和恢复
####ES缺点
1.一致性问题和对自动操作的依赖性
网络阻塞或节点处理能力饱和可能导致数据不一致,这种现象被称为“脑裂”问题
首先,通过增加副本数可以提高数据的冗余性和可用性,减少脑裂问题的发生。
其次,使用最小化网络延迟集群可以帮助节点之间更快地交换信息,避免数据不一致的情况。
此外,确保硬件资源充足也是预防脑裂问题的重要措施,因为节点处理能力饱和可能导致同步阻塞。最后,优化集群配置,如合理设置节点角色和调整集群参数,也可以有助于减少脑裂问题的发生。

Index Warmup API(操作来预热新创建的索引问题):
不适合当前新的Index Warmup API,用户可能需要手动干预以优化性能
Index Warmup API是ElasticSearch中的一个功能,它允许用户通过注册warmup(搜索)操作来预热新创建的索引,从而提高搜索效率
————————————————

4、text 和 keyword类型的区别:

    两个类型的区别主要是分词:keyword 类型是不会分词的,直接根据字符串内容建立倒排索引,所以keyword类型的字段只能通过精确值搜索到;Text 类型在存入 Elasticsearch 的时候,会先分词,然后根据分词后的内容建立倒排索引

5、query 和 filter 的区别?

(1)query:查询操作不仅仅会进行查询,还会计算分值,用于确定相关度;

(2)filter:查询操作仅判断是否满足查询条件,不会计算任何分值,也不会关心返回的排序问题,同时,filter 查询的结果可以被缓存,提高性能。
————————————————

6.ES有哪些数据类型?

字符串类型

text 、 keyword

数值类型

long, integer, short, byte, double, flfloat, half_flfloat, scaled_flfloat

日期类型

date

te布尔值类型

boolean

二进制类型

binary

PUT localhost:9200/索引名称/类型名称/文档id 	创建文档(指定文档id)
POST localhost:9200/索引名称/类型名称 		 创建文档(随机文档id)
POST localhost:9200/索引名称/类型名称/文档id/_update 修改文档
DELETE localhost:9200/索引名称/类型名称/文档id 	删除文档
GET localhost:9200/索引名称/类型名称/文档id 	查询文档通过文档id
POST localhost:9200/索引名称/类型名称/_search 查询所有数据

IK提供了两个分词算法:ik_smart 和 ik_max_word,其中 ik_smart 为最少切分,ik_max_word为最细粒度划分

// 命令解释
// PUT 创建命令 test1 索引 type1 类型 1 id
PUT /test1/type1/1
{
    "name":"狂神说", // 属性
    "age":16 // 属性
}
查询条件是一步步构建出来的,将查询条件添加到 match 中即可;
match_all的值为空,表示没有查询条件,查询所用;
在查询中,通过 _source 来控制仅返回 name 和 age 属性。
布尔查询:通过在 bool 属性内使用 must 来作为查询条件!看结果,是不是 有点像 and 的感觉,里面的条件需要都满足!
排序查询:正序 或 倒序,在排序的过程中,只能使用可排序的属性进行排序。数字、日期、ID
should (or):或查询
must_not (not):不等于查询
filter 条件过滤查询,过滤条件的范围用 range 表示, gt 表示大于,gte 表示大于等于,lt 表示小于,lte 表示小于等于
term查询精确查询
查找多个精确值(terms)
高亮显示:highlight

term和match的区别:

match是经过分析(analyer)的,也就是说,文档是先被分析器处理了,根据不同的分析器,分析出

的结果也会不同,在会根据分词 结果进行匹配。

term是不经过分词的,直接去倒排索引查找精确的值。

脑裂问题

一个集群中只有一个A主节点,A主节点因为需要处理的东西太多或者网络过于繁忙,从而导致其他从节

点ping不通A主节点,这样其他从节点就会认为A主节点不可用了,就会重新选出一个新的主节点B。过

了一会A主节点恢复正常了,这样就出现了两个主节点,导致一部分数据来源于A主节点,另外一部分数

据来源于B主节点,出现数据不一致问题,这就是脑裂

rabbitmq

MQ有三个部分组成

生产者:主要负责载业务信息的消息的创建

消息服务端:主要负责消息的存储、投递以及跟消息队列相关的附加功能

消费者:主要是根据消息所承载的信息去处理各种业务逻辑

2、为什么要使用rabbitmq?

答:
1、在分布式系统下具备异步,削峰,负载均衡等一系列高级功能;

2、拥有持久化的机制,进程消息,队列中的信息也可以保存下来。

3、实现消费者和生产者之间的解耦。

3、使用rabbitmq的场景。

答:

1、服务间异步通信

2、顺序消费

3、定时任务

4、请求削峰

4、如何确保消息正确地发送至RabbitMQ?如何确保消息接收方消费了消息?

发送方确认模式

1.将信道设置成confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID

2.一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一 ID)。

3.如果 RabbitMQ发生内部错误从而导致消息丢失,会发送一条nack(notacknowledged,未确认)消息。发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。

接收方确认机制

接收方消息确认机制

消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息。保证数据的最终一致性;

下面罗列几种特殊情况

如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。

(可能存在消息重复消费的隐患,需要去重)如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息。
————————————————

5.如何避免消息重复投递或重复消费?

答:

在消息生产时,MQ内部针对每条生产者发送的消息生成一个全局唯一的内部消息id(inner-msg-id),作为去重的依据(消息投递失败并重传),避免重复的消息进入队列;

在消息消费时,要求消息体中必须要有一个 bizId(对于同一业务全局唯一ID,如支付ID、订单ID、帖子ID 等)作为去重的依据,避免同一条消息被重复消费。
————————————————

6、消息基于什么传输?

RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制。

7、消息如何分发?

答:

若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。

通过路由可实现多消费的功能

8.RabbitMQ如何保证消息不丢失

开启生产者消息确认(confirm)模式,
在生产者开启了confirm模式之后,每次写的消息都会分配一个唯一的id,然后如何写入了rabbitmq之中,rabbitmq会给你回传一个ack消息,告诉你这个消息发送成功了;如果rabbitmq没能处理这个消息,会回调你一个nack接口,告诉你这个消息失败了,你可以进行重试。而且你可以结合这个机制知道自己在内存里维护每个消息的id,如果超过一定时间还没接收到这个消息的回调,那么你可以进行重发

2.MQ丢失了消息
我们说了对于mq丢失消息的话我们可以开启mq的持久化,而且持久化和生产者的confirm机制配合起来,只有消息持久化到了磁盘,才会给生产者发送ack,这样就算是在持久化之前rabbitmq挂了,数据丢了,生产者收不到ack回调也会进行消息重发。

3.消费者丢失消息
使用rabbitmq提供的ack机制,首先关闭rabbitmq的自动ack,然后每次在确保处理完这个消息之后,在代码里手动调用ack。这样就可以避免消息还没有处理完就ack。
————————————————

9、mq的缺点

系统可用性降低

一致性问题

10、MQ的优点。

异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。

应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。

流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。

11、Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?

RabbitMQ时效性微秒级,比其他MQ快。Kafka,RocketMQ 高吞吐量,可以做到0消息丢失,支持分布式。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JkU0ZvAs-1685634781275)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230530111346162.png)]

12、rabbitmq几种工作模式

1.点对点(简单)队列

2.工作(公平性)队列模式:多个消费端共同消费同一个队列中的消息

3.发布订阅模式:

P:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
C:消费者,消息的接收者,会一直等待消息到来
Queue:消息队列,接收消息、缓存消息
Exchange:交换机(X)一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、 递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。
    Fanout:广播,将消息交给所有绑定到交换机的队列
    Direct:定向,把消息交给符合指定routing key 的队列
    Topic(常用):通配符,把消息交给符合routing pattern(路由模式)的队列
Exchange(交换机)只负责转发消息,不具备存储消息的能力

4.路由模式Routing

指定一个 RoutingKey(路由key)

​ Exchange 不再把消息交给每一个绑定的队列,而是根据消息的 Routing Key 进行判断,只有队列的 Routingkey 与消息的 Routing key 完全一致,才会接收到消息

5.通配符模式Topics

通配符规则# 匹配一个或多个词,*** 匹配有且仅有1个词**,例如:item.# 能够匹配 item.insert.abc 或者 item.insert,item.* 只能匹配 item.insert

总述:topics模式比routing模式要更加灵活,笼统的说就是routing模式加上通配符

13、RabbitMQ基本概念。

答:

Broker:简单来说就是消息队列服务器实体

Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列

Queue:消息队列载体,每个消息都会被投入到一个或多个队列

Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来

Routing Key: 路由关键字,exchange根据这个关键字进行消息投递

VHost:vhost可以理解为虚拟broker ,即mini-RabbitMQ server。其内部均含有独立的queue、exchange和binding等,但最最重要的是,其拥有独立的权限系统,可以做到vhost范围的用户控制。当然,从RabbitMQ的全局角度,vhost可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。

Producer: 消息生产者,就是投递消息的程序

Consumer:消息消费者,就是接受消息的程序

Channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务由Exchange、Queue、RoutingKey三个才能决定一个从Exchange到Queue的唯一的线路
————————————————

锁补充

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wtF9wWRg-1685634781275)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111623282.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NQdFGPkP-1685634781276)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111632516.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ubyLagO-1685634781276)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111638543.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4nIvBYxW-1685634781276)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111643398.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ACr47OUj-1685634781276)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111648492.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WLosqtHv-1685634781276)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111657466.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5kkKZIw9-1685634781277)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111736085.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3NOIgbwm-1685634781277)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111740591.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SeVdiLmv-1685634781277)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111748412.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wNLBHFhd-1685634781277)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111752680.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OgK97jnd-1685634781277)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111758476.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-slE4gk3Z-1685634781278)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111803281.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bd6tTZyH-1685634781278)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111814543.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qhwa6MJM-1685634781278)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111821273.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TWnIflim-1685634781278)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111827678.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f78JBUM2-1685634781278)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111832542.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cNWTaGSJ-1685634781279)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111837820.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DbHcLLWN-1685634781279)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111850289.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VeEq8tA9-1685634781279)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111856034.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u8wT22Eg-1685634781279)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111900458.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5jJOTkUo-1685634781279)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111907970.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lO468ECF-1685634781279)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111912498.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTfcYQhp-1685634781280)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111919635.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lIjoVeaW-1685634781280)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111927780.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8zasZleh-1685634781280)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111931261.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C1jz38aT-1685634781280)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111935252.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v8bdWn2E-1685634781280)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111939542.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BdJBZuTe-1685634781281)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522111947351.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jk1oD1fu-1685634781281)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112014940.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U4UQNnFq-1685634781281)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112019528.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SZGQ5ITF-1685634781281)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112023406.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8DwFGc2H-1685634781281)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112027054.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-grMolVyc-1685634781281)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112031292.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pa7YsZdd-1685634781282)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112036485.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jqQkZKc6-1685634781282)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112040702.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hn8xwh40-1685634781282)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112044454.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0D77AfAY-1685634781282)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112051155.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iIEf6xHy-1685634781282)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112055851.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ivqujNRd-1685634781283)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112101563.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fLaa3zdM-1685634781283)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112108387.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZJpMipoZ-1685634781283)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112112317.png)]

集合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GJ2qYpJS-1685634781283)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112127807.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mYktM07P-1685634781283)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112131019.png)]

1.7和1.8的hashMap有什么区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6FSuBmoK-1685634781284)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112151000.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MGVe78ZN-1685634781284)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112155243.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vggPQ9yq-1685634781284)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112159390.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GvibbbnL-1685634781284)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112203716.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8oHGqXG-1685634781284)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112207499.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eFtT6ana-1685634781285)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112211555.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IBh00MTg-1685634781285)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112215719.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9YGVFp0p-1685634781285)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112221246.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7TUX2xDf-1685634781285)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112224648.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EUe65DZK-1685634781285)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112229185.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-46vBd4nF-1685634781286)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112234444.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K6kAEkn2-1685634781286)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112238366.png)]

redis

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OIilGuPJ-1685634781286)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112244114.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MOXzzdCE-1685634781286)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112301543.png)]

缓存雪崩

一句话总结
在高并发下,大量缓存key在 同一时间失效,大量请求直接落在数据库上,导致数据库宕机。

解决方案
1.随机设置key失效时间,避免大量key集体失效

setRedis (Key,value,time + Math.random() * 10000) ;

2.若是集群部署,可将热点数据均匀分布在不同的Redis库中也能够避免key全部失效问题
3.不设置过期时间
4.跑定时任务,在缓存失效前刷进新的缓存

缓存穿透。

一句话总结
redis缓存和数据库 中没有相关数据(例用户直接携带 d<=0 的参数不断发起请求),redis中没有这样的数据,无法进行拦截,直接被穿透到 数据库,导致数据库压力过大宕机。
解决方案
1.对不存在的数据缓存到redis中,设置key,value值为null(不管是数据未null还是系统bug问题),并设置一个短期过期时间段,避免过期时间过长影响正常用户使用。
2.拉黑该IP地址
3.对参数进行校验,不合法参数进行拦截
4.布隆过滤器 将所有可能存在的数据哈希到一个足够大的bitmap(位图)中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

缓存击穿?

一句话总结
某一个热点key,在不停地扛着高并发,当这个热点key在 失效的一瞬间,持续的高并发访问就 击破缓存 直接访问数据库,导致数据库宕机。
解决方案
1.设置热点数据”永不过期“.
2.加上互斥锁: 上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它
其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后将数据放到redis缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存。

最后总结
雪崩是大面积的kev缓存失效;穿透是redis里 不存在这个缓存key; 击穿是redis 某一个热点kev突然失效,最终的受害者都是数据库。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9I6jJ0E7-1685634781286)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112725382.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hTvJqasu-1685634781287)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112729118.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M10neew9-1685634781287)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112732947.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tEsLkkwc-1685634781287)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112737138.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fji48K7j-1685634781287)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112750182.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7XtJX29t-1685634781287)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112758597.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0tsmKQGq-1685634781287)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112802546.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BSoYJtJb-1685634781288)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112806281.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kTyymqdF-1685634781288)(D:\1study\1study notes\学习笔记及工作文档\mybatis\image-20230522112809891.png)]0

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值