跳到主要内容

面试题2

1.什么是 IOC,简单讲一下 Spring IOC 的实现机制?

什么是IOC

IOC是控制反转的意思是spring框架的核心内容,IOC容器主要负责对象的实例化、配置和管理对象的Bean,并将它们之间的依赖关系进行注入,spring框架通过IOC容器实现了高度的可重用性和松偶性、开发者只需要关心bean的定义和依赖关系配置,不需要关心他们如何被创建

**Spring IOC的实现机制:**工厂模式+反射机制

  1. 配置Bean:

    • xml配置:将bean的信息配置到xml文件,让spring加载并且创建bean
    • 容器初始化:当spring IOC启动时,会读取配置文件,创建对象并将其放入到容器中,同时建立对象间得依赖关系
    • 对象注入:当需要使用某个对象时,容器会将它注入到需要的对象中

    Bean的注册方式:

    • 通过@Bean和@Configuration的方式直接定义要创建的对象与对象的关系

    • 通过@Component定义类需要定义ComponentScan的扫描路径

      2.**创建IOC容器:**通过ApplicationContext接口的实现类来创建IOC容器

    3.加载配置文件:IOC容器通过读取配置文件或者注解方式获取Bean的定义和配置信息

    4.创建Bean实例:利用反射机制,根据配置文件中的信息实例化Bean

    5.依赖注入:spring IOC提供了三种注入方法:

    • Setter注入:通过Setter方法将对象注入到属性中
    • 构造器注入:通过构造器将对象注入到属性中
    • 接口注入:通过实现接口将对象注入到属性中

6.初始化Bean:调用Bean实例的初始化方法,完成Bean的实例;

7.Bean的正常使用

8.Bean的销毁:调用DisposableBean的destory()方法;或者Bean实现了DisposableBean接口直接调用destory方法;

2.一条 SQL 语句在 MySQL 中的执行过程是怎样的?

连接器过程:

  1. 建立链接:同客户端进行TCP三次握手建立链接
  2. 校验密码:校验客户端的用户名和密码
  3. 权限判断:密码通过读取该用户权限

查询缓存过程:

  1. 解析SQL语句的第一个字段
  2. 如果是select语句,Mysql先去查询缓存,命中直接返回给客户端,如果没有继续往下执行,并将结果存入缓存

版本变动:Mysql8.0将查询缓存删除了

**解析SQL过程:**对sql语句进行词法、语法分析

执行SQL过程

​ 分析器:对要执行的SQL预计进行解析,并得到抽象语法树

​ 优化器:查询优化器会找出执行该语句所有可能的使用方案,选择一条最优的查询路径

​ 执行器:根据执行计划完成SQL语句操作,执行前会校验该用户有没有权限,yes:嗲用查询引擎 (no:返回错误)

3.并发和并行有什么区别?同步和异步有什么区别?

并发和并行的区别

  • 并发:指的是两个及两个以上的作业在同一时间段内执行(共享时间:俩人在一段时间内咦前一后走同一条路)
  • 并行:两个及两个以上的作业在同一时刻进行(共享空间:相当于俩人同时并排走一条路)

关键点:是空间和时间共享

同步和异步的区别:

  • 同步:发出一个调用,没收到结果之前,该调用就不可以返回一直等待(安全性高效率低)
  • 异步:调用发出后,不用等待返回结果,而是继续执行下一个任务,当任务完成后再进行相应的处理(安全性低效率高)

4. String 和 StringBuffer、StringBuilder 的区别是什么?

三者都是用来处理字符串的类,他们的主要区别在于可变性和线程安全性:

  • String类:是不可变的底层被final修饰,所以是个不可变的对象,每次进行字符串的修改就会产生新的String对象,它是线程安全的;
  • StringBuffer:它是线程安全的底层是char[],但是它的每个方法上被加了sychronized,它的长度是可变的主要用于字符串的拼接,内部也封装也多种方法
  • StringBuilder他和StringBuffer功能一样唯一的不同就是它是线程不安全的,他们两个都是操作本身

string是存储在字符串常量空间里的、StringBuild存储在堆内存里、stringBuffer存储在堆内存里

5. MySQL 中的索引是怎么实现的?B+ 树是什么,B 树和 B+ 树的区别,为什么 MySQL 要用 B+ 树?

