高频面试题

目录

1.集合

1.1. Collection和Collections的区别  

1.2. LIst 与 Set 的区别

1.3. Set内存放的元素为什么不可以重复,内部是如何保证和实现的?

1.4. ArrayList 与 LinkedList 区别

1.5.  Arraylist与LinkedList,Map默认空间是多少;

1.6.  ArrayList 与 Vector 区别  ;

1.7. HashSet 和 HashMap 区别  ;

1.8. HashMap 和 Hashtable 的区别  ;

1.9.  谈谈HashMap,哈希表解决hash冲突的方法;

1.10. HashMap 和 ConcurrentHashMap 的区别  ;

1.11. ConcurrentHashMap 的工作原理及代码实现,如何统计所有的元素个数  

1.12. ArrayList和LinkList的删除一个元素的时间复杂度;(ArrayList是O(N),LinkList是O(1));

1.13.  HashMap在什么时候时间复杂度是O(1),什么时候是O(n),什么时候又是O(logn); 

1.14. HashMap 底层实现原理

2. JVM

2.1. JVM 内存模型

2.2. 堆分为哪几块,比如说新生代老生代,那么新生代又分为什么两个Survivor;

2.3. 如何判断对象是否可以回收或存活(引用计数法与GC Root可达性分析法区别;

2.4. 常见的GC回收算法及其含义  ;

2.5. 类加载的过程;

2.6. 什么情况下会触发类加载;

2.7. 什么是双亲委派模型?有哪些类加载器?

2.8.  JVM怎么调优?

3. 多线程 

3.1.  创建线程的方式;

3.2. 一个线程连着调用start两次会出现什么情况?

3.3. wait方法能不能被重写,wait能不能被中断;

3.4. 线程池的实现原理?四种线程池?重要参数及原理?任务拒接策略有哪几种? 

3.5.  wait/notify/notifyAll⽅法需不需要被包含在synchronized块中?这是为什么?  

3.6. 两个线程设计题。记得一个是:t1,t2,t3,让t1,t2执行完才执行t3,原生实现。

3.7. synchronized 与 lock 的区别 

3.8. 产生死锁的四个条件;

3.9.  CAS无锁的概念、乐观锁和悲观锁  ;

3.10. Java中有哪些同步方案;

3.11. 怎么保证多线程情况下,同一方法同时只被一个线程调用

4.  设计模式

4.1. 常见的设计模式

4.2. 设计模式的的六大原则及其含义  

4.3. 常见的单例模式以及各种实现方式的优缺点,哪一种最好,手写常见的单利模式  

4.4. JDK中哪些实现了单例模式?  

4.5. 设计模式在实际场景中的应用

4.6. Spring中用到了哪些设计模式 

5. 数据库

5.1. 存储引擎的 InnoDB与MyISAM区别,优缺点,使用场景  

5.2. 为什么要用 B+tree作为MySQL索引的数据结构 

5.3. 遇到过索引失效的情况没,什么时候可能会出现,如何解决  

5.4. 数据库事物ACID

5.5. 事物的隔离级别(读未提交、读以提交、可重复读、可序列化读)  

 5.6. 如何选择合适的分布式主键方案 

5.7. 数据库高并发下的优化思路;  

5.8. 数据库三范式

5.9. 聚集索引和非聚集索引有何区别?

5.10. 怎么查看sql使用索引情况?

5.11.  Explain内容中type有几种类型?各代表什么意思?

5.12. 说说数据库sql的优化

6. WEB

6.1. cookie和session的区别  

6.2. http协议简单协议

6.3. Get和Post请求方式的区别?

6.4. JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?  

6.5. jsp九大内置对象  

6.6. jsp四大作用域  

6.7. servlet的生命周期  

6.8. 重定向和转发的区别?  

6.9. 如何防止表单重复提交?  

6.10. 过滤器有哪些作用,以及过滤器的生命周期  

6.11. session 共享怎么做的(分布式如何实现 session 共享)

6.12. Servlet是安全的吗?

7. Spring

7.1. BeanFactory 和 ApplicationContext 有什么区别 

7.2. Spring Bean 的生命周期

7.3. 说说 Spring AOP、Spring AOP 实现原理 

7.4. Spring 事务实现方式、事务的传播机制、默认的事务类别  

7.5. Spring注入有那些方式?  

7.6. 简述Spring的优缺点? 

7.7. Mybatis 的工作机制

8. SpringBoot   

8.1. 什么是springboot 

8.2. springboot常用的starter有哪些 

8.3. 你如何理解 Spring Boot 中的 Starters?

8.4. springboot自动配置的原理

8.5.  springboot读取配置文件的方式

8.6. springboot集成mybatis的过程

8.7. 为什么要用 Spring Boot?

8.8. Spring Boot 的核心配置文件有哪几个?它们的区别是什么?  

8.9. Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?  

8.10. 运行 Spring Boot 有哪几种方式?

8.11. Spring Boot 2.X 有什么新特性?与 1.X 有什么区别?

8.12. Spring Boot 有哪几种读取配置的方式?

8.13. SpringBoot 实现热部署有哪几种方式?

8.14.springboot的事务是如何实现的?

8.15. springboot的事物注解什么时候失效?如何解决

9. SpringCloud

9.1. Spring Cloud 你们在项目中如何使用?

9.2. Spring Cloud 有哪些组件

 9.3. springcloud如何实现服务的注册和发现

9.4. ribbon和feign区别  

9.5. springcloud和dubbo的区别

9.6. 分布式锁

10. MQ 消息队列

10.1.延迟消息怎么实现

10.2. 怎么保证不重复消费

10.3. 消息队列丢失数据怎么解决

10.4. 怎么控制分布式事务

11. Redis

11.1. Redis消息一致性问题怎么解决

11.2. 说说redis的使用场景

11.3. 服务雪崩,服务击穿

11.4. Redis 持久化机制

1.集合

1.1. Collection和Collections的区别  

Collections是个java.util下的类,它包含有各种有关集合操作的静态方法。

Collection是个java.util下的接口,它是各种集合结构的父接口

1.2. LIst 与 Set 的区别

list:列表,表达形式 [ ],或者list(),有序,通过索引值进行查找

set:集合,表达形式set([ ]),无序自动去重。可以做集合的交集,并集,差集

1.3. Set内存放的元素为什么不可以重复,内部是如何保证和实现的?

 HashSet类实现了Set接口, 其底层其实是包装了一个HashMap去实现的。HashSet采用HashCode算法来存取集合中的元素,因此具有比较好的读取和查找性能。首先根据key的hashCode()返回值决定该Entry的存储位置,如果两个key的hash值相同,那么它们的存储位置相同。如果这个两个key的equals比较返回true。那么新添加的Entry的value会覆盖原来的Entry的value,key不会覆盖

1.4. ArrayList 与 LinkedList 区别

ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构;

对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针;

对于添加和删除操作add和remove,一般大家都会说LinkedList要比ArrayList快,因为ArrayList要移动数据。但是实际情况并非这样,对于添加或删除,LinkedList和ArrayList并不能明确说明谁快谁慢

所以当插入的数据量很小时,两者区别不太大,当插入的数据量大时,大约在容量的1/10之前,LinkedList会优于ArrayList,在其后就劣与ArrayList,且越靠近后面越差。所以个人觉得,一般首选用ArrayList,由于LinkedList可以实现栈、队列以及双端队列等数据结构,所以当特定需要时候,使用LinkedList,当然咯,数据量小的时候,两者差不多,视具体情况去选择使用;当数据量大的时候,如果只需要在靠前的部分插入或删除数据,那也可以选用LinkedList,反之选择ArrayList反而效率更高
 

1.5.  Arraylist与LinkedList,Map默认空间是多少;

jdk1.6 ArrayList 初始化大小是 10,扩容大小规则是,扩容后的大小= 原始大小+原始大小/2 + 1

jdk1.7 ArrayList 初始化大小是 0,第一次添加元素时,会将容量设置为10,扩容大小规则是,扩容后的大小= 原始大小+原始大小/2 

linkedList 是一个双向链表,没有初始化大小,也没有扩容的机制

HashMap 初始化大小是 16 ,扩容因子默认0.75(可以指定初始化大小,和扩容因子)
扩容机制.(当前大小 和 当前容量 的比例超过了 扩容因子,就会扩容,扩容后大小为 一倍。例如:初始大小为 16 ,扩容因子 0.75 ,当容量为12的时候,比例已经是0.75 。触发扩容,扩容后的大小为 32.)

1.6.  ArrayList 与 Vector 区别  ;

Vector 是线程安全的,也就是说是它的方法之间是线程同步的,而 ArrayList 是线程
  序不安全
ArrayList 与 Vector 都有一个初始的容量大小,当存储进它们里面的元素的个数超过
  了容量,就需要增加 ArrayList 与 Vector 的存储空间。Vector 增长原来的一倍, ArrayList 增加原来的0.5倍。

1.7. HashSet 和 HashMap 区别  ;

HashMap实现Map接口,存储键值对,HashMap使用键(Key)计算Hashcode

HashSet实现Set接口,底层采用HashMap实现,仅存储对象,HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false。

1.8. HashMap 和 Hashtable 的区别  ;

HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。HashMap中,null可以作为键,这样的键只有一个;


Hashtable也是JDK1.0引入的类,是线程安全的,Hashtable中,key和value都不允许出现null值。
遍历方式: Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。


哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值


扩容机制和初始化大小: HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。

1.9.  谈谈HashMap,哈希表解决hash冲突的方法;

JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash  值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的时数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间,提高查询速度

1.10. HashMap 和 ConcurrentHashMap 的区别  ;

从ConcurrentHashMap代码中可以看出,它引入了一个“分段锁”的概念,具体可以理解为把一个大的Map拆分成N个小的HashTable,根据key.hashCode()来决定把key放到哪个HashTable中。可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。
 

1.11. ConcurrentHashMap 的工作原理及代码实现,如何统计所有的元素个数  

 这也就是为什么sumCount()中需要遍历counterCells数组,sum累加CounterCell.value值了
  (16) Java Collections和Arrays的sort方法默认的排序方法是什么; 

 java的Collections.sort算法调用的是合并排序
-Arrays.sort() 采用了2种排序算法 -- 基本类型数据使用快速排序法,对象数组使用归并排序

1.12. ArrayList和LinkList的删除一个元素的时间复杂度;(ArrayList是O(N),LinkList是O(1));

ArrayList 是线性表(数组)
   get() 直接读取第几个下标,复杂度 O(1)
  add(E) 添加元素,直接在后面添加,复杂度O(1)
  add(index, E) 添加元素,在第几个元素后面插入,后面的元素需要向后移动,复杂度O(n)
  remove()删除元素,后面的元素需要逐个移动,复杂度O(n)

LinkedList 是链表的操作
  get() 获取第几个元素,依次遍历,复杂度O(n)
  add(E) 添加到末尾,复杂度O(1)
  add(index, E) 添加第几个元素后,需要先查找到第几个元素,直接指针指向操作,复杂度O(n)
  remove()删除元素,直接指针指向操作,复杂度O(1)

1.13.  HashMap在什么时候时间复杂度是O(1),什么时候是O(n),什么时候又是O(logn); 

链表的长度尽可能短,理想状态下链表长度都为1 
- 当 Hash 冲突严重时,如果没有红黑树,那么在桶上形成的链表会变的越来越长,这样在查询时的效率就会越来越低;时间复杂度为O(N)。 
- 采用红黑树之后可以保证查询效率O(logn)

1.14. HashMap 底层实现原理

HashMap的底层是数组+链表,(很多人应该都知道了)
JDK1.7的是数组+链表
1.7只是一个例子,以前的话也是这样后面就以1.7为例子了)
首先是一个数组,然后数组的类型是链表
元素是头插法


