Java面试题库
一,基础
1、Java都有哪些数据类型?基本数据类型有哪些?分别占多少字节?多少位?引用数据类型又有哪些?
Java的数据类型:基本数据类型和引用数据类型
基本数据类型:byte(1),short(2),int(4),long(8),float(4),double(8),char(2),boolean(1)
引用数据类型:类,接口,数组
2、Java语言的几大特性是什么?分别怎么理解?(封装、继承、多态的好处)
继承、封装、多态
继承:
继承是从已有类得到继承信息新创类的过程。
提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。
继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。
封装:
通常认为分装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
面向对象的本质就是将现实世界描绘成一系列的完全自治、封闭的对象。
在类中编写的方法就是对实现细节的一种封装,编写的一个类就是对数据和数据操作的封装。
可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
多态:
多态性是指允许不同子类型的对象对同一消息做出不同的响应。
简单地说就是用同样的对象引用调用同样的方法,但做了不同的事情。
多态性分为编译时的多态性和运行时的多态性。
如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:
当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的。
方法重载(overload)实现的是编译时的多态性(也就是前绑定),而方法的重写(override)实现的是运行时的 多态性(也称为后绑定)。
运行时的多态时面向对象最精髓的东西,要实现多态需要做两件事:
1)、方法重写(子类继承父类并重写父类中已有的或抽象的方法)
2)、对象造型(用父类引用引用子类对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)
3、Java的权限修饰符有哪些?都能加在哪些地方?分别代表什么意义?
Java的权限修饰符有4种:
public、protected、default、private
都能加在哪些地方:
类、方法、成员变量
分别代表的意义:
public:公共的,任意位置都能访问
protected:当前类、同一个包下、子类都能访问,其他包下不能访问
default:当前类、同一个包下可访问,子类和其他包下不能访问
private:私有的,只能当前类中访问
4、什么是重写?什么是重载?
重写:
重写发生在子类和父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型;
比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常
重载:
重载发生在一个类中,同名的方法如果有不同的参数列表,则视为重载
5、final关键字能加在哪些地方?分别代表什么?
属性:属性不可变
方法:方法不可覆盖
类:被其修饰的类不可继承
6、static关键字能加在哪些地方?分别代表什么?
成员变量:使其变成静态变量,该类的所以实例都将共享此变量
方法:使其变成静态方法,类加载后,便可以直接调用此方法,而不需要一个该类的实例
代码块:类加载时,会执行这一段代码
7、接口中可以有哪些成员?抽象类呢?接口和抽象类又有什么区别?(注意JDK1.8接口中是可以出现非抽象方法的:default方法、静态方法)
1 | 接口: |
8、Java异常体系是什么? 运行时异常和检查(编译)异常有什么区别?常见的运行时异常有哪些?
异常体系:
Thorwable类是所以异常和错误的父类
两个直接子类为 Error和 Exception ,分别表示错误和异常。
其中异常类 Exception 又分为运行时异常和编译时异常
运行时异常和检查(编译)异常的区别:
编译时异常:
是Java要求必须处理的,如果程序在编译时期未处理,该程序编译时就会发生错误无法编译,try…catch或throw抛出。
运行时异常:
是代码在运行是才会出现的异常,编译时不需要try…catch。
如:除数是0,数组角标越界,其产生频繁,处理麻烦,若显示声明或者捕获将会对程序的可读性和运行效率影响很大。
所以由系统自动检测并将它们交给缺少的异常处理程序。如果有处理要求也可显示捕获。
9、== 和 equals的异同?
1 | ==: |
10、&与&&、|与||的区别?
&:左右两边的布尔值都是true,整个表达式的值才是true
&&:如果左边的表达式的值为false,右边的表达式会被直接短路掉,不会进行运算,整个表达式的值就是false
|:左右两边的布尔值只要有一个为true,整个表达式的值就是true
||:如果左边的表达式的值为true,右边的表达式会被直接短路掉,不会进行运算,整个表达式的值就是true
11、String可以修改本身吗?为什么?
不可以
字符串内部其实就是一个使用final关键字定义的char[]数组,数组长度一档声明则不可改变
字符串一旦声明则不可改变,变的只是引用变量所指向的对象
12、StringBuffer和StringBuilder的区别是什么?
StringBuilder实在单线程环境下使用的,因为它的使用方法都没有被 synchronized 修饰,因此理论上它的效率比 StringBuffer 高
13、valueOf和toString的区别?
1 | toString:变换的值是 null 的情况,则会抛出 NullPointerException 异常 |
14、大量字符串用 “+” 号进行拼接效率高吗?为什么?应该用什么替代?为什么?
不高
大量字符串用 “+” 号进行拼接,每次拼接一次都会创建一个 StringBuilder 对象,时间和空间开销太大
大量字符串拼接可以使用 StringBuilder 中的 append() 方法代替
append( )方法其实是创建了一个新的数组,扩大了长度,将需要添加的字符串给复制到这个新的数组中
15、创建一个类的实例都有哪些办法?
1、new 关键字。工厂模式是对这种方式的包装
2、类实现克隆接口,克隆一个实例
3、用该类的加载器,newinstance()方法。反射,反射使用实例:Spring的依赖注入、切面编程中的动态代理
4、sun.misc。Unsafe类,allocateInstance()方法创建一个实例
5、实现序列化接口的类,通过IO流反序列化读取一个类,获得实例
16、Java集合的体系是什么样的?
Java集合的体系分为两大部分:Collection 和 Map
Collection 接口下常见的子类有 List、Set 接口
List 接口下有 ArrayList、LinkedList 等实现类
Set 接口下有 HashSet、LinkedHashSet、TreeSet 等实现类
Map 接口下有 HashMap、TreeMap、Hashtable 等实现类
17、Set和List分别有哪些特点?Set去重的原理?
List 中存储的数据是有顺序,并且允许重复;Set 中存储的数据是无序的,且不允许有重复
Set的去重是通过 hash 和 eq 结合实现的
当两个变量的哈希值不相同时,就认为这两个变量不同
当两个变量哈希值一样时,调用 eq 方法,放返回值为 true 时,去除一个;返回 false 时,不去重
18、ArrayList底层原理是什么?扩容原理?
构造方法:
空参构造:new 一个空参 ArrayList 的时候,系统内部使用了一个 new Object[0]数组
带参构造1:传入一个 int 值,该值作为数组的长度值。如果该值小于0,则抛出一个运行时异常。
如果等于0,则使用一个空数组。如果大于0,则创建一个长度为该值的新数组
带参构造2:如果调用构造方法时传入了一个 Collection 的子类,那么先判断该集合是否为null,为null则抛出空指针异常。
如果不是null则将该集合转为数组a,然后将该数组赋值为成员变量array,将该数组的长度作为成员变量size。
add 方法:
1、首先将成员变量 array 赋值给局部变量 a,将成员变量 size 赋值给局部变量 s
2、判断集合长度 s 是否等于数组的长度(如果集合的长度已经等于数组的长度了,说明数组已满,该重新分配新数组了),
重新分配数组的时候需要计算新分配内存的空间大小,如果当前的长度小于 MIN_CAPACITY_INCREMENT/2 ,
(这个常量值是12,除以2就是6,也就是如果当前集合长度小于6)则分配12个长度,如果集合长度大于6则分配当前长度s的一半
3、将新添加的 object 对象作为数组的 a[s] 个元素
4、修改集合长度 size 为 s+1
5、modCotun++,该变量是父类中声明的,用于记录集合修改的次数,
记录集合修改的次数是为了在用迭代器迭代集合时避免并发修改异常,或者说用于判断是否出现并发修改异常的
6、return true,这个返回值意义不大,因为一直返回true,除非报了一个运行时异常
remove 方法:
1、先将成员变量 array 和 size 赋值给局部变量 a 和 s
2、判断形参 index 是否大于等于集合的长度,如果成立则抛出运行时异常
3、获取数组中角标为 index 的对象 result,该对象作为方法的返回值
4、调用 System 的 arraycopy 方法,将删除的元素后面的所有元素全部往前移一位
5、因为删了一个元素,而且集合整体向前移动了一位,因此要将集合最后一个元素置为 null,否则就会内存泄漏
6、重新给成员变量 array 和 size 赋值
7、记录修改次数
8、返回删除的元素
clear 方法:
如果集合长度不等于0,则将数组所有的值都为 null,然后将成员变量 size 设置为0,最后将修改记录加1
19、LinkedList底层原理是什么?和ArrayList的区别是什么?
LinkedList底层原理:
LinkedList 的底层是通过双向链表实现的。
链表的单元是节点,链表由多个节点构成,每个节点都包含三个部分,
头节点指向上一个节点的尾节点,中间节点指向该节点,尾节点指向下一个节点的头节点
和ArrayList的区别:
1、数据结构实现:ArrayList是动态数组的数据结构实现;LinkedList是双向链表的数据结构实现
2、随机访问效率:ArrayList比LinkedList在随机访问的时候效率要高,
因为LinkedList是线性的数据存储方式,使用需要移动指针从前向后一次查找
3、增加和删除效率:在非首尾的增删操作,LinkedList要比ArarryList的效率高,因为ArrayList增删操作要影响数组内其他数据的角标
综合来说,在需要频繁读取集中的元素时,更推荐使用ArrayList,而在增删操作比较多时,更加推荐使用LinkedList
20、HashMap的底层原理是什么?扩容原理?
HashMap的底层采用了一个数组,该数组的默认大小为16且每一个元素都是一个链表的头节点,
每当添加一个元素,就先计算元素Key的hash值,以此确定存放在数组的位置,
如果出现了同一hash值的元素时,这时新元素就会挂在老元素的下面,形成链表,
当链表的长度太长,大于8时,链表就会转换为红黑树,就能提高查找的效率
扩容阈值:
当前容量 * 加载因子 = 阈值,默认的加载因子为0.75
扩容的机制:
当数组中的元素达到阈值时,第一次扩容即16 * 0.75 = 12时,就会触发扩容机制,扩大到元素组的2倍
加载因子:
- 加载因子的大小决定了HashMap的数据密度。
- 加载因子越大HashMap的数据密度也大,发生碰撞的几率越高,数组中的链表越容易长,造成查询或插入的次数增多,性能下降。
- 加载因子越小,就越容易触发扩容,数据密度也就越小,发生碰撞的几率越小,数组中的链表就越短,查询和插入时比较的次数也越小,
性能会更高,但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预留大一点的空间。
- 所以会将加载因子设置为0.7-0.75,此时平均检索长度接近于常数。
21、concurrentHashMap原理是什么?
底层采用分段的数组+链表实现,线程安全
通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。
Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁
扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容
22、JDK8对于HashMap做了哪些优化?
JDK8之前采用的是数组加链表,JDK8之后采用的是数据加链表加红黑树
23、什么是socket?什么是IO/NIO/BIO/AIO?区别是什么?
socket:
socket是一个接口,在用户进程与TCP/IP协议之间充当中间人,完成TCP/IP协议的书写
IO:
在Java中,用流的方式完成IO。
所有IO都被视为单个的字节的移动,通过一个称为Stream的对象一次移动一个字节。
流I/O用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对象。
NIO:
NIO不需要为每个连接开启一个线程,而是统一由Selector管理,当连接没有IO操作时,不需要阻塞线程等待数据,
只有当Selector检测到呢个Channel有有效的IO请求时,再为其开启线程操作,节省线程的开销,操作结束后返回结果,故为同步非阻塞。
BIO:
在BIO通信模式下,服务端每收到一个链接(socket),就会创建专门的线程(serverssocket)响应该连接。
这个连接会一直存在等待读取发来的数据,这个过程会阻塞搜在线程,不能做别的事,直到操作结束后返回结果值,因此这是同步阻塞。
BIO模式下,服务端连接多个客户端时,会开启多个线程响应连接。
AIO:
没有线程阻塞,与AIO不同的是NIO会主动轮询操作系统数据是否准备完毕,而AIO则是等待系统主动通知,再去读取数据
24、什么是反射?可以用来干嘛?列举一下反射应用场景?什么是暴力反射?
反射:
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁
Java 中的反射首先是能够获取到 Java 中要反射类的字节码,获取字节码有三种方法:
1、Class.forName(className) 2、类名.class 3、this.getClass()
然后将字节码中的方法,变量,构造函数等映射成相应的Method,Filed,Constructor等类,这些类中提供了丰富的方法供使用
反射的作用:
1、在运行时判定任意一个对象的所属类
2、在运行时构造任意一个类的对象
3、在运行时判定任意一个类所有具有的成员变量和方法
4、在运行时调用任意一个对象的方法
5、生成动态代理
应用场景:
反射最重要的用途就是开发各种通用框架。
很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,
它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
暴力反射:
反射里的Constructor,Field,Method三个类都有一个 getDeclaredXxx 方法,
可以不受权限控制的获取类的构造函数,字段,方法,如果想要私有构造函数创建对象,字段赋值,方法调用的话,
会自动的访问类的isAccessable,默认的是false,
所以,想要访问类中的私有成员的时候,就要调用setAccessable()方法,将其改为true,这样,就可以对类中的私有成员进行操作了
25、算法了解过吗?冒泡排序、选择排序、快排原理?
冒泡排序:
1、比较相邻的元素。如果第一个比第二个大,就交换它们两个;
2、对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
3、针对所有的元素重复以上的步骤,除了最后一个;
4、重复步骤1~3,直到排序完成。
选择排序:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
然后,再从剩余未排序元素中继续寻找最小(大)元素,再放到已排序序列的末尾。
以此类推,直到所有元素均排序完毕
快速排序:
1、从数列中挑出一个元素,称为 “基准”(pivot);
2、重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3、递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
26、JDK1.8的新特性有哪些?(lamda表达式、stream流、函数式接口、接口中默认方法、方法引用等等)
Lambda表达式,函数式接口,方法引用和构造器调用,Stream API,接口中的默认方法和静态方法,新时间日期API
27、IO流体系
1、按流向分:
输入流:程序可以从内存中读取数据的流
输出流:程序可以向磁盘中写入数据的流
2、按数据传输单位分:
字节流:以字节为单位传输数据的流
字符流:以字符为单位传输数据的流
28、如何实现分布式主键自增?
Redis和IdWork工具类
二、JVM
1、JDK和JRE的区别是什么?
JDK:开发工具
JRE:运行时环境
2、JVM内存模型是什么样的?
1、线程隔离私有数据区
1、程序计数器
2、栈
1、线程栈
2、本地方法栈
2、线程数据共享区
1、堆
1、新生代区
2、老年代区
2、方法区
3、JVM双亲委派加载机制,为什么JVM这么做?有违反双亲委派的例子吗?
双亲委派加载机制:
加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,
如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类
为什么JVM这么做:
1、可以防止核心API库被随意篡改
2、避免类的重复加载:当父亲已经加载了该类时,就没有必要子 ClassLoader 再加载一次,保证被加载类的唯一性
违反双亲委派的例子:
服务供应商接口 SPI,常见的 SPI 有 JDBC、JNDI、JAXP 等
4、类的加载流程是什么样的,每个阶段解释一下
1、加载:
通过一个类的全限定名获取该类的二进制流;
将该二进制流的静态存储结构转化为方法区运行时数据结构;
在内存中生成该类的Class对象,作为该类的数据访问入口;
2、链接:
验证:验证的目的是为了确保Class文件的字节流中的信息不会危害到虚拟机;
准备:准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进行分配。
准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象一起分配在Java堆中。
解析:主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之前,也有可能在初始化之后。
3、初始化:
初始化是类加载的最后一步,真正开始执行类中定义的Java程序代码
5、JVM的GC的主要区域以及各自的GC机制是什么样的?
GC的主要区域:
堆内存
各自的GC机制:
新生代区:Minor GC
1、当Eden区满了时,触发一次MG,即:
还存活的对象从Eden或者Survivor From/Survivor To区中用copy复制算法移至Survivor To/Survivor From区中
Eden中剩下的待回收的对象进行回收,且存活对象的MG次数+1,当对象上的MG次数大于15次时,该对象将会移至老年代区
2、JVM每次只会使用Eden和其中的一块Suivivor区域来为对象服务,所以无论什么时候,总是有一块Survivor区域是空闲的
因此,新生代实际可用的内存空间为90%的新生代空间,所以这种回收机制是比较浪费内存的,优点是简单高效
老年代区:Full GC
当老年代没有内存空间容纳新进来的对象时,触发一次FG,即:
1、首先stop the world:暂停GC回收线程外的所有线程;
2、然后使用标记-清除/整理算法,标记出
6、JVM的GC算法都有哪些?
1、确定哪些是垃圾的算法:
1、引用计数法:
每被引用一次,引用次数+1, 弊端:无法解决对象相互循环引用而无法被回收问题
2、GC Root可达性算法:
从根节点出发,给所有可达对象做标记(解决对象相互循环引用而无法被回收问题)
Java中可作为GC Root的对象有:
1、虚拟机栈中引用的对象(本地变量表)
2、方法区中静态属性引用的对象
3、方法区中常量引用的对象
4、本地方法栈中引用的对象(Native对象)
2、最终回收垃圾的算法
1、标记-清除:效率较高,但最后得到的内存空间不连续
2、标记-整理:最后得到的内存空间是连续的,但整理压缩耗费性能,效率较低
3、复制算法:将内存空间分为两块,每次只使用其中的一块,回收时,将存活的对象复制到另一块中,简单高效,但是浪费内存空间
针对以上的回收算法的利弊,引出分代算法,新生代区使用复制算法,老年代区使用标记-清除/标记-整理算法
三、Spring & SpringBoot
1、说下对SpringIOC的理解,怎么理解控制反转?IOC容器中Bean的生命周期?
IOC:
是spring的核心,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在对象内部直接控制
控制反转:
所有的类都会在spring容器中登记,告诉spring你是什么,你需要什么,然后spring会在系统运行到适当的时候,把你要的东西主动给你,
同时也把你交给其他需要你的东西。
所有的类的创建、销毁都由spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。
对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转
生命周期:
1、通过构造器或工厂方法创建Bean的实例
2、为Bean的属性赋值或对其他Bean的引用
3、调用Bean的初始化方法
4、使用Bean
5、当容器关闭时,调用Bean的销毁方法
2、Spring如何解决IOC中的循环依赖问题?
三级缓存。
比如说:
实例化A的时候,先将A创建(早期对象)放入一个池子中。
这个时候虽然属性没有赋值,但是容器已经能认识这个是A对象,只是属性全是null而已。在
populateBean 方法中对属性赋值的时候,发现A依赖了B,那么就先去创建B了,又走一遍bean的创建过程(创建B)。
同样也会把B的早期对象放入缓存中。
当B又走到 populateBean 方法的时候,发现依赖了A,
又去创建A,但是这个时候去创建A,发现我们在缓存能找到A(早期对象)。
就可以把B的A属性赋值了,这个时候B就初始化完成了。
现在回到A调用的populateBean方法中。
返回的就是B对象了,对A的B属性进行赋值就可以了。
3、说下对SpringAOP的理解、有哪些通知?使用场景有哪些?(底层原理:两种动态代理)
AOP 面向切面编程:
将与业务无关,却为业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于可操作性和可维护性
有哪些通知:
1、前置通知:在目标方法执行之前执行执行的通知
2、环绕通知:在目标方法执行之前和之后都可以执行额外代码的通知
3、后置通知:在目标方法执行之后执行的通知
4、异常通知:在目标方法抛出异常时执行的通知
5、最终通知:最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成
使用场景有哪些:
事务、日志、缓存
4、说下SpringMvc的流程(从访问一个URL到得到页面结果的具体流程:DispatcherServlet的职责流程)
1、用户发送请求至前端控制器DispatcherServlet
2、前端控制器DispatcherServlet收到请求调用HandlerMapping处理器映射器
3、处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成),一并返回给DispatcherServlet
4、DispatcherServlet调用HandlerAdapter处理器适配器
5、HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
6、Controller执行完成返回ModelAndView
7、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9、ViewReslover解析后返回具体View
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
11、DispatcherServlet响应用户
5、对Spring声明式事务的理解?Spring的事务隔离级别?Spring事务传播行为?
理解:
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的
Spring的事务隔离级别:
默认、读未提交、读已提交、可重复读、串行化
Spring事务传播行为:
1、required(默认属性)
如果存在一个事务,则支持当前事务,如果没有事务,则开启一个新的事务
2、Mandatory
支持当前事务,如果当前没有事务,就抛出异常
3、Never
以非事务方式执行,如果当前存在事务,则抛出异常
4、Not_supports
以非事务方式执行操作,如果当前存在事务,把当前事务挂起
5、requires_new
新建事务,如果当前存在事务,则把当前事务挂起
6、Supports
支持当前事务,如果当前没有事务,就以非事务方式执行
7、Nested
支持当前事务,新增Savepoint点,与当前事务同步提交或者回滚
6、什么情况下会让spring事务失效
1、发生自调用,类里面使用this调用本类中的方法,此时这个this不是代理对象,所以事务失效
2、方法不是public的
3、数据库不支持事务
4、没有被spring管理
5、异常被try、catch,事务不会回滚
7、Spring有哪些核心注解?分别的作用?
1 | @Controller:Web控制器模式注解 |
8、Spring和SpringBoot的关系?
Spring Boot不是一门新技术。从本质上来说,Spring Boot就是Spring,它做了一些对Spring Bean的默认配置。
它消除了设置 Spring 应用程序所需的 XML配置,为更快,更高效的开发生态系统铺平了道路。
9、SpringBoot的自动装配原理是什么?
@EnableAutoConfiguration 这个注解通过 @springbootapplication 这个注解被间接的标记在了springboot的启动类上,
在springapplication.run(…)的内部就会执行selectimports()方法,
找到所有javaconfig自动配置类的全限定类名对应的class,然后将所有的自动配置类加载到spring容器中
10、SpringBoot的核心注解是哪个?详细说下
核心注解是@SpringBootApplication,它主要由 @SpringBootConfiguration,@EnableAutoConfiguration、@ComponentScan 这三个构成
@SpringBootConfiguration:
里面就只有一个 @Configuration 主要注解,也就是把该类变成一个配置类所以 @SpringBootConfiguration 就相当于 @Configuration
@EnableAutoConfiguration:
是由 @AutoConfigurationPackage 和 @Import(EnableAutoConfigurationImportSelector.class) 这两个组成的
@AutoConfigurationPackage :
是自动配置包,包括了一个@Import注解,给容器导入了自动配置包的注册器
AutoConfigurationPackages.Registrar.class:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
@Import(AutoConfigurationImportSelector.class):
导入自动配置导入选择器组件
AutoConfigurationImportSelector.class:自动配置导入选择器,主要是从类路径下的META-INF/spring.factories中获取资源
@ComponentScan:扫描包,该注解默认会扫描该类所在的包下所有的配置类
11、SpringBoot项目的启动加载流程大概说下
第一部分:
SpringApplication初始化模块,配置一些基本的环境变量、资源、监听器、构造器
第二部分:
实现了应用具体的启动方案,包括流程的监听模块,加载配置环境模块以及上下文环境模块
第三部分:
自动化配置模块,这个模块是实现SpringBoot的自动装配
12、SpringBoot项目读取配置文件的方式有几种?
1 | @Value |
13、如何自定义SpringBoot starter?
1、引入 SpringBoot 自动化配置依赖
2、创建配置属性类
3、创建 Starter 包的服务类
4、创建自动配置类
5、配置 spring.factories
四、Mysql & Mybatis?
1、什么是索引?
本质是帮助MySQL高效获取数据的数据结构
2、Mysql的数据结构是什么(mysql索引的数据结构)?为什么用这种结构?(如何提高磁盘IO效率)
Mysql的数据结构:
MySql中主要应用的索引数据结构为B+Tree
为什么用这种结构:
由于数据存储于物理磁盘,所以要尽量减少从磁盘IO数据的次数
IO次数取决于B+Tree的高度
B+Tree把真实的数据放到叶子节点,数据项占的空间越小,数据项的数量越多,树的高度越低
3、Mysql的数据IO查找流程是什么样的?
1、对于聚集索引(主键索引)来说,也就是通过主键查询,一次查询就能查询到具体的行数据信息;
2、对于非聚集索引来说(唯一索引、普通索引、全文索引),
如果需要查询的数据列在索引中,如A+B联合索引,根据A去查询B,则通过一次查询就能直接拿到索引上的数据,也就是覆盖索引现象;
如果需要查询的数据不在索引中,则需要先去普通索引树中进行第一次查找得到行数据的主键值,
然后通过主键值去主键索引树中第二次搜索得到真实数据,这种需要二次查询的现象叫做回表查询
4、B+tree和Btree由什么组成?他们的异同?
异同:
1、B+tree是Btree的变体
2、在Btree的基础上增加了叶子节点的顺序访问指针,B+Tree提高了顺序访问的性能
3、Btree每个节点的指针上限为2d+1,B+Tree每个节点的指针上限为2d
4、B+Tree非叶子节点只存储索引值,叶子节点存储真实数据,Btree所有节点上都存储数据
5、Mysql两种存储引擎(InnoDB和Mysiam)的区别?这两种引擎B+tree的叶子结点和非叶子结点分别存储的什么?
区别:
1、InnoDB支持事务和外键,MySIAM不支持
2、InnoDB支持行锁,MySIAM只支持表锁
3、InnoDB的真实数据和索引都存储在同一个文件中,MMySIAM存储在两个文件中
4、InnoDB不支持全文索引,MySIAM支持
InnoDB:
叶节点data域保存了完整的数据记录
MySIAM:
叶节点的data域存放的是数据记录的地址
6、Mysql索引有哪些类型?什么场景使用哪种索引?
普通索引、唯一索引、主键索引、联合索引、全文索引
7、如何进行Mysql优化?(sql优化层面和服务器优化层面)
SQL层面:
1、尽量避免使用select *
2、规范sql语句大小写,sql时有缓存的,避免每次都需要解析
3、使用exsits代替in
4、mysql sql解析执行过程是从右到左,from后面能过滤掉更多数据的基础表放在后面,where后面能过滤掉更多数据的查询条件放在后面
5、查询条件中用相同类型去查询,比如避免数值列用字符串查询条件
6、合理使用索引
7、explain命令进行sql慢查询排查
服务器优化层面:
1、读写分离:主节点写,从节点读
2、分库:根据业务或者其他维度把数据存放到不同数据库
3、分表:
1、水平分表:字段都一样,分多张表存放不同时间范围或不同维度的数据,如实时数据表、历史数据表
2、垂直分表:将不同字段放在多张表,使用外键关联
4、常用分库分表中间件:阿里的Cobar及开源社区基于Cobar维护的Mycat
8、Sql调优你会从何入手(措施)?
explain命令进行sql慢查询排查:
1、id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序,id相同,执行顺序从上至下,id值越大,优先级越高,越先执行
2、select_type:查询类型 SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT
SIMPLE:表示简单杳询,其中不包括连接查询和子查询;
PRIMARY:表示主查询,或者最外层的查询语句;
SUBQUERY:子查询中的第一个SELECT语句;
DERIVED:导出表的SELECT (FROM语句的子查询);
UNION:表示连接查询的第2个或后面的查询语句;
UNION RESULT:连接查询的结果;
3、table:正在访问哪个表
4、partitions:匹配的分区
5、type:访问的类型,效率从快到慢:
NULL>system>const>eq_ref>ref>ref_or_null>index_merge>range>index>ALL
6、possible_keys:显示可能应用在这张表中的索引,一个或多个,但不一定实际使用到
7、key:实际使用到的索引,如果为NULL,则没有使用索引
8、key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
9、ref:显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值
10、rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数,这个数量越小越好
11、filtered:查询的表行占表的百分比
12、Extra:包含不适合在其它列中显示但十分重要的额外信息
9、Mysql中如何合理使用索引?有哪些会使索引失效的情况?
何合理使用索引
1、为合适的列添加索引(主键、唯一索引、组合索引)
2、尽量建立联合索引,也省空间成本
3、尽量使用覆盖索引
4、避免会使索引失效的操作
会使索引失效的情况:
1、索引列有null值不走索引
2、使用is null或is not null不走索引
3、各种负向查询not ,not in, not like ,<> ,!= ,!> ,!< 不会使用索引
4、like将%放左边不走索引
5、查询条件的数据类型做了隐式转换
6、使用in或union代替or,or两侧有非索引列就不会走索引
7、尽量保持索引列干净,不在索引列上使用函数转换、运算
8、联合索引要遵循最左匹配原则
9、使用比较运算或between会使联合索引从使用比较运算的下一个索引处断开
10、Mysql如何排查慢查询(哪个关键字)?分别会列出来哪些信息项?
explain命令进行sql慢查询排查:
1、id:select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序,id相同,执行顺序从上至下,id值越大,优先级越高,越先执行
2、select_type:查询类型 SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION、UNION RESULT
SIMPLE:表示简单杳询,其中不包括连接查询和子查询;
PRIMARY:表示主查询,或者最外层的查询语句;
SUBQUERY:子查询中的第一个SELECT语句;
DERIVED:导出表的SELECT (FROM语句的子查询);
UNION:表示连接查询的第2个或后面的查询语句;
UNION RESULT:连接查询的结果;
3、table:正在访问哪个表
4、partitions:匹配的分区
5、type:访问的类型,效率从快到慢:
NULL>system>const>eq_ref>ref>ref_or_null>index_merge>range>index>ALL
6、possible_keys:显示可能应用在这张表中的索引,一个或多个,但不一定实际使用到
7、key:实际使用到的索引,如果为NULL,则没有使用索引
8、key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度
9、ref:显示索引的哪一列被使用了,如果可能的话,是一个常数,哪些列或常量被用于查找索引列上的值
10、rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需读取的行数,这个数量越小越好
11、filtered:查询的表行占表的百分比
12、Extra:包含不适合在其它列中显示但十分重要的额外信息
11、事务的特性是什么?Mysql事务隔离级别有哪几种?分别会产生什么问题?Mysql默认隔离级别是什么?Oracle呢?
事务的特性:
原子性、一致性、隔离性、永久性
Mysql事务隔离级别:
读未提交、不可重复读、可重复读、串行化
分别会产生什么问题:
读未提交:一个事物读到了另一个事务尚未提交的数据,不符合事务的隔离性
不可重复读:同一个事务中针对同一行记录两次读出来的结果不一样,原因就是第二次读到了其他事务修改提交的数据
可重复读:同一个事务中针对同一范围内的数据两次读出来的结果不一样,原因就是第二次读到了其他事务新增提交的数据
mysql默认隔离级别:
可重复读,但是一般会设置为不可重复读,因为在实际业务中常常是,一个事务中需要读到别的事务提交修改的数据
oraclel默认隔离级别:
不可重复读
12、Mysql的行锁、表锁,悲观锁、乐观锁?
1 | 行锁: |
13、Mysql的vachar和char的区别?
vachar:
长度为可变的,实际使用多少空间就占多少空间。
char:
列长度固定,为创建表时声明的长度,长度值范围是1到255,当char值未填满指定长度时,其他空间会用空格进行填充,检索CHAR值时需删除尾随空格。
14、什么是内连接(inner join)、外连接(left join)?
内连接:
只显示左右两表中匹配条件的行,在结果表中删除与其他被连接表中没有匹配行的所有行
左外连接:
把左边表的数据全部取出来,而右边表的数据有相等的,显示出来,如果没有,显示NULL
15、平时Mysql的sql练习要练到位!!
牛客网,练习SQL语句
16、Mybatis底层的原理?一级缓存和二级缓存是什么?
Mybatis底层的原理:
动态代理
一级缓存:
Mybatis中sqlSession对象的缓存,当执行查询以后,查询的结果会同时存入到SqlSession提供的一块区域中,该区域的结构是一个Map,
当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,的话直接拿出来用,
当SqlSession对象消失时,mybatis的一级缓存也就消失了,
同时一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除、commit(),close等方法时,就会清空一级缓存。
二级缓存:
Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存,但是其中缓存的是数据而不是对象
17、mybatis #{}和${}的区别?
#{}方式能够很大程度防止sql注入
${}的方式无法防止Sql注入
18、Mysql存储过程、存储函数、触发器分别用来干嘛的?创建语法是什么?
存储过程:
存储过程把经常使用的SQL语句或业务逻辑封装起来,预编译保存在数据库中,当需要时从数据库中直接调用,省去了编译的过程。
提高了运行速度同时降低网络数据传输量
存储函数:
参数可以有多个,也可以没有参数,必须有且只有一个返回值
触发器:
触发器的执行不是由程序调用,也不是由手工启动,而是由事件来触发、激活从而实现执行
19、union和unionAll有什么区别?
Union:
对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序
在进行表链接后会筛选掉重复的记录,所以在表链接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果
Union All:
对两个结果集进行并集操作,包括重复行,不进行排序
如果返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了
20、创建表、删除表、更新表字段语句?
创建表:
create table 表名
删除表:
drop table 表名
更新表字段:
修改字段名称:ALTER TABLE 表名 CHANGE 旧字段名 新字段名 数据类型;
修改字段数据类型:ALTER TABLE 表名 MODIFY 字段名 新数据类型;
21、mysql左外连接语句的写法?
Select A.name,B.name from A Left Join B on A.id=B.id;
22、听过InnoDB的Mvcc技术吗?说下是什么?
Mvcc:
多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,
取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能
23、Java实现动态代理有哪些方式?区别是什么?
Java实现动态代理有哪些方式:
jdk动态代理、cglib动态代理
区别:
jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。
总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效。
还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。
如果没有上述前提,jdk动态代理不能应用。
由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。
五、多线程
1、创建线程的方式有哪些?相比继承Thread类,实现Runable接口的好处是什么?
创建线程的方式:
1、实现Runnable接口
2、继承Thread类
好处:
1、适合多个相同程序代码的线程去处理同一个资源
2、可以避免由于Java的单继承性带来的局限性
3、增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的
2、线程的状态有哪些?
1 | 1、新建状态 |
3、run()和start()方法有哪些区别?
run():
是来完成实际的业务逻辑,当run()方法结束后,此线程就会终止
start():
是用来开启一个线程的,使线程处于就绪状态,即可以被JVM来调度执行
4、实现线程间通讯的方法有哪些?
1、wait():
让线程处于冻结状态,被wait的线程会被存储到线程池中
2、notify():
唤醒线程池中的一个线程
3、notifyAll():
唤醒线程池中的所有线程
5、wait、notify、notifyAll分别的作用是什么?可以用在同步代码块之外吗?为什么?
分别的作用:
wait():
让线程处于冻结状态,被wait的线程会被存储到线程池中
notify():
唤醒线程池中的一个线程
notifyAll():
唤醒线程池中的所有线程
可以用在同步代码块之外吗:
不行
为什么:
因为这些方法是用于操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程
6、Sleep和Wait的区别?
1 | sleep(): |
7、什么是线程安全问题?什么情况下会产生?如何解决?
线程安全问题:
多个线程同时访问共享数据时可能会出现问题,称为线程安全问题
什么情况下会产生:
当多线程访问共享数据时,由于CPU的切换,导致一个线程只执行了关键代码的一部分,还没执行完此时另一个线程参与进来,导致共享数据发生异常
如何解决:
通过线程同步机制synchronized + 锁来解决线程安全问题
8、什么是死锁?如何防止产生死锁?
什么是死锁:
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去
如何防止产生死锁:
1、设置超时时间
2、使用JUC包提供的并发类,而不是自己设计锁
3、尽量降低锁的粒度
4、尽量使用同步代码块,而不是同步方法
5、给线程其有意义的名字
6、避免的嵌套
7、分配锁资源之前先看能不能回收回来资源
8、专锁专用
9、Synchronized关键字的底层原理是什么?
1 | 当一个线程第一次运行到 synchronized 代码,获取到了 myObject 对象的 monitor 的锁,然后计数器就会加1, |
10、Synchronized可以用在哪些地方?分别的锁对象是什么?
1 | 1、同步代码块:锁对象为括号中的对象 |
11、Synchronized和JUC下Lock锁的异同?
1、Lock是JUC包下提供的封装好的锁,是类的概念,而synchronized是一个虚拟机层面的关键字。
2、Lock显示的加锁和解锁,且解锁要在finally代码块中,否则可能会死锁,而synchronized为隐式的上锁和解锁。
3、Lock锁提供了尝试获取锁和设置获取锁时间的机制,可返回取锁状态,当获取不到锁的时候也可以选择放弃取锁,
而synchronized无法判断返回取锁状态,取锁不成功只能阻塞,没有Lock灵活。
4、Lock锁阻塞可被打断,而synchronized阻塞不可被打断。
5、Lock可实现可重入、可公平锁,而synchronized是可重入、非公平锁。
6、Lock可以很灵活的根据线程角色类型去创建Condition监视器对象,调用await()、signal()、signalAll()进行线程通讯调度,
而synchronized使用Object对象本身作为监视器对象去调用wait() 、notify()、notifyAll()进行线程通讯调度。
7、Lock提供了更丰富的锁分类,如读锁、写锁,可以更细粒度的关注线程安全问题。
12、Synchronized是公平锁还是非公平锁?获取不到锁时会阻塞吗?
非公平,会阻塞
13、Synchronized在JDK1.6做了什么优化?
自适应的CAS自旋、锁消除、锁粗化、偏向锁、轻量级锁
13、同步代码块中执行完wait/notify/notifyAll后会立马释放锁吗?
不会,必须等其所在的同步代码块执行完才会释放锁
14、Lock锁有哪些实现?分别的特点是什么?
1、ReentrantLock
2、ReentrantReadWriteLock类中的静态内部类ReadLock(读-写锁)
3、ReentrantReadWriteLock类中的静态内部类WriteLock(读-写锁)
15、JUC下Lock的监视器对象是哪个类?与Synchronized的监视器有什么异同?
1 | JUC下Lock的监视器对象: |
16、什么是线程可重入?Synchronized具备吗?Lock呢?
重入锁就是一个线程能否获取一个已经由它自己持有的锁。如果可以,就是可重入锁,否则为不可重入锁。它的作用就是能够避免重复获取的时候出现死锁
synchronized 是可重入锁
Lock 是可重入锁
17、什么是AQS?
是一个用于构建锁和同步容器的队列同步器,它是整个JUC包下Lock体系的核心,
如ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore都是基于它来实现的,
它解决了在实现同步容器时设计的大量细节问题,
它的核心构成部分为:使用一个 先进先出的FIFO的队列存储排队等待锁的线程,使用一个用volatile修饰的int类型的state同步状态来记录
当前是否有线程持有锁,0表示没有线程获得锁,1表示有,上锁state就加1,释放锁就对应减1,有重入锁现象,这个值就大于1,然后需要逐级去释放。
18、什么是CAS?什么是CAS的ABA问题?如何解决?
CAS:
CAS其实就是乐观锁的一种实现方式
ABA问题:
1、线程1读取出指定内存地址的数据A,加载到寄存器,此时读取出来的原值不仅将作为要被计算的值A,还会作为比较值A。
2、此时线程1的cpu被线程2抢占了,线程2也从同样的内存地址中读取了同样的数据A,线程2还比线程1先执行完,线程2产生了新数据B,
并且遵守了CAS原理把新数据B存入该内存地址。 (这个时候内存的值由A被该为B)
3、还没完,线程2执行完之后,线程1又没抢过其它线程,此时cpu被线程3抢占,之后步骤和第 2 步一样,线程3从同样的内存地址中读取了数据B,
线程3还比线程1先执行完,线程3产生了新数据A(没错,与一开始的A相等,但值相等并不意味着此A就是彼A,已经被替换了),
并且遵守了CAS原理把新数据A存入该内存地址。(这个时候内存的值由B又变为A)
4、这个时候线程1执行完了,要遵守CAS原理存入数据,然后比较值A是原来的A(简称原A),而执行内存地址中的A是被替换过的了,
但原A的值与内存中的A值是相等的,根据CAS,线程1会把新的执行结果存入该内存地址
在实际业务中,两个数的值相等,但这两个数并不是同一个数
解决方案:
加个版本号,多一步比较版本号即可解决
19、你了解JUC下的哪些工具类,分别有什么作用?(CountdownLatch、Cyclicbarrier、Simephore)
CountdownLatch:
利用它可以实现类似计数器的功能
Cyclicbarrier:
字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行
Simephore:
信号量,Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可
20、说下volatile关键字,有什么作用?原理是什么?
是一个变量类型修饰符,被voltile修饰的变量具有以下特性:
可见性:
保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
实现(缓存共享协议):
对于用volatile形容的变量,线程写入本地内存中的同时会将数据立即刷新到主内存中。
他线程读取该变量时,发现被volatile修饰,会将本地变量值置为无效,然后从主内存中读取。
有序性:
禁止进行指令重排序。为提高执行效率,在不影响最终执行结果的前提下,代码在编译成字节码的时候有可能进行指令重新
排序,这在单线程情况下是没有问题的,但是在多线程的情况下会出现问题。volatile修饰的变量则可以避免这个问题。
不保证原子性:
volatile 只能保证对单次读/写的原子性。i++ 这种操作不能保证原子性。关于volatile 原子性可以理解为把对volatile
变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。
21、说下ThreadLocal,有什么作用?有哪些主要方法,实现原理是什么?为什么会有内存泄漏问题?如何解决?
ThreadLocal:
ThreadLocal是除了加锁这种同步方式之外的另一种可以规避出现多线程安全问题的思路。
ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,
在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题
主要方法:
get()、set()、remove()
实现原理:
每个线程都有属于自己的一个ThreadLocalMap,可通过Thread获得,这个map存储着以threadLock对象为key、以设置的值为value的键值对。
调用get或者set还有remove方法都是操作这个map
内存泄漏:
对于线程池里面不会销毁的线程, 里面总会存在着<ThreadLocal, LocalVariable>的强引用,
因为 final static 修饰的 ThreadLocal 并不会释放,
而 ThreadLocalMap 对于 Key 虽然是弱引用, 但是强引用不会释放, 弱引用当然也会一直有值,
同时创建的 LocalVariable 对象也不会释放, 就造成了内存泄露;
解决方案:
为了避免出现内存泄露的情况, ThreadLocal 提供了一个清除线程中对象的方法, 即 remove,
其实内部实现就是调用 ThreadLocalMap 的 remove 方法:
22、说下线程池的几大核心参数?分别有什么作用?有几种默认的线程池?他们的7个核心参数为什么要那么设置?
线程池的七大核心参数:
1、核心线程数:
没达到核心线程数时,会创建新的线程。当达到核心线程数时,任务会进去队列
2、最大核心线程数:
当达到核心线程数时,会去创建额外的线程来执行任务,最多不超过最大线程数
3、存活时间:
当任务处理完成,额外的线程存活一段时间后,会自行销毁
4、存活时间单位:
空闲等待时间的单位
5、阻塞队列:
利用什么队列来存放任务,有界队列、无界队列等
6、线程池工厂:
线程创建工厂
7、拒绝策略:
分为四大策略:
1、AbortPolicy(默认策略):直接抛出异常,阻止系统正常运行
2、CallerRunsPolicy 策略:等线程池处理不了的任务,谁提交的任务,就给谁拿回去,让其自己执行
3、DiscardPolicy 策略:直接抛弃策略,异常也不会抛,什么都不做
4、DiscardOldestPolicy 策略:丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务
有几种默认的线程池:
1、newCachedThreadPool:
可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
2、newFixedThreadPool:
指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中
3、newSingleThreadExecutor:
单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。
4、newScheduleThreadPool:
定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行
23、单例模式写法有哪几种?(懒汉和饿汉式)那么懒汉式中保证线程安全的写法是什么?为什么要用双重检查模式?
1 | 单例模式写法: |
六、Redis & 多级缓存
1、Redis是一个什么样的数据库?读写速度怎么样?
非关系型型数据库
Redis将数据存储在内存上,避免了频繁的IO操作
2、Redis有哪些数据类型,分别的特点?在你们项目中常见的应用场景有哪些?请列举
数据类型:
1、string(字符串):
一个key对应一个value
2、hash(哈希):
是一个键值对集合
3、list(列表):
简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)
4、set(集合):
string类型的无序集合,且不允许重复的成员
5、zset(有序集合):
string类型元素的集合,且不允许重复的成员
不同的是每个元素都会关联一个double类型的分数
redis正是通过分数来为集合中的成员进行从小到大的排序
zset的成员是唯一的,但分数(score)却可以重复
项目有哪些应用场景:
1、计数器:
对 string 进行自增自减运算,从而实现计数器功能
redis 内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。如每日登录次数计数
2、热点数据缓存:
将热点数据放到内存中。如首页排行榜数据,具有很大访问频次,使用zset可以实现基于score分数排序
3、会话缓存:
用redis统一存储多台应用服务器的会话信息
当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,
从而更容易实现高可用性以及可伸缩性
4、取数据交集、并集:
基于 redis set 的特性可以对共同好友进行很方便的查询
5、分布式事务锁的使用:
基于 set lock requestId nx ex time 模式可以很方便编写分布式事务锁
3、Redis的持久化机制是什么样的?
1 | 1)、什么是RDB?RDB的持久化机制(Save、bgSave)? |
4、Redis主从结构能解决什么问题?
提高并发读写效率
5、Redis主从同步具体流程是什么?
1 | 1)、建立连接 |
6、如何优化主从同步效率?
1 | 1)、从尽量避免全量同步的方面入手(安全重启使runid不发生变化、调大复制积压缓冲区) |
7、Redis的故障恢复依靠什么机制?哨兵机制的主要工作范围、工作流程和作用?
1 | Redis的故障恢复机制: |
8、什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?
1 | 缓存雪崩: |
9、Redis是单线程的,为什么读写效率还那么高?
基于非阻塞的IO多路复用机制的线程模型
10、Redis的线程模型是什么样的?(典型的NIO,非阻塞式IO)
基于非阻塞的IO多路复用机制的线程模型,单线程
11、Redis过期数据的删除策略是什么?有哪些?
定时删除、惰性过期、定期过期
12、Redis的数据淘汰策略是什么?有哪些?
1 | 1、volatile-lru: |
13、Redis的慢查询如何排查?
Redis 慢查询可通过配置两个参数进行:
slowlog-log-slower-than:设置慢查询预设的超时阈值,单位是微秒
slowlog-max-len:表示慢查询日志存储的条数
14、如何正确使用Redis的分布式事务锁?(Zookeeper也可以实现分布式锁)
1 | 正确使用redis分布式事务锁需要保证两个原子性: |
15、Redis的双写一致性如何保证?
1 | 采用延时双删策略: |
16、项目搭建多级缓存的好处是什么?实现多级缓存的流程是什么?(加分项)
好处:
可以避免缓存雪崩(缓存失效,大量请求直达DB),提高系统的可用性
流程:
1、反向代理nginx将请求负载均衡到业务nginx集群
2、接着业务Nginx读取本地缓存(本地缓存可以使用Lua),如果本地缓存命中则直接返回,
使用应用Nginx本地缓存可以提升整体的吞吐量,降低后端的压力,尤其应对热点问题非常有效
3、如果Nginx本地缓存没有命中,则会读取相应的分布式缓存(如Redis缓存,另外可以考虑使用主从架构来提升性能和吞吐量),
如果分布式缓存命中则直接返回相应的数据(并会写到Nginx本地缓存)
4、如果分布式缓存也没有命中,则会回源到Tomcat集群,在回源到Tomcat集群时也可以使用轮询和一致性哈希作为负载均衡算法
5、在Tomcat中,首先读取本地堆缓存,如果有则直接返回(并会写到Redis集群)
6、如果所有的缓存都没有命中只能查询DB或相关服务获取相关数据并返回
7、返回的数据异步写到Redis
17、Redis的hash槽一共有多少个?数据是如何进行入槽的?如果实现动态扩容?
1 | 个数: |
七、Rabbitmq
1、Rabbitmq消息模式有哪些?你们用的哪种?(5种)
1 | 1、简单模式: |
2、Rabbitmq如何保证mq消息可靠性?(3大方面)
1 | 1、保证消息投递到MQ不丢: |
3、Rabbitmq如何实现延时消息?(2种)
1 | 1、利用死信交换机实现延时投递 |
4、什么是死信队列?什么样的消息会进入死信队列?
1 | 死信队列: |
5、Rabbitmq如何解决消息堆积问题?(3种思路)
1 | 1、针对同一队列增加多个消费者进行消息消费 |
6、如何保证消息的幂等性?(从业务层面进行判断)
1 | 1、数据要写mysql,先根据主键查一下,如果这数据都有了,就别插入了,update一下 |
八、微服务
1、概念问题
1 | 1)、什么是微服务?解决微服务各种问题都用了哪些组件? |
2、远程调用
1 | 1)、什么是Feign,用来做什么的?Feign底层调用是怎么实现的?底层协议是什么?优势是什么? |
3、注册中心
1 | 3、配置中心1)、Eureka |
3、配置中心
1 | 1)、实现配置中心都可以使用哪些技术? |
4、服务保护
1 | 1)、Hystrix |
5、网关
1 | 1)、网关有什么作用?在你们项目中用网关来干嘛了? |
6、分布式事务
1 | 1)、什么是本地事务?什么是分布式事务? |
九、Docker
1、什么是Docker?优点是什么?有哪些核心概念?
概念:
Docker是一个快速交付应用、运行应用的技术
优点:
1、可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统
2、运行时利用沙箱机制形成隔离容器,各个应用互不干扰
3、启动、移除都可以通过一行命令完成,方便快捷
核心概念:
解决大型项目依赖关系复杂,不同组件依赖的兼容性问题:
1、Docker允许开发中将应用、依赖、函数库、配置一起打包,形成可移植镜像
2、Docker应用运行在容器中,使用沙箱机制,相互隔离
解决开发、测试、生产环境有差异的问题:
Docker镜像中包含完整运行环境,包括系统函数库,仅依赖系统的Linux内核,因此可以在任意Linux操作系统上运行
2、镜像操作命令有哪些?
拉取、推送、查看、查看所有、删除、删除所有、制作镜像、导出镜像、加载镜像
3、容器操作命令有哪些?
查看所有、查看正在运行的容器、删除、强制删除、创建容器、创建并运行、
启动容器、停止容器、重启容器、暂停容器、恢复容器、进入容器
4、数据卷操作命令有哪些?
创建数据卷、查看单个数据卷详情、查看数据卷列表、删除数据卷、删除未使用的数据卷、创建容器时挂载数据卷
5、docker如何自定义镜像?docker file的语法是什么样的?
自定义镜像:
1、准备基础镜像和tar包
2、创建Dockerfile
3、使用Dockerfile创建镜像
docker file的语法:
保留字指令要大写,后边有空格,后边必须有内容。 比如:FROM scratch
指令从上往下依次执行
6、docker compose是干嘛的?语法是什么样的?
作用:
调用docker服务的API负责实现对docker容器集群的快速编排,即通过一个单独的yaml文件,来定义一组相关的容器来为一个项目服务
语法:
一份标准配置文件应该包含 version、services、networks 三大部分,其中最关键的就是 services 和 networks 两个部分
十、ElasticSearch
1、什么是ES?由什么语言编写?和Lunce的关系?什么是ELK?
概念:
1)、是一款分布式、高性能、高扩展,支持海量数据分析、搜索、计算的搜索引擎
2)、基于Java语言编写,发起的请求时基于Json风格的符合RestFull风格的DSL语句
3)、前身Lucene诞生于1999年,2004年变为了compass,2010年重构成了现在的ES
4)、ELK:是一个围绕ElasticSearch的技术栈,包含:ElasticSearch、Logstatch、Kibana,最新版本7.16.3
5)、Solr:是ES的一款竞品,2016被ES反超,主流成为ES
由什么语言编写:
Java
和Lunce的关系:
前身Lucene诞生于1999年,2004年变为了compass,2010年重构成了现在的ES
什么是ELK:
是一个围绕ElasticSearch的技术栈,包含:ElasticSearch、Logstatch、Kibana,最新版本7.16.3
2、ES的核心概念有哪些?什么是索引?什么是文档?文档格式是什么?什么是映射?什么是DSL?
核心概念:
1)、倒排索引
对文档进行合理化的分词,形成一个不重复的词条列表,每一个词条对应一个文档id集合,将来根据文档分成词条找id,
再根据id找到相应的文档(涉及到两次的Btree查询)
2)、索引 – index
同一类型文档的集合,相当于mysql的表
3)、映射 – mapping
对索引结构的约束,相当于mysql的schema(约束)(表结构)
4)、文档 – document
Json格式的数据,相当于mysql的row(行)
5)、字段 – field
一个个的字段,相当于mysql的列
6)、DSL语句
Json风格的符合restful风格的请求语句
3、什么是倒排索引?倒排索引建立过程?
倒排索引:
对文档进行合理化的分词,形成一个不重复的词条列表,每一个词条对应一个文档id集合,将来根据文档分成词条找id,
再根据id找到相应的文档(涉及到两次的Btree查询)
倒排索引建立过程:
1、将每一个文档的数据利用算法分词,得到一个个词条
2、创建表,每行数据包括词条、词条所在文档id、位置等信息
3、因为词条唯一性,可以给词条创建索引,例如hash表结构索引
4、ES有哪些数据类型?keyword和text有什么区别?
数据类型:
1、字符串:
text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)
2、数值:
long、integer、short、byte、double、float、
3、布尔:
boolean
4、日期:
date
5、对象:
object
keyword和text有什么区别:
text可分词,keyword不可分词
5、重要:说说用户输入框输入查询条件 进行ES搜索的底层原理过程
1、如果用户输入条件“华为手机”进行搜索。
2、对用户输入内容分词,得到词条:“华为”、“手机”。
3、拿着词条在倒排索引中查找,可以得到包含词条的文档id:1、2、3。
4、拿着文档id到正向索引中查找具体文档。
6、ES分词器适合在什么字段上使用?分词器在ES中的使用场景有哪些?(建立倒排索引时对文档分词和用户搜索时对搜索条件分词)
建立倒排索引时对文档分词和用户搜索时对搜索条件分词
7、你们分词器用的哪种?为什么要自定义拼音分词器?为什么搜索时不能用拼音分词器?
用的哪种:
IK分词器
为什么要自定义拼音分词器:
要实现根据字母做补全,就必须对文档按照拼音分词。
默认的拼音分词器会将每个汉字单独分为拼音,而我们希望的是每个词条形成一组拼音,需要对拼音分词器做个性化定制,形成自定义分词器
为什么搜索时不能用拼音分词器:
为了避免搜索到同音字,搜索时不要使用拼音分词器
8、ES有哪些查询类型,分别用在什么场景?如何实现复合查询?要给指定的数据进行加分如何实现?
查询类型:
1、查询所有:
查询出所有数据,一般测试用。例如:match_all
2、全文检索(full text)查询:
利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:
match_query
multi_match_query
3、精确查询:
根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。例如:
ids
range
term
4、地理(geo)查询:
根据经纬度查询。例如:
geo_distance
eo_bounding_box
5、复合(compound)查询:
复合查询可以将上述各种查询条件组合起来,合并查询条件。例如:
bool
function_score
如何实现复合查询:
1、根据原始条件查询搜索文档,并且计算相关性算分,称为原始算分(query score)
2、根据过滤条件,过滤文档
3、符合过滤条件的文档,基于算分函数运算,得到函数算分(function score)
4、将原始算分(query score)和函数算分(function score)基于运算模式做运算,得到最终结果,作为相关性算分。
给指定的数据进行加分如何实现:
算分函数:
可以简单粗暴,直接给固定的算分结果,weight
9、给指定的数据进行加分如何实现?如何实现高亮?
给指定的数据进行加分如何实现:
1、排序
2、分页
3、高亮
如何实现高亮:
1、给文档中的所有关键字都添加一个标签,例如标签
2、页面给标签编写CSS样式
10、ES有哪些聚合查询?
1、桶(Bucket)聚合:
用来对文档做分组
2、度量(Metric)聚合:
用以计算一些值,比如:最大值、最小值、平均值等
3、管道(pipeline)聚合:
其它聚合的结果为基础做聚合
11、ES如何实现自动补全查询
1、修改索引库结构,设置自定义拼音分词器
2、修改索引库的需要补全的属性,使用自定义分词器
3、索引库添加一个新字段suggestion,类型为completion类型,使用自定义的分词器
4、给实体类doc添加suggestion字段
5、重新导入数据到索引库
12、如何自定义分词器?
1、创建索引库时,在settings中配置,可以包含三部分
2、character filter:
在tokenizer之前对文本进行处理。例如删除字符、替换字符
3、tokenizer:
将文本按照一定的规则切割成词条(term)。例如keyword,就是不分词;还有ik_smart
4、filter:
将tokenizer输出的词条做进一步处理。例如大小写转换、同义词处理、拼音处理等
13、如何实现es与mysql的数据同步?
1、同步调用:
1、ES微服务对外提供接口,用来修改elasticsearch中的数据
2、操作数据库微服务在完成数据库操作后,直接调用微服务提供的接口,
2、异步通知:
1、操作数据库微服务对mysql数据库数据完成增、删、改后,发送MQ消息
2、ES微服务监听MQ,接收到消息后完成elasticsearch数据修改
3、监听binlog
1、给mysql开启binlog功能
2、mysql完成增、删、改操作都会记录在binlog中
3、ES微服务基于canal监听binlog变化,实时更新elasticsearch中的内容
14、es集群节点有哪些类型?分别的职责是什么?
1、备选主节点(master eligible):
主节点可以管理和记录集群状态、决定分片在哪个节点、处理创建和删除索引库的请求
2、数据节点(data):
存储数据、搜查、聚合、CRUD
3、ingest:
数据存储之前的预处理
4、coordinating:
路由请求到其他节点,合并其他节点处理的结果,返回给用户
15、什么是es脑裂问题?
1、一个集群中,主节点与其它节点失联
2、此时,node2和node3认为node1宕机,就会重新选主
3、当node3当选后,集群继续对外提供服务,node2和node3自成集群,node1自成集群,两个集群数据不同步,出现数据差异
4、当网络恢复后,因为集群中有两个master节点,集群状态的不一致,出现脑裂的情况
十一、压测、高并发性能优化
1、jemiter压测工具使用
2、多级缓存(nginx共享字典、redis缓存、tomcat进程缓存)
3、数据库主从读写分离(mycat)
4、发布:
开发环境、测试环境用的shell脚本自动发布(包括从git上拉取代码、打包编译、启动)
预上线、线上环境用的jenkins持续集成
5、提交代码之前需要做什么?
- 标题: Java面试题库
- 作者: 小颜同学
- 创建于: 2023-05-28 11:05:48
- 更新于: 2024-02-07 14:23:20
- 链接: https://www.wy-studio.cn/2023/05/28/Java面试题库/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。