Mysql的索引是依靠B+树来实现的。

B+树是一个多路搜索树,是对B树的一个改进

B+树:

B+树

B树

B树

B+树是什么?

B+树是一个多叉树,其非叶子节点都是键值的一部分,它可以将数据按照一定的顺序组织起来从而提高查询效率;

B+树和B树的区别:

  • 叶子节点(最底部的节点)才会存放实际数据(索引+记录),非叶子节点只会存放索引
  • 所有索引都会有叶子节点出现,叶子节点之间构成一个有序链表
  • 非叶子节点的索引也会同时存在于子节点中,并且是在子节点中所有索引的最大(或最小)
  • 非叶子节点中有多少个子节点,就有多少个索引

性能差别:

  1. 单点查询
    • 数据量相同的情况下,相比于存储及存索引又记录的B树,B+树的非叶子节点可以存放更多的索引,因此B+树更【矮胖】查询底层节点的磁盘I/O次数会更少
  2. 插入和删除的效率
    • 因为有冗余节点的存在,B+树的插入和删除效率更高
  3. 查询范围
    • B+树的所有叶子节点间还有一个链表进行链接

Mysql中的B+树:

image.png

为什么Mysql要用B+树?

  • B+树的非叶子节点,不存放实际的记录数据,进仅存放索引,因此数据量相同的情况下,相比于存储即存索引又存记录的B树,B+树的非叶子节点可以存放更多的索引,因此B+树可以比B树更【矮胖】,查询底层节点的磁盘I/O次数会更少
  • B+树种有大量的冗余节点(所有的非叶子节点都是冗余节点),这些冗余索引让B+树在插入、删除的效率都很高,比如删除根节点的时候,不会像B树一样发生复杂的树变化;
  • B+树叶子节点之间用链表链接起来了,有利于范围查询,而B树要实现的范围查询,因此只能通过树的遍历来完成范围的查询,这会涉及多个节点的磁盘I/O操作,范围查询效率不如B+树

6.Spring中都用到了什么设计模式?

  • 单例模式:spring的Bean默认是单例模式,通过Spring容器IOC管理Bean的生命周期,只被创建一次,可以被复用
  • 工厂模式:通过BeanFactory和ApplicationContext创建并且管理Bean对象
  • 代理模式:Spring AOP 基于动态代理技术面向切面编程,提供了AOP编程的支持
  • 责任链模式:spring的过滤器和拦截器使用了责任链模式,多个过滤器和拦截器按照一定的顺序执行,每个过滤器和拦截可以拦截请求或者相应并作出相应的处理

7.MySQL 事务有哪些隔离级别、分别有什么特点,以及 MySQL 的默认隔离级别是什么?

  • 读未提交
    • 允许一个事务读取另一个事务未提交的数据,这种隔离级别容易出现脏读、不可重复度和幻读的问题
  • 读已提交
    • 保重一个事务提交后才能被另一个事务读取,这种隔离级别避免了脏读的问题,但是仍然会出现不可重复读和幻读问题
  • 可重复读
    • 保证一个事务在多次执行同一个查询时,结果是一致的,避免了脏读和不可重复读问题、这种隔离级别容易出现幻读
  • 串行化
    • 最高级别的隔离级别,保证事务的串行化执行,避免了脏读、不可重复读,幻读的问题,但是效率很低

​ Mysql的默认级别是可重复读,因为在大多数应用中,可重复读级别能够提供足够的隔离性,同时性能相对较好,隔离级别需要按场景来进行调整,如在高并发情况下,需要将隔离级别调整为读已提交或者序列化来提高效率。

8.Redis的单线程模型,IO 多路复用是什么?

Redis的单线程模型:

Redis是基于内存的高性能键值存储系统、采用单线程模型设计、在Redis中所有的客户端的请求都是有一个单线程进行处理,这个单线程不断的从客户端套接字读取命令请求,并将命令请求放入一个队列中、Redis的事情处理器会按照一定的规则选择一个请求进行处理、处理完毕后将相应结果返回客户端。

优势:

  1. 简单高效:单线程避免了多线程之间的竞争和上下文切换开销、可以更高效的利用CUP和内存资源
  2. 可靠性高:单线程避免了多线程之间竞争和死锁的问题,提高系统的可靠性
  3. 易于维护:单线程简化了Redis的代码结构和网络通信。