JDK1.8的是数组+链表 或者 数组+红黑树
首先是一个数组,然后数组的类型是链表
在链表的元素大于8的时候,会变成红黑树
在红黑树的元素小于6的时候会变成链表
元素进行尾插

HaspMap的数组默认大小为16,加载因子 0.75
数组也叫做Hash桶;

 

2. JVM

2.1. JVM 内存模型

JVM包括类加载子系统、堆、方法区、栈、本地方法栈、程序计数器、直接内存、垃圾回收器、执行引擎。

1、类加载子系统

类加载子系统负责加载class信息,加载的类信息存放于方法区中。

2、直接内存

直接内存是在Java堆外的、直接向系统申请的内存空间。访问直接内存的速度会由于Java堆。出于性能的考虑,读写频繁的场合可能会考虑使用直接内存。

3、垃圾回收器

垃圾回收器可以对堆、方法区、直接内存进行回收。

4、执行引擎

执行引擎负责执行虚拟机的字节码,虚拟机会使用即时编译技术将方法编译成机器码后再执行。
 

运行时数据区包括堆、方法区、栈、本地方法栈、程序计数器。

1、堆

堆解决的是对象实例存储的问题,垃圾回收器管理的主要区域。

2、方法区

方法区可以认为是堆的一部分,用于存储已被虚拟机加载的信息,常量、静态变量、即时编译器编译后的代码。

3、栈

栈解决的是程序运行的问题,栈里面存的是栈帧,栈帧里面存的是局部变量表、操作数栈、动态链接、方法出口等信息。

(1)栈帧

每个方法从调用到执行的过程就是一个栈帧在虚拟机栈中入栈到出栈的过程。

(2)局部变量表

用于保存函数的参数和局部变量。

(3)操作数栈