Redis的IO多路复用:

指的是利用操作系统的一些特性,通过监听多个文件描述符来实现同时处理多个连接的I/O操作,在Redis中通过使用多路复用技术、主线程可以同时监听多个客户端的连接、当某个客户端出现了I/O事件时,主线程便可以及时的处理该事件,从而提高了Redis的I/O效率

9. 什么是 BIO、NIO、AIO?

三者都是Java中关于网络编程的不同实现方式,主要的区别在于IO操作的方式和实现方式。

  • BIO(同步阻塞IO)

指的是在IO操作时,必须等待IO完成才能进行下一步的操作,在服务器端,每个链接都需要独立的线程完成数据的读取和处理,如果连接数过多,会导致系统资源消耗严重,性能下降。在客户端,如果连接远程服务器时,服务器相应时间过长,客户端会一直等待,造成堵塞。它的并发性低。

  • NIO(同步非阻塞IO)

指的是在进行IO操作时不需要等待IO完成才能进行下一步操作,而是通过轮询机制来实现IO操作的异步执行。在服务器端,可以使用一个线程处理多个连接的数据读写,大大降低了系统资源的消耗,提高了系统的可扩展性和性能,在客户端,连接远程服务器时,如果服务器响应时间过长,客户端可以进行其他操作,不会造成堵塞,虽然弥补了并发能力,NIO代码比较复杂,适合的场景连接数多而且变化较少

  • AIO(异步非阻塞IO)

java1.7之后增加的IO模型,它支持异步非IO操作,与NIO区别不需要像NIO一样轮询,而是通过回调函数的方式在数据准备好后通知应用程序进行数据的读取,这样可以更加高效的利用系统的资源,提高吞吐量,代码实现复杂,连接数非常多而且连接时间较长

10. Redis 基础类型中的 String 底层实现是什么?

Redis的底层实现类型是一个动态的字符串SDS,他没有直接复用C语言的字符串而是新建了属于自己的结构SDS,Redis中所有的键都是由字符串对象实现的即底层都是SDS;它的优势:

  • 字符串的长度处理:直接记录当前的字符串的长度,时间复杂度O(1),相比于C语言的时间复杂度O(n)强悍了亿点点
  • 内存的重新分配,先说C语言,会出现数组下标越级或者内存分配溢出,然而!SDS有两个重要的空间管理方案
    • 空间预分配:SDS修改后,len长度小于1M,将会分配相同长度的未使用空间,反之,将会分配1M的使用空间
    • 惰性空间释放:SDS缩短时并不会回收多余的内存空间,而是使用free字段将多出来的空间记录下来,如果有后续变更操作直接使用free记录的空间
  • 二进制安全:根据len的长度来判断字符串的结束,而C语言时读取‘\0’ 可能存在提前结束情况
  • SDS 对内存的分配和释放进行了封装,使得字符串的空间可以根据需要进行增长或缩减,避免了 C 语言字符串需要手动分配空间的问题。
  • 提供了字符串追加操作,可以在 O(1) 的时间内完成追加操作。

11. Spring、SpringMVC、SpringBoot 三者之间是什么关系?

Spring、SpringMVC、SpringBoot 是三个独立的框架,它们之间的关系是:

  • spring是一个开源的轻量级java开发框架,它提供了依赖注入和面向切面编程等,可以使得java开发更简单、高效、灵活和可维护它提供了IOC和AOP的支持,用于构建企业级的项目,spring包含多个模块Spring Core、Spring Context、Spring JDBC 、Spring Web等每个模块提供了不同的功能。
  • Spring MVC是spring框架的一部分,是基于MVC(model 、view、Controller)设计模式的框架,用于构建web框架,它提供了控制器、视图解析器、数据绑定、异常处理等使得开发web应用程序更加简单、springMVC也支持restful架构
  • springboot 是基于spring框架的一个开发框架,用于快速构建独立的生产级别的spring应用程序,它通过自动配置和约定优于配置的方式,简化了spring应用程序的配置和开发过程,提高了开发效率、springboot集成了很多常用的第三方库和工具例如springData、spring Security、Thymeleaf、Logback等、sprinboot内置了tomcat和Undertow等服务器,不需要像SSM一样自己去配置Tomcat环境简化了开发