操作数栈又称操作栈,大多数指令都是从这里弹出数据,执行运算,然后把结果压回操作数栈。

4、本地方法栈

与栈功能相同,本地方法栈执行的是本地方法,一个Java调用非Java代码的接口。

5、程序计数器(PC寄存器)

程序计数器中存放的是当前线程所执行的字节码的行数。JVM工作时就是通过改变这个计数器的值来选取下一个需要执行的字节码指令。
 

2.2. 堆分为哪几块,比如说新生代老生代,那么新生代又分为什么两个Survivor;

整个堆区划分为新生代和老年代;新生代又被划分成 Eden 空间、 From Survivor 和 To Survivor 三块区域。
Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。


首先说如果没有Survivor区会出现什么情况:此时每触发一次Minor GC,就会把Eden区的对象复制到老年代,这样当老年代满了之后会触发Major Gc(通常伴随着MinorGC,可以看做Full GC),比较耗时。


如果只有1个Survivor区,那当Eden区满了之后,就会复制对象到Survivor区,容易产生内存碎片化。严重影响性能。所以使用2个Survivor区,始终保持有一个空的Survivor区,可以避免内存碎片化。

2.3. 如何判断对象是否可以回收或存活(引用计数法与GC Root可达性分析法区别;

引用数法:引用计数法师垃圾收集的早期策略,在这中方法中,堆中每个对象都有一个引用计数,每当有一个地方引用他时,引用计数值就+1,当引用失效时,引用计数值就-1,任何时刻引用计数值为0的对象就是可以被回收,当一个对象被垃圾收集时,被它引用 的对象引用计数值就-1,所以在这种方法中一个对象被垃圾收集会导致后续其他对象的垃圾收集行动。


缺点:不完全准确,当两个对象相互引用的时候就无法回收,导致内存泄漏。


可达性分析算法:基本思路就是通过一系列名为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,下图对象object5, object6, object7虽然有互相判断,但它们到GC Roots是不可达的,所以它们将会判定为是可回收对象。


  可作为GC Roots对象的包括如下几种:
    >a.虚拟机栈(栈桢中的本地变量表)中的引用的对象  
    >  b.方法区中的类静态属性引用的对象  
    >  c.方法区中的常量引用的对象   
    >  d.本地方法栈中JNI的引用的对象   

2.4. 常见的GC回收算法及其含义  ;

 标记-清除算法:
         标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象;
          清除阶段:清除所有未被标记的对象。
          缺点:标记和清除过程效率不高,标记清除之后会产生大量不连续的内存碎片。
复制算法:(新生代的GC)
          将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象。
          缺点:内存缩小为原来的一半。
标记-整理算法:(老年代的GC)
          标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象
          整理阶段:将将所有的存活对象压缩到内存的一端;之后,清理边界外所有的空间  
          缺点:在标记-清除的基础上还需进行对象的移动,成本相对较高,好处则是不会产生内存碎片。
分代收集算法:
          存活率低:少量对象存活,适合复制算法:在新生代中,每次GC时都发现有大批对象死去,只有少量存活(新生代中98%的对象都是“朝生夕死”),那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成GC。
          存活率高:大量对象存活,适合用标记-清理/标记-整理:在老年代中,因为对象存活率高、没有额外空间对他进行分配担保,就必须使用“标记-清理”/“标记-整理”算法进行GC。

2.5. 类加载的过程;

加载、验证、准备、解析、初始化  

2.6. 什么情况下会触发类加载;

创建类的实例  
    访问类的静态变量(除常量【被final修辞的静态变量】原因:常量一种特殊的变量,因为编译器把他们当作值(value)而不是域(field)来对待。如果你的代码中用到了常变量(constant variable),编译器并不会生成字节码来从对象中载入域的值,而是直接把这个值插入到字节码中。这是一种很有用的优化,但是如果你需要改变final域的值那么每一块用到那个域的代码都需要重新编译。  
    访问类的静态方法  
    反射如(Class.forName("my.xyz.Test"))  
    当初始化一个类时,发现其父类还未初始化,则先出发父类的初始化  
    虚拟机启动时,定义了main()方法的那个类先初始化 

2.7. 什么是双亲委派模型?有哪些类加载器?

当需要加载一个类的时候,子类加载器并不会马上去加载,而是依次去请求父类加载器加载,一直往上请求到最高类加载器:启动类加载器。当启动类加载器加载不了的时候,依次往下让子类加载器进行加载。当达到最底下的时候,如果还是加载不到该类,就会出现ClassNotFound的情况

(1)启动类加载器(Bootstrap ClassLoader)
这个类加载器负责将存放在JAVA_HOME/lib下的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。
(2)扩展类加载器(Extension ClassLoader)
这个加载器负责加载JAVA_HOME/lib/ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器
(3)应用程序类加载器(Application ClassLoader)
这个加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(Classpath)上所指定的类库,可直接使用这个加载器,如果应用程序没有自定义自己的类加载器,一般情况下这个就是程序中默认的类加载器
 

2.8.  JVM怎么调优?

  • 内存占用:程序正常运行需要的内存大小。

  • 延迟:由于垃圾收集而引起的程序停顿时间。

  • 吞吐量:用户程序运行时间占用户程序和垃圾收集占用总时间的比值。

3. 多线程 

3.1.  创建线程的方式;

继承Thread
- 实现Runnable接口
- 实现Callable接口
- 区别:
 1. 实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
    2. Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;
    3. Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!
 

3.2. 一个线程连着调用start两次会出现什么情况?

线程是不允许启动两次的,第二次调用必然会抛出IllegalThreadStateException,这是一种运行时异常,多次调用start被认为是编程错误。

3.3. wait方法能不能被重写,wait能不能被中断;

wait时间到或被中断唤醒,不一定会继续执行或者跳到catch里,而是还需要等待获得锁。
  如果wait时间到或被中断唤醒,而T2还在syn里,那么T1还是会等待。

- wait、notify和notifyAll方法是Object类的final native方法。所以这些方法不能被子类重写,Object类是所有类的超类,这些方法都只能在同步方法或同步块中调用
- sleep方法不同的是wait方法调用完成后,线程将被暂停,但wait方法将会释放当前持有的监视器锁(monitor),直到有线程调用notify/notifyAll方法后方能继续执行,而sleep方法只让线程休眠并不释放锁。同时notify/notifyAll方法调用后,并不会马上释放监视器锁,而是在相应的synchronized(){}/synchronized方法执行结束后才自动释放锁。

3.4. 线程池的实现原理?四种线程池?重要参数及原理?任务拒接策略有哪几种? 

线程池内部维护了一个线程列表,我们使用线程池只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行.jdk1.5引入Executor线程池框架Executor线程池框架,有以下四种实现
- newFixedThreadPool():初始化一个指定线程数的线程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene作为阻塞队列。
    >特点:即使当线程池没有可执行任务时,也不会释放线程。
- newCachedThreadPool():初始化一个可以缓存线程的线程池,默认缓存60s,线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;
    >特点:在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源;当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;
- newSingleThreadExecutor():初始化只有一个线程的线程池,内部使用LinkedBlockingQueue作为阻塞队列。
    >如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行
- newScheduledThreadPool():初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。
- 提交任务给线程池
     - Executor.execute(Runnable command);
    -  ExecutorService.submit(Callable<T> task);
- 线程池的关闭
    - shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
    - shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
    
>总结:线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果阻塞队列满了,那就创建新的线程执行当前任务;直到线程池中的线程数达到maxPoolSize,这时再有任务来,只能执行reject()处理该任务;

(10) 说说 CountDownLatch、CyclicBarrier 原理和区别

- CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行, await()可以让线程等待,countDown() 每调用一次就减1.指定减到0所有等待的线程就开始执行。
- CyclicBarrier是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。
当所有线程都准备好再一起执行。

(13) 多个线程同时读写,读线程的数量远远⼤于写线程,你认为应该如何解决并发的问题?你会选择加什么样的锁?  

采用Read-Write Lock Pattern是一种将对于共享资源的访问与修改操作分离,称为读写分离。用单独的线程来处理读写,允许多个读线程同时读,但是不能多个写线程同时写。ReadWriteLock锁

  前置处理(获取锁定)

    try{

         实际操作
    }finally{
        后续处理(释放锁)

3.5.  wait/notify/notifyAll⽅法需不需要被包含在synchronized块中?这是为什么?  

需要
- 因为wait/notify/notifyAll需要通过监视器操作对象锁。达到对对象锁得释放和获取

(17) ExecutorService你一般是怎么⽤的?是每个Service放一个还是个项目放一个?有什么好处?

- Java线程池ExecutorService
  如果有一套相同逻辑的多个任务的情况下,应用一个线程池是个好选择。
  如果项目中有多套不同的这种任务,那每套任务应该一个线程池不是很正常的吗

3.6. 两个线程设计题。记得一个是:t1,t2,t3,让t1,t2执行完才执行t3,原生实现。

使用线程的join() 方法控制

3.7. synchronized 与 lock 的区别 

首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
- synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
- synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
- 用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
- synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
- Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

3.8. 产生死锁的四个条件;

互斥、请求与保持、不剥夺、循环等待

3.9.  CAS无锁的概念、乐观锁和悲观锁  ;

CAS(比较与交换,Compare and swap) 是一种有名的无锁算法,无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)


- 当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。 

3.10. Java中有哪些同步方案;

重量级锁、显式锁、并发容器、并发同步器、CAS、volatile、AQS等

3.11. 怎么保证多线程情况下,同一方法同时只被一个线程调用

添加synchronized

4.  设计模式

4.1. 常见的设计模式

软件设计模式的分类
    - 创建型:创建对象时,不再由我们直接实例化对象,工厂方法、抽象工厂模式、单例模式
    - 结构型:用于帮助将多个对象组织成更大的结构,适配器模式,装饰器模式、门面模式、亨元模式和代理模式
    - 行为型:用于帮助系统间各对象的通信,以及如何控制复杂系统中流程,命令模式、观察者模式、策略模式

4.2. 设计模式的的六大原则及其含义  

开闭原则(Open Close Principle)
    - 开闭原则的意思是:对扩展开放,对修改关闭。
    - 对原有代码不修改,可以进行拓展,实现热插拔。
- 里氏代换原则(Liskov Substitution Principle)
    - 里氏代还原则是面向对象设计的基本原则之一。原则:基类存在的的地方,子类一定存在。
- 依赖倒转原则(Dependence Inversion Principle)
    - 这个原则是开闭原则的基础,针对接口编程,依赖于抽象而不依赖于具体。
- 接口隔离原则(Interface Segregation Principle)
    - 使用多个隔离的接口,降低类之间的耦合度。便于升级和维护,降低依赖,降低耦合。
- 迪米特法则,又称最少知道原则(Demeter Principle)
    - 一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
- 合成复用原则(Composite Reuse Principle)
    - 尽量使用合成、聚合的方式,而不是用继承。

4.3. 常见的单例模式以及各种实现方式的优缺点,哪一种最好,手写常见的单利模式  

懒汉式单例
    - 优点:第一次调用才初始化,避免内存浪费。
    - 缺点:必须加锁synchronized 才能保证单例,(如果两个线程同时调用getInstance方法,会chuxia)但加锁会影响效率。 

饿汉式单例
    - 优点:没有加锁,执行效率会提高。
    - 缺点:类加载时就初始化,浪费内存。

登记式模式(holder)

        - 内部类只有在外部类被调用才加载,产生SINGLETON实例;又不用加锁。此模式有上述两个模式的优点,屏蔽了它们的缺点,是最好的单例模式。

4.4. JDK中哪些实现了单例模式?  

java.lang.Runtime  java.awt.Desktop#getDesktop()

4.5. 设计模式在实际场景中的应用

装饰者模式+增强HttpServletRequest对象
- IO编程中使用大量装饰者模式
- 集合中使用了大量组合模式
- JDBC编程使用桥接模式

4.6. Spring中用到了哪些设计模式 

- 代理模式:在AOP和remoting中被用的比较多。
- 单例模式:在spring配置文件中定义的bean默认为单例模式。
- 模板方法模式:用来解决代码重复的问题。
- 前端控制器模式:Spring提供了DispatcherServlet来对请求进行分发。
- 依赖注入模式:贯穿于BeanFactory / ApplicationContext接口的核心理念。
- 工厂模式:BeanFactory用来创建对象的实例。

5. 数据库

5.1. 存储引擎的 InnoDB与MyISAM区别,优缺点,使用场景  

主要区别:

    1).MyISAM是非事务安全型的,而InnoDB是事务安全型的。  
    2).MyISAM锁的粒度是表级,而InnoDB支持行级锁定。  
    3).MyISAM支持全文类型索引,而InnoDB不支持全文索引。  
    4).MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM。  
    5).MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦。  
    6).InnoDB表比MyISAM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表(alter table tablename type=innodb)。

- 应用场景:  
    1).MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。  
    2).InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT或UPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能

5.2. 为什么要用 B+tree作为MySQL索引的数据结构 

文件很大,不可能全部存储在内存中,故要存储到磁盘上
- 索引的结构组织要尽量减少查找过程中磁盘I/O的存取次数(为什么使用B-/+Tree,还跟磁盘存取原理有关。)
- 局部性原理与磁盘预读,预读的长度一般为页(page)的整倍数,(在许多操作系统中,页得大小通常为4k)
- 数据库系统巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入,(由于节点中有两个数组,所以地址连续)。而红黑树这种结构,h明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性

5.3. 遇到过索引失效的情况没,什么时候可能会出现,如何解决  

如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)
注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
对于多列索引,不是使用的第一部分,则不会使用索引 
如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引

5.4. 数据库事物ACID

- 原子性:原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生
- 一致性:一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
- 隔离性:多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果
- 持久性:持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。
 

5.5. 事物的隔离级别(读未提交、读以提交、可重复读、可序列化读)  

- Read uncommitted 读未提交:事务B读取了事务A尚未提交的数据,可能出现脏读
- Read committed 读提交:事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变,避免了脏读,但是可能会造成不可重复读
- Repeatable read 重复读: ,避免了不可重复读,但还有可能出现幻读。注:MySQL的默认隔离级别就是Repeatable read。
- Serializable 序列化:Serializable是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读。
 

 5.6. 如何选择合适的分布式主键方案 