因此springBoot可以看作是在spring的基础上,通过自动配置和约定优于配置的方式,提供了更加方便的体验、springMVC则是在spring的框架中构建web应用程序 模块.

12.意向锁是什么?有什么作用?它是表级锁还是行级锁?

意向锁是一种 MySQL 数据库中的锁,用于表级锁协调多个行级锁的使用。在表级锁定一个表之前,MySQL 需要先获得一个意向锁,以表明要获取的锁类型(读锁或写锁),避免其他事务锁定整个表或锁定一部分表时引发死锁。

  • 意向锁是什么?
    • 使用InnoDB引擎表里时对某些记录加上【共享锁】之前,需要先在表级别上加上一个【意向共享锁】
    • 使用InnoDB引擎表里时对某些记录加上【独占锁】之前,需要先在表级别上加上一个【意向独占锁】
  • 意向锁的作用

意向锁是表级锁,不会和行级的共享锁和独占锁发生冲突,而且 意向锁之间也不会发生冲突、只会和共享锁独占表锁发生冲突。

意向锁可以提高数据库并发性能,防止死锁的发生。它是表级锁,而不是行级锁。

表锁和行锁是满足读读共享、读写互斥、写写互斥的

作用:为了快速判断表里是否有记录被加锁

  • 如果没有意向锁,那么加独占表锁时,就需要遍历表里的所有记录,查看是否有记录存在独占锁、这样效率会特别慢
  • 有了意向锁,加【独占表锁】前,先回加上表级别的意向独占锁、那么在加【独占表锁】时,直接查该表是否有意向独占锁、如果有则意味着表里面已经有记录被加了独占锁、这样就不用遍历表里面的内容

意向锁是表级锁还是行级锁

表级锁有:表锁、元数据锁、意向锁、AUTO-INC锁

其他:

普通的select是不会加行级锁的、普通的select语句是利用MVCC实现的一致性读是无锁的、不过select也是可以对记录加共享锁和独占锁的。

13、硬链接和软连接是什么,二者有什么区别?

在 Linux 文件系统中,硬链接(hard link)和软链接(symbolic link)都是一种文件链接的方式,可以用于将一个文件链接到另一个文件上。它们的主要区别在于创建方式、所占空间和使用限制等方面。

image-20230316143503168

硬链接:

Linux下文件时通过索引节点Inode来识别文件,硬链接当中、多个文件名可以指向同一个索引节点,当创建一个硬链接的时候,文件系统会维护一个引用计数、只要有文件指向这个 区块,他就不会从硬盘中消失、两个文件拥有相同的inode、通过查看文件内容也会发现是同一个文件,只删除一个连接并不影响索引节点本身和其他的连接、只有当最后一个连接也被删除后文件的数据块及目录的连接才会被释放,也就是真正的删除(一句话硬链接就是单纯的copy一份也就是镜像文件,要想删除完毕必须全部删除)

软连接:

又叫做符号连接、这个文件包含了另一个文件的路径名,软连接可以是任意文件或目录,可以连接不同的系统文件、在对符号文件进行读或写操作的时候,系统会自动将这些操作转换为对源文件的操作、但删除链接文件时,系统仅仅删除链接文件而不是删除源文件本身(一句话类似于桌面的快捷方式能快速打开,但是无法从根底删除)

img

14.JVM 内存模型,分为哪些区域?各区域的作用是什么?

划分区域:

运行时的数据区:程序计数器、java虚拟机栈、本地方法栈、java堆、方法区

  • 程序计数器:每个线程都有自己的程序计数器,用于指示当前线程执行的字节码指令的行号,以便线程执行时能够回到正确的位置。是个线程不共享区域,如果线程正在执行 Native方法,计数器值为空。
  • java虚拟机栈:也成为java方法栈,用于存放方法执行时的局部变量表、操作数栈、动态链接方法出口等信息。每一个线程在执行一个方法时都会为该方法分配一个栈帧、并将栈帧压入虚拟机栈,当方法执行完毕后、虚拟机会将其出栈。线程不共享区域
  • 本地方法栈:类似于java虚拟机栈,面向的native方法线程不共享区域
  • 堆:存放对象实列:线程共享区域
  • 方法区:用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据、方法区也是被线程所共享的
  • 运行时的常量池:时方法区的一部分,用于存储编译期间生成的各种字面量和符号引用。