- redis的自增
- oracle数据库的序列
- 手动UUID
- 推特雪花算法 
 

5.7. 数据库高并发下的优化思路;  

- 代码中sql语句优化
- 数据库字段优化,索引优化
- 加缓存,redis/memcache等
- 主从,读写分离
- 分区表
- 垂直拆分,解耦模块
- 水平切分

5.8. 数据库三范式

1、列不可再分;

2、每一行数据只做一件事,只与一列相关,主键;

3、每个属性都与主键有直接关系,而不是间接关系;

5.9. 聚集索引和非聚集索引有何区别?

聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个
聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续
聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序。
非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序。
索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。
 

5.10. 怎么查看sql使用索引情况?

我们使用explain关键词就可以查看sql语句是否使用索引,也提高我们sql优化

5.11.  Explain内容中type有几种类型?各代表什么意思?

ALL  --全表扫描,查找匹配的行,(这个是最慢的)

INDEX--  使用索引  where 条件中没有用到索引,但是用索引就能完成扫描全表; 速度有提升;

RANGE ----使用索引范围扫描,索引范围扫描,常见于<、<=、>、>=、between等操作符; 

REF----使用非唯一性索引或者唯一索引的前缀扫描,返回匹配某个单独值的记录行; 使用了前缀索引

eq_ref--- 相对于ref来说就是使用的是唯一索引,对于每个索引键值,只有唯一的一条匹配记录; 一般用于联表查询;

CONST or SYSTEM --- 这个是通过索引很快技能定位到一行匹配的数据,就像一个常量一行(很快);

                             单表中最多只有一条匹配行,查询起来非常迅速,

                             select * from table_name where tableI_id =' '

                             table_id 为主键索引,或则唯一索引,

NULL --- 这个是sql 语句是不经过表就能返回结果的 

           select  1 from dual;  不通过表就能查询到数据。


5.12. 说说数据库sql的优化

        查询SQL尽量不要使用select *,而是具体字段
        避免在where子句中使用or来连接条件
        使用varchar代替char
        尽量使用数值替代字符串类型
        查询尽量避免返回大量数据
        使用explain分析你SQL执行计划
        是否使用了索引及其扫描类型
        创建name字段的索引
        优化like语句
        字符串怪现象
        索引不宜太多,一般5个以内
        索引不适合建在有大量重复数据的字段上
        where限定查询的数据
        避免在索引列上使用内置函数
        避免在where中对字段进行表达式操作
        避免在where子句中使用!=或<>操作符
        去重distinct过滤字段要少
        where中使用默认值代替null

6. WEB

6.1. cookie和session的区别  

- cookie数据保存在客户端,session数据保存在服务端。
- cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,相当重要的数据,应该使用session保存到服务端。
- session会在一定时间内保持在服务器上,但是会占用内存资源,当访问的用户过多,会加重服务器的负载,考虑到减轻服务器的压力,可以将不重要的数据放在cookie中持久的保存。
- 单个cookie保存的数据不能超过4k,很多浏览器都限制站点最多保存20个cookie。
- session与浏览器得会话默认会以cookie实现

6.2. http协议简单协议