image-20230316145426321

15.有哪些注解可以注入 Bean?@Autowired 和 @Resource 的区别?

spring内置的@Autowired以及JDK内置的@Resource和@Inject都可以注入Bean

@Autowire和@Resource用的多

  • @Autowire是spring提供的注解、@Resource是JDK提供的注解
  • @Autowire默认的注入方法是byType(根据类型进行匹配)@Resource默认的方式是ByName(根据名字进行匹配)
  • 当一个接口存在多个实现类的情况下、@Autowire和@Resource都需要通过名称才能正确的匹配到对应的Bean,Autowire可以通过@Qualifier注解来指定名称,@Resource可以通过name属性来指定

16.线程的生命周期是什么,线程有几种状态,什么是上下文切换?

image-20230316150137090

  • 【初始状态】:创建线程对象
  • 【可运行状态】:(就绪状态)指该线程已经被创建,可以由CPU调度运行
  • 【运行状态】:指获取CPU时间片运行中的状态当CPU时间用完,会从【运行状态】转换为可运行状态,会导致线程的上下文切换
  • 【阻塞状态】:如果调用了阻塞API如BIO读写文件、这时该线程实际不会用到CPU,会导致线程上下文切换
  • 【终止状态】:表示线程已经执行完毕、生命周期已经结束、不会再转换为其他状态

线程上下文切换:CPU通过分配时间片来执行任务,当一个任务的时间片用完,就会切换到另一个任务,在切换之前会保存上一个任务的状态,当下次再切换到该任务,就会加载这个状态

image-20230316151226093

17. 什么是网关,网关有哪些作用?

**什么是网关:**是连接两个或者多个不同的网络设备,可以实现协议的转换,数据的转发和安全策略的实现功能、简单来说网关是设备与路由器之间的桥梁,由它将不同的网络间进行访问控制,转换交接等等

常见的网关有:应用网关、协议网关、安全网关

作用:

  • 实现协议的转换:不同网络之间通常使用不同的网络协议,通过网关可以实现协议的转换。使得不同网络之间能够相互通信
  • 提供数据转发功能:网关可以传输的数据进行过滤、路由转发等处理、确保数据的安全有效
  • 实现安全策略:网关可以对传输的数据进行加密、认证授权等操作、保证数据的安全性和可靠性
  • 提供缓存功能:网关可以将 一部分的数据缓存起来,提高数据的访问速度和响应性能。
  • 支持负载均衡:网关可以将请求分配到不同的服务器上,实现负载均衡、提高系统的可用性和性能
  • 实现访问控制:网关可以对访问进行控制防止未授权的访问和攻击,提高系统的安全性

网关可以分为以下几种:

  1. 应用网关:用于应用层协议的处理:如HTTp、STMP等
  2. 数据库网关:用于数据库的访问的控制和管理
  3. 通信网关:用于不同通信协议之间的数据交换如TCP/IP、UDP/IP等
  4. API网关:用于管理API请求,实现API的授权、限流、监控等功能

18.使用 Redis 实现一个排行榜?

主要使用的是ZSet进行实现,下面是为什么选用ZSet:

  • 有序性:排行榜肯定需要实现一个排序的功能,在Redis中有序的数据结构有List和ZSet;
  • 支持分数操作:ZSet可以对集合中的元素进行增删改查操作,十分贴合排行榜中用户分数动态变化的场景,而List并不能针对分数进行操作,只有其中的value进行操作;
  • 支持范围查询:ZSet可以按照分数进行范围查询,如排行榜中的Top10需求就可通过该特性进行实现;
  • 支持去重:由于ZSet属于Set的特殊数据结构,因此同样拥有Set不可重复的特性,对于排行榜中不可出现重复项的需求也十分贴合,而List只能手动去重。

因此选择Zset实现排行榜相对list会更加合适高效:

# 添加示例数据
ZADD scores 90 "张三"
ZADD scores 85 "李四"
ZADD scores 95 "王五"
ZADD scores 92 "赵六"
# 查询排名前3的学生信息
ZRANGE scores 0 2 WITHSCORES
# 查询排名前3的打印
1) "王五"
2) "95"
3) "赵六"
4) "92"
5) "张三"
6) "90"
# 删除学生“李四”的成绩信息
ZREM scores "李四"

下面是SpringBoot整合Redis进行实现

// 添加学生成绩
public void addScore(String name, int score) {
redisTemplate.opsForZSet().add("scores", name, score);
}

// 查询排名前N的学生成绩
public List<Map.Entry<String, Double>> getTopScores(int n) {
return redisTemplate.opsForZSet().reverseRangeWithScores("scores", 0, n - 1)
.stream()
.map(tuple -> new AbstractMap.SimpleEntry<>(tuple.getValue(), tuple.getScore()))
.collect(Collectors.toList());
}

// 删除某个学生的成绩
public void removeScore(String name) {
redisTemplate.opsForZSet().remove("scores", name);
}

19.Nginx 是什么?它有哪些应用场景?

Nginx是一款高性能的Web服务器软件,也可以作为反向代理服务器、负载均衡器、http缓存和安全防护等多种用途,Nginx可以将服务器上的静态文件(html、css、图片)通过HTTP协议返回给浏览器客户端。同时Nginx是基于REST架构风格、以统一资源描述符URL或者统一资源定位符URL作为沟通依据,通过HTTP协议提供各种网络服务。它支持高达50000个并发连接数

Nginx的应用场景包括:

  • Web服务器:Nginx可以作为Web服务器,提供静态文件和动态文件(如PHP)的处理,静态HTML页面,CSS、JavaScript、图片等,它能够快速响应客户端的请求,提高网站的访问速度。
  • 反向代理服务器:Nginx可以作为反向代理服务器,将请求转发到后端服务器,实现负载均衡和高可用性
  • 负载均衡器:Nginx可以作为负载均衡器,将客户端的请求均衡分配到多个应用服务器上,从而达到分摊负载的效果、提高系统的可靠性和性能
  • HTTP缓存:Nginx可以作为HTTP缓存,缓存静态文件和动态文件,从而减少后端应用服务器的负载,提高网站的访问速度
  • 安全防护服务器:Nginx可以作为安全防护服务器,通过设置防火墙,反向代理等技术,防止恶意攻击和DDos攻击,保障网站的安全性。
  • WebSocket服务器:Nginx支持WebSocket协议,可以作为WebSocket服务器,实现实时通信即时聊天等功能
  • Nginx支持热部署、启动简单可以做到7*24小时不间断运行不需要重新启动

Redis 的持久化机制有哪些?说说各自的优缺点和应用场景?

Redis的持久化包括RDB快照和AOF日志两种持久化方式,快照是一次全量的备份,AOF日志是连续的增量备份,快照是内存数据的二进制序列化形式,在存储上非常紧凑,而AOF日志记录的是内存数据修改的指令记录文本。

RDB(默认)的优缺点:

  • 优点:RDB文件小,适合定时备份,用于灾难恢复,Redis加载RDB文件的速度比AOF快很多、因为RDB文件中直接存储的时内存数据,而AOF文件存储的是一条条命令,需要重演命令。
  • 缺点:RDB无法做到实时的持久化,若在两次的bgsave(background save)间宕机,则会丢失区间(分钟级)的增量数据,不适用于实时性要求较高的场景,RDB的cow(Copy On Write)机制中,fock子进程属于重量级操作,而且会阻塞redis的主进程。存在老版本的redis不兼容新版本的RDB格式文件问题

AOF日志(Append only file)优缺点:

AOF日志会在持续运行中持续的增大,需要定期的进行AOF重写,对AOF进行瘦身

**优点:**AOF只是追加写日志文件、对服务器性能影响较小、速度比RDB要快、可以保证数据不会因为断电或者崩溃等原因丢失;适用于对数据安全性要求较高的场景

**缺点:**AOF方式生成的日志文件太大,需要不断的AOF重写,进行瘦身。即使经过AOF重写瘦身,由于文件是文本文件,文件体积较大(相对于RDB的二进制文件)。AOF重演命令式的恢复数据速度比RDB要慢.Redis持久化

image-20230317150220593