HTTP(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于TCP的连接方式。http请求由三部分组成,分别是:请求行、消息报头、请求正文,响应也包含三部分:状态行(包含HTTP版本、状态码、状态码的原因短语)、响应首部字段、响应内容实体
 

6.3. Get和Post请求方式的区别?

- get地址栏有参数显示  post不会再地址栏显示参数(参数是放在了请求体)
- get不安全  post相对安全
- get限制大小  post理论上不限制

6.4. JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?  

JSP是Servlet技术的扩展,本质上就是Servlet的简易方式。JSP编译后是“类servlet”。Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet主要用于控制逻辑

6.5. jsp九大内置对象  

- request:负责得到客户端请求的信息,对应类型:javax.servlet.http.HttpServletRequest
- response:负责向客户端发出响应,对应类型:javax.servlet.http.HttpServletResponse
- session:负责保存同一客户端一次会话过程中的一些信息,对应类型:javax.servlet.http.httpsession
- out:负责管理对客户端的输出,对应类型:javax.serlvet.jsp.jspwriter
- application:表示整个应用环境的信息,对应类型:javax.servlet.servletcontext
- config:表示ServletConfig,对应类型:javax.servlet.servletconfig
- exception:表示页面中发生的异常,可以通过它获得页面异常信息,对应类型:java.lang.exception
- pagecontext:表示这个JSP页面上下文,对应类型:javax.servlet.jsp.pagecontext
- page:表示当前JSP页面本身

6.6. jsp四大作用域  

- page是代表一个页面相关的对象和属性。
- request一个请求可能跨越多个页面。
- session一个web回话也可以经常跨域多个请求。
- application是代表与整个web应用程序相关的对象和属性。

6.7. servlet的生命周期  

- 第一次请求的时候servlet被初始化,而且只初始化一次,所以tomcat容器中每一个servlet只有一个对象存在
- 初始化后先调用init方法,只执行一遍
- 每个请求,调用一遍service -> service -> doGet/doPost。以多线程的方式运行
- 卸载前调用destroy方法

6.8. 重定向和转发的区别?  

- 重定向是客户端行为,转发是服务器端行为
- 重定向两次请求两次响应,转发一次请求一次响应
- 重定向路径需要加工程名,转发的路径不需要加工程名.
- 重定向可以跳转到任意网站,转发只能在服务器内部进行转发.
- 重定向会导致request对象信息丢失。转发则不会

6.9. 如何防止表单重复提交?  


- JavaScript控制 
- 使用session实现令牌机制

6.10. 过滤器有哪些作用,以及过滤器的生命周期  

- 生命周期:每个Filter在tomcat启动时进行初始化,每个Filter只有一个实例对象
  (1). Init:在服务器启动时会创建Filter实例  
  (2). doFilto:这个方法会在用户每次访问“目标资源”时执行  
  (3). destroy():服务器关闭时销毁Filter对象  
- 作用:  
  (1). 验证客户是否来自可信网络  
  (2). 对客户提交的数据进行重新编码  
  (3). 过滤掉客户的某些不应该出现的词汇  
  (4). 验证用户是否可以登录  
  (5). 验证客户的浏览器是否支持当前的应用  
  (6). 记录系统日志 
 

6.11. session 共享怎么做的(分布式如何实现 session 共享)

(1) 可以使用tomcat广播机制实现session共享
(2) 可以使用redis+tomcat实现session共享
(3) 可以使用Spring Session完成session共享

6.12. Servlet是安全的吗?

- 是线程不安全的,因为servlet是单例模式,当多个客户端共同访问的时候线程不安全。
尽量用局部变量,同步块,如果当前字段是不会改变的,用final修饰

7. Spring

7.1. BeanFactory 和 ApplicationContext 有什么区别 

- BeanFactory和ApplicationContext都是接口,并且ApplicationContext是BeanFactory的子接口。

- BeanFactory是Spring中最底层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。而ApplicationContext是Spring的一个更高级的容器,提供了更多的有用的功能。

- ApplicationContext提供的额外的功能:国际化的功能、消息发送、响应机制、统一加载资源的功能、强大的事件机制、对Web应用的支持等等。

- 加载方式的区别:BeanFactory采用的是延迟加载的形式来注入Bean;ApplicationContext则相反的,它是在Ioc启动时就一次性创建所有的Bean,好处是可以马上发现Spring配置文件中的错误,坏处是造成浪费。

7.2. Spring Bean 的生命周期

- spring对bean进行实例化,默认bean是单例  
- spring对bean进行依赖注入  
- 如果bean实现了BeanNameAware接口,spring将bean的id传给setBeanName()方法  
- 如果bean实现了BeanFactoryAware接口,spring将调用setBeanFactory方法,将BeanFactory实例传进来  
- 如果bean实现了ApplicationContextAware()接口,spring将调用setApplicationContext()方法将应用上下文的引用传入  
- 如果bean实现了BeanPostProcessor接口,spring将调用它们的  postProcessBeforeInitialization接口方法  
- 如果bean实现了InitializingBean接口,spring将调用它们的afterPropertiesSet接口方法,类似的如果bean使用了init-method属性声明了初始化方法,改方法也会被调用  
- 如果bean实现了BeanPostProcessor接口,spring将调用它们的  postProcessAfterInitialization接口方法  
- 此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁  
- 若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用  

7.3. 说说 Spring AOP、Spring AOP 实现原理 

- 概念:
    - 切面(Aspect) :官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”。
    - 连接点(Joinpoint) :程序执行过程中的某一行为。
    - 通知(Advice) :“切面”对于某个“连接点”所产生的动作。
    - 切入点(Pointcut) :匹配连接点的断言,在AOP中通知和一个切入点表达式关联。
    - 目标对象(Target Object) :被一个或者多个切面所通知的对象。
    - AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。
- 通知(Advice)类型:
    - 前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。
    - 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。
    - 返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
    - 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。
    - 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。 ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。

    - 切入点表达式 :如execution(* com.spring.service.*.*(..))
- 特点
    - 降低模块之间的耦合度
    - 使系统容易扩展
    - 更好的代码复用。

7.4. Spring 事务实现方式、事务的传播机制、默认的事务类别  

pring事物配置的五种方式:
    - 每个Bean都有一个代理
    - 所有Bean共享一个代理基类
    - 使用拦截器
    - 使用tx标签配置的拦截器
    - 全注解
- spring里面事务的传播属性和事务隔离级别  
    - Propagation :key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:  
    - PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。  
    - PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。  
    - PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。  
    - PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。  
    - PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。  
    - PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。

7.5. Spring注入有那些方式?  

- Set注入
- 构造器注入
- 静态工厂的方法注入
- 实例工厂的方法注入

7.6. 简述Spring的优缺点? 

- Spring 的优点??
  - 降低了组件之间的耦合性 ,实现了软件各层之间的解耦
  - 可以使用容易提供的众多服务,如事务管理,消息服务等
  - 容器提供单例模式支持
  - 容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
  - 容器提供了众多的辅助类,能加快应用的开发
  - spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等
  - spring属于低侵入式设计,代码的污染极低
  - 独立于各种应用服务器
  - spring的DI机制降低了业务对象替换的复杂性
  - Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可以自由选择spring的部分或全部
- 缺点:
  - 使用到了大量反射机制。反射机制非常占内存,

7.7. Mybatis 的工作机制

在mybatis中,一个SqlSession实例代表着一次数据库连接,SqlSession由SqlSessionFactory来获取。在整个应用运行过程,我们只需要一个SqlSessionFactory实例,但是可以有多个SqlSession实例。 SqlSession是一个会话,类似于jdbc的connection,SqlSession是线程不安全的,所以我们最好在方法级别使用SqlSession,并且在使用完毕及时关闭连接,避免资源的浪费。
 

8. SpringBoot   

8.1. 什么是springboot 


- 用来简化spring应用的初始搭建以及开发过程 使用特定的方式来进行配置(properties或yml文件) 
- 创建独立的spring引用程序 main方法运行
- 嵌入的Tomcat 无需部署war文件
- 简化maven配置
- 自动配置spring添加对应功能starter自动化配置

8.2. springboot常用的starter有哪些 

- spring-boot-starter-web 嵌入tomcat和web开发需要servlet与jsp支持
- spring-boot-starter-data-jpa 数据库支持
- spring-boot-starter-data-redis redis数据库支持
- spring-boot-starter-data-solr solr支持
- mybatis-spring-boot-starter 第三方的mybatis集成starter

8.3. 你如何理解 Spring Boot 中的 Starters?

Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成 Spring 及其他技术,而不需要到处找示例代码和依赖包。

8.4. springboot自动配置的原理

- 在spring程序main方法中 添加@SpringBootApplication或者@EnableAutoConfiguration会自动去maven中读取每个starter中的spring.factories文件  该文件里配置了所有需要被创建spring容器中的bean

8.5.  springboot读取配置文件的方式

- springboot默认读取配置文件为application.properties或者是application.yml. 最先会读取一个bootstrap.yml的文件

8.6. springboot集成mybatis的过程

-  添加mybatis的starter maven依赖
-  在mybatis的接口中 添加@Mapper注解
-  在application.yml或者application.properties配置数据源信息

8.7. 为什么要用 Spring Boot?

-  独立运行
-  简化配置
-  自动配置
-  无代码生成和XML配置
-  应用监控
-  上手容易

8.8. Spring Boot 的核心配置文件有哪几个?它们的区别是什么?  

- Spring Boot 的核心配置文件是 application 和 bootstrap 配置文件。application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。bootstrap 配置文件有以下几个应用场景。
- 使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;一些固定的不能被覆盖的属性;一些加密/解密的场景;

8.9. Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?  

启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:  

- @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能。
- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
- @ComponentScan:Spring组件扫描。

8.10. 运行 Spring Boot 有哪几种方式?

- 打包用命令或者放到容器中运行
- 用 Maven/ Gradle 插件运行
- 直接执行 main 方法运行
 

8.11. Spring Boot 2.X 有什么新特性?与 1.X 有什么区别?

- 配置变更
- JDK 版本升级
- 第三方类库升级
- 响应式 Spring 编程支持
- HTTP/2 支持
- 配置属性绑定
- 更多改进与加强

8.12. Spring Boot 有哪几种读取配置的方式?

Spring Boot 可以通过 @PropertySource,@Value,@Environment, @ConfigurationProperties 来绑定变量

8.13. SpringBoot 实现热部署有哪几种方式?

- Spring Loaded
- Spring-boot-devtools  

8.14.springboot的事务是如何实现的?

 @EnableTransactionManagement

8.15. springboot的事物注解什么时候失效?如何解决

1、检查数据库的引擎是否是innoDB

2、启动类上是否加入@EnableTransactionManagement注解(这个并不是必须的,springboot默认开启)

3、是否在方法上加入@Transactional注解或Service的类上是否有@Transactional注解

4、方法是否为public修饰的

5、是否是因为抛出了Exception等Checked检查异常,默认情况下,spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。如果想回滚Excepton异常可以在注解上添加@Transactional(rollbackFor = Exception.class)这样添加就可以了

6、在一个类中的方法调用时,被调用的方法如果有事务管理,那么事务管理会失效。

7、是否添加了@Async异步注解

8、事务方法的类是否被代理,可用AopUtils.isAopProxy()查看
 

在Realm中自动装配的Service加上 @Lazy便可以解决了

9. SpringCloud

9.1. Spring Cloud 你们在项目中如何使用?

项目中基础的Spring Cloud架构是有架构师搭建好的,我们主要安装代码规范开发微服务,并且把微服务注册到Eurake 注册中心,然后在需要调用微服务的客户端使用Feign的注解@FeignClient绑定接口来调用,同时
在网关项目中配置自己编写的服务地址

9.2. Spring Cloud 有哪些组件

 Eurake: SpringCloud的服务发现和注册的组件
    - 注册中心 : Eureka server
        - 1) 引入SpringBoot依赖,Eureka server依赖
        - 2)编写启动类,添加注解@EnableEurekaServer
        - 3)在application.yml中配置注册中心相关信息和指定注册中心的地址
    - 生产者:  Eureka Client
        - 1) 引入SpringBoot依赖,Eureka client依赖
        - 2) 编写启动类,添加注解@EnableEurekaClient
        - 3)在application.yml中配置相关信息同时指定服务注册中心的地址
        - 4) 编写微服务(controller+service+dao)
    - 消费者:  Eureka Client
        - 1) 引入SpringBoot依赖,Eureka client依赖
            - ribbon依赖 
            - feign依赖
        - 2) 编写启动类,添加注解@EnableEurekaClient
            - @EnableDiscoveryClient : 服务发现的注解
            - @EnableFeignClients  : 开启feign的注解
        - 3)在application.yml中配置相关信息同时指定服务注册中心的地址
        - 4) 编写微服务访问接口同时使用@FeignClient 指定该接口访问哪一个微服务,然后指定方法访问的url地址
    
- Ribbon:负载均衡客户端,需要结合RestTemplate进行服务的调用
    - 默认的负载均衡:轮询    
    - 服务器启动时,先从Eureka server获取服务列表,然后
    在请求微服务时,通过RestTemplate进行http调用

- Feign:Feign默认集成了Ribbon
    - Feign可以通过@FeignClient 注解标识一个接口,通过该接口生成一个代理类来进行远程的微服务调用

- Hystrix : 熔断限流的组件
    - 为了防止微服务直接调用时,由于某一个微服务宕机导致整个无法雪崩,这里采用Hystrix来阻断对存在宕机,异常情况的请求,直接本地返回
    - 1)Ribbon集成,首先引入相关的依赖,接着开启@EnableHystrix
    接着在调用微服务方法上添加 @HystrixCommand(fallbackMethod = "hiError")指定服务异常的之后本地执行的方法
    
    - 2)Feign集成,在微服务绑定的接口@FeignClient(value = "service-hi",fallback = SchedualServiceHiHystric.class)中指定异常时调用本地接口实现

    - 判定失败:Hystrix会在某个服务连续调用N次不响应的情况下,立即通知调用端调用失败,避免调用端持续等待而影响了整体服务,执行本地业务。
    - 恢复服务:Hystrix间隔时间会再次检查此服务,如果服务恢复将继续提供服务。Hystrix间隔几秒会让其中一个请求去调用远程微服务,如果调用成功,就表示服务正常,后面就重新链接

    - 开路 断路

- Spring Cloud Config : 集中管理中心- 特点:(1)集中管理配置文件,可以将配置文件存在本地或者用git仓库存储 (2)配置文件自动刷新,不用重启client
    - 在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client
    - Server提供配置文件的存储、以接口的形式将配置文件的内容提供出去
    - Client通过接口获取数据、并依据此数据初始化自己的应用。

- Zull (网关):Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能。
    - 1) 引入相关的依赖, Eureka client和zuul的依赖
    - 2) 启动类添加
        @EnableZuulProxy  -- 开启网关
        @EnableEurekaClient -- 表示时一个eurake客户端
        @EnableDiscoveryClient -- 开启服务发现
   - 3) 编写application.yml配置注册中心地址,
         - 配置zuul转发路径和微服务的对应关系
         
    - 4)编写过滤器拦截请求,完成相关权限的校验
    

 9.3. springcloud如何实现服务的注册和发现

- 服务在发布时 指定对应的服务名(服务名包括了IP地址和端口) 将服务注册到注册中心(eureka或者zookeeper)
- 这一过程是springcloud自动实现 只需要在main方法添加@EnableDisscoveryClient  同一个服务修改端口就可以启动多个实例
- 调用方法:传递服务名称通过注册中心获取所有的可用实例 通过负载均衡策略调用(ribbon和feign)对应的服务

9.4. ribbon和feign区别  

Ribbon添加maven依赖 spring-starter-ribbon 使用@RibbonClient(value="服务名称") 使用RestTemplate调用远程服务对应的方法
- feign添加maven依赖 spring-starter-feign 服务提供方提供对外接口 调用方使用 在接口上使用@FeignClient("指定服务名")

- Ribbon和Feign的区别: Ribbon和Feign都是用于调用其他服务的,不过方式不同。
  - 启动类使用的注解不同,Ribbon用的是@RibbonClient,Feign用的是@EnableFeignClients。
  - 服务的指定位置不同,Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。
  - 调用方式不同,Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建http请求。不过要注意的是抽象方法的注解、方法签名要和提供服务的方法完全一致。  
 

9.5. springcloud和dubbo的区别

- 最大的区别:Spring Cloud抛弃了Dubbo 的RPC通信,采用的是基于HTTP的REST方式。Dubbo目前也已经支持REST风格的服务,但是在项目中使用dubbo还有很多问题需要解决,比如熔断机制,集中配置等需要自行使用其他技术,但是Spring Cloud提供一套微服务架构比较完善的解决方案。所以目前更多的企业使用Spring Cloud

9.6. 分布式锁

基于数据库实现分布式锁; 
基于缓存(Redis等)实现分布式锁; 
基于Zookeeper实现分布式锁;

10. MQ 消息队列

10.1.延迟消息怎么实现

使用Redis的Sorted Set结构。
使用RabbitMQ的rabbitmq delayed message exchange插件。
使用ActiveMQ的5.4及以上版本的延迟消息功能。
使用RocketMQ仅支持特定级别的延迟消息。
定制RocketMQ,以重新计算延迟级别的方式实现自定义延时。
 

10.2. 怎么保证不重复消费

 要保证消息不被重复消费,需要保证消息消费时的幂等性,保证了幂等性,重复消费了也不会造成系统异常。

幂等性,通俗的说,无论你重复请求多少次,你得确保对应的数据是不会改变的。 一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。
 

10.3. 消息队列丢失数据怎么解决

首先mq丢数据分三种情况

1.生产者网mq写数据时,2.mq丢数据(如宕机)3.消费者丢数据(consumer宕机)

先说rabitmq:
1)生产者丢数据
  1.rabitmq提供事物功能(百分百不会丢数据)但是会降低吞吐量(同步事物会阻塞)
  2.开启confirm模式,异步ack机制(失败消息重发)、
2)rabbitmq弄丢了数据
  1.开启rabbitmq的持久化(除非极其罕见的是,rabbitmq还没持久化,自己就挂了,可能导致少量数据会丢失的,但是这个概率较小)
  2.持久化配合confirm模式,只有当所有的消息持久化到硬盘之后才返回ack,可以保证百分百不会丢失数据
3)消费端弄丢了数据
  1.rabbitmq提供的ack机制(但是要关闭rabitmq的自动ack机制,当consumer处理完消息之后手动ack)

10.4. 怎么控制分布式事务

11. Redis

11.1. Redis消息一致性问题怎么解决

我们常见的三种缓存更新方案:

  1. 先更新数据库,再更新缓存
  2. 先删除缓存,再更新数据库
  3. 先更新数据库,再删除缓存(推荐)

11.2. 说说redis的使用场景

分布式锁(string)

计数器(string)

分布式全局唯一id(string)

消息队列(list)

新浪/Twitter用户消息列表(list)

抽奖活动(set)

实现关注模型,可能认识的人(set)

电商商品筛选(set)

排行版(zset)

11.3. 服务雪崩,服务击穿

假设有如下一个系统,高峰期请求为5000次/秒,4000次走了缓存,只有1000次落到了数据库上,数据库每秒1000的并发是一个正常的指标,完全可以正常工作,但如果缓存宕机了,每秒5000次的请求会全部落到数据库上,数据库立马就死掉了,因为数据库一秒最多抗2000个请求,如果DBA重启数据库,立马又会被新的请求打死了,这就是缓存雪崩。

  1. 事前:redis高可用,主从+哨兵,redis cluster,避免全盘崩溃
  2. 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL被打死
  3. 事后:redis持久化,快速恢复缓存数据

假如客户端每秒发送5000个请求,其中4000个为黑客的恶意攻击,即在数据库中也查不到。举个例子,用户id为正数,黑客构造的用户id为负数,
如果黑客每秒一直发送这4000个请求,缓存就不起作用,数据库也很快被打死。

查询不到的数据也放到缓存,value为空

使用布隆过滤

11.4. Redis 持久化机制

RDB(Redis DataBase): rdb是redis默认的持久化方式,按照一定周期策略把内存的数据以快照方式保存到硬盘中(二进制文件),即Snapshot快照存储,对应将产生dump.rdb数据文件,通过配置文件中的save参数来决定快照的周期

优点:

1、适合大规模的数据恢复!

2、对数据的完整性要不高!

缺点:

1、需要一定的时间间隔进程操作!如果redis意外宕机了,这个最后一次修改数据就没有的了!

2、fork进程的时候,会占用一定的内容空间!

AOF(Append Only File): Redis会将每个收到的命令都通过write函数追加到文件最后,类似于MySQL的binlog。当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。

Tips:

当两种方式同时开启时,数据恢复redis会优先选择AOF恢复
如果这个 aof 文件有错误,这时候 redis 是启动不起来的吗,我们需要修复这个aof文件 redis 给我们提供了一个工具redis-check-aof --fix
优点:

1、每一次修改都同步,文件的完整会更加好!

appendfsync always # 每次修改都会 sync。消耗性能 
1
2、每秒同步一次,可能会丢失一秒的数据

appendfsync everysec # 每秒执行一次 sync,可能会丢失这1s的数据! # 
1
3、从不同步,效率最高的!

appendfsync no # 不执行 sync,这个时候操作系统自己同步数据,速度最快!
1
缺点:
1、相对于数据文件来说,aof远远大于 rdb,修复的速度也比 rdb慢!

2、Aof 运行效率也要比 rdb 慢,所以我们redis默认的配置就是rdb持久化
 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringMVC的高频面试题包括以下几个方面: 1. SpringMVC的执行流程是怎样的? SpringMVC的执行流程包括以下步骤: - 用户发送请求到前端控制器DispatcherServlet。 - DispatcherServlet收到请求后调用HandlerMapping,找到对应的处理器和拦截器(如果有)。 - DispatcherServlet调用HandlerAdapter适配器,适配调用具体的处理器(Handler/Controller)。 - 处理器方法上可以使用@RequestParam注解指定请求参数的名称,@PathVariable注解从请求路径中获取请求参数,@RequestBody注解用于接收HTTP请求的JSON数据。 - 处理器方法执行完后,通过HttpMessageConverter将返回结果转换为JSON格式,并响应给前端。 2. SpringMVC常见的注解有哪些? SpringMVC常见的注解包括: - @RequestMapping:用于映射请求路径,可以定义在类和方法上,定义在类上表示类中的所有方法都是以该地址作为父路径。 - @RequestBody:用于将HTTP请求的JSON数据转换为Java对象。 - @RequestParam:用于指定请求参数的名称。 - @PathVariable:从请求路径中获取请求参数。 - @ResponseBody:将Controller内方法的返回对象转换为JSON格式传递给前端。 - @RequestHeader:获取指定的请求头数据。 - @RestController:相当于@Controller + @ResponseBody,用于表示返回的是JSON数据。 这些是SpringMVC高频面试题的主要内容,掌握了这些知识可以更好地应对相关的面试问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [SpringMVC的高频面试题](https://blog.csdn.net/qsw1364941774/article/details/131607365)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值