面试题集合

1.数据库连接池的工作机制是什么?为什么需要数据库连接池?

答:数据库连接池是一种维护和管理数据库连接的机制,他可以缓存一定数量的数据库连接对象,并将他们提供给需要访问数据库的线程使用。当需要连接数据库时,客户端直接从连接池中获取一个现成的连接对象,而不是每次都重新创建一个新的连接。连接完成后,客户端将连接还回连接池,以供其他线程复用。

线程池可以提高数据库的性能和并发操作的吞吐量,并减少了数据库服务器对新连接进行建立、断开和验证的开销。

线程池工作机制如下:

1.初始化连接池:在应用程序启动时,连接池会预先创建一定数量的数据库连接,以满足后续的请求需求。

2.获取连接:当应用程序需要连接到数据库时,他会向连接池发送一个请求,连接池会返回一个可用的连接对象。

3.使用连接:应用程序使用连接对象执行数据库操作。

4.将连接还回连接池:当应用程序完成数据库操作后,他会将连接对象归还到连接池中,以便其他线程可以继续使用。

5.清除失效连接:连接池还会检查连接对象的状态,如果发现连接已经失效时,连接池会将其从连接池中清除,并创建新的连接来替换他。

需要使用数据库连接池的原因包括:

1.数据库连接对象的创建和销毁涉及到较大的系统资源开销,使用连接池可以避免这种开销。

2.连接池可以限制并发连接数,防止过多的连接导致数据库服务器负载过高。

3.连接池可以自动管理连接状态,检测和清除失效连接,确保连接的有效性和可靠性。

综上,数据库连接池是一种提高数据库访问效率和可靠性的重要机制,在高并发、大负载的应用场景下具有重要意义。

2.常见的数据库连接池

1.c3p0:c3p0是一个稳定且功能丰富的java数据库连接池,他支持JDBC3和JDBC4规范,并提供了许多高级特性,如连接检测、连接泄露监控等

2.Druid:Druid是阿里巴巴开源的一个java数据库连接池,具有强大的监控和扩展功能。他支持MySQL、Oracle、SQL Server等主流数据库,并提供了丰富的监控指标和统计信息。

3.Tomcat JDBC Pool:Tomcat JDBC Pool是Apache Tomcat服务器自带的连接池,适用于Tomcat环境下的数据库连接管理,具有轻量级和易于集成的特定。

3.什么是线程池

线程池是一种用于管理和复用线程的机制,他包含了一组预先创建的线程,并提供一种方式来有效地执行多个任务。

线程池的主要目的是为了减少线程的创建和销毁,以及控制并发线程的数量,避免系统因过多的线程而导致资源耗尽或性能下降。

线程池通常具有以下部分:

1.任务队列:任务队列用于存储等待执行的任务。当一个任务到达时,线程池会从任务队列中获取任务并将其分配一个空闲线程执行。

2.线程管理:线程管理器负责创建、销毁和管理线程池中的线程。他根据需要动态地去创建新的线程或回收空闲线程,以保持线程池中的线程数量在一定范围内。

3.工作线程:工作线程是线程池中实际执行任务的线程。他们负责从任务队列中获取任务并执行任务的操作。

线程池的工作流程如下:

1.当一个任务到达时,线程池首先将任务放入任务队列中等待执行。

2.线程管理器检查线程池中是否有空闲线程,如果有,则将任务分配给一个空闲线程执行。如果没有空闲线程,则根据线程池的配置情况,动态地创建新的线程并将任务分配给新创建的线程。

3.工作线程从任务队列中获取任务,并执行任务的操作

4.执行完任务后,工作线程返回线程池,并变为可用状态,等待下一个任务的分配。

通过使用线程池,可用有效地控制并发线程的数量,减少线程的创建和销毁开销,并提高系统的性能和资源利用率。

4.常见的状态码

1.1xx(信息性状态码):表示接收的请求正在被处理,常见的有:

      100:客户端应该继续请求

      101:服务器要求切换协议

2.2xx(成功状态码):表示请求已经被服务器接收、理解、并接受

    200:请求成功

   201:请求已经被创建

   204:请求成功但无响应内容

3.3xx:重定向状态码

4.4xx(客户端错误状态码):表示客户端提交的请求有错误或不能被处理。

   400:请求格式错误

  401:没有提供认证信息

 403:服务器拒绝访问请求资源

404:请求的资源不存在

5.5xx(服务器错误状态码):表示服务器在处理请求时出错

  500:服务器内部错误

 502:网关错误

503:请求服务不可用

5.JSP和Servlet是Java Web开发中常用的两种技术,他们有以下相同点和不同点

相同点:

1.都是用于构建Java Web应用程序的技术,可以在服务端处理请求并生成动态内容

2.都属于javaEE规范的一部分,可以通过Java Servlet容器(如Tomcat)来运行。

不同点:

1.JSP是基于HTML页面的扩展,允许再HTML页面中嵌入Java代码,而Servlet是纯Java代码,没有HTML结构。

2.JSP主要关注页面的展示,允许开发者以类似HTML的语言编写动态内容,而Servlet更注重请求的处理和逻辑控制。

3.JSP通过JSP引擎将JSP文件编译为Servlet类,然后由Servlet容器执行,而Servlet本身就是一个Java类,直接由Servlet容器执行

4.JSP适合用于页面展示和前端开发;Servlet则更加灵活,适用于复杂的业务逻辑处理和后端开发。

6.请谈一下spring框架IOC,AOP的认识

Spring框架是一个广泛应用于Java开发的轻量级框架,其中的两个核心概念是IOC(控制反转)和AOP(面向切面编程)

1.IOC(控制反转):IOC即控制反转,也称为依赖注入。他是一种设计原则,通过IOC容器来实现对象的依赖关系的管理。传统的开发方式中,对象之间的依赖关系需要手动创建和管理,而使用IOC容器后,对象的创建和依赖关系的注入由容器自动完成。

在Spring框架中,通过配置或注解将对象的创建和依赖关系描述出来,,然后由Spring容器负责管理这些对象的生命周期和依赖关系。这样可以提高代码的灵活性、可维护性和可测试性,使得开发者能够更专注于业务逻辑的实现。

2.AOP(面向切面编程):AOP即面向切面编程,他是与OOP(面向对象编程)相辅相成的一种编程思想。AOP的核心思想是将横切关注点(如日志记录、事务管理等)从业务逻辑解耦出来,以一种模块化的方式来管理和复用。

在Spring框架中,AOP通过代理模式和动态代理技术来实现。通过配置或注解的方式,可以定义切面(Aspect)以及切入点(Pointcut),并在特定的方法执行前、后或异常抛出时,通过织入(Weaving)的方式将切面逻辑嵌入到目标方法中。

使用AOP可以提高代码的可重用性和可维护性,避免了代码冗余和重复性的工作,使得横切关注点的管理更加简洁和灵活。

7.HTTP协议中,Get和Post请求有何异同

1.参数传递方式:

get请求:参数通过URL的查询字符串进行传递,参数会附加在URL后面

post请求:参数通过请全体进行传递,参数不会暴露在URL中,而是以键值对的形式包含在请求体中。

2.安全性

get请求:参数在URL上可见,敏感信息可能会被记录在浏览器的历史记录、服务器的日志文件等地方,不适合传递敏感信息。

post请求:参数不会暴露在URL中,相对于GET请求更加安全,适合传递敏感信息,例如用户的用户名和密码。

3.缓存

get请求:默认情况下可以被缓存,浏览器可以缓存响应结果,下次再发送相同的GET请求时可以之间从缓存中获取,提高性能

post请求:默认情况下不会被缓存,每次请求都会向服务器请求最新的数据

4.请求语义

get请求:用于获取资源,不应带有副作用,即不应更改服务器端状态,仅用于读取数据。

post请求:用于提交数据给服务器,可以具有副作用,可能导致服务器端的状态变化

8.应用系统一级二级缓存是怎样定义的,一般实现有哪些技术

应用系统中的一级和二级缓存是为了提高系统性能而引入的缓存机制。

一级缓存也称为本地缓存,通常位于应用程序内部。他是再内存中直接存储数据,用于临时保存经常访问的数据或计算结果,一级缓存的生命周期与应用程序的生命周期相同,常见的一级缓存实现有:

1.内存缓存:使用内存数据结构将数据保存在内存中,读写速度快,常见的内存缓存库有Ehcache、Caffeine等。

二级缓存是位于应用系统和数据库之间的缓存层。他可以减轻数据库的负载,并加快读取频繁的数据的速度。不同的应用系统可能采用不同的二级缓存实现方式,常见的二级缓存实现有:

1.基于数据库的缓存:将缓存数据存储在数据库中,例如使用Redis作为缓存数据库

2.分布式缓存:将缓存数据分布在多个节点上,常见的分布式缓存系统有Redis、Cluster等

3.ORM框架的缓存:一些ORM框架(如Hibernate)提供了自己的缓存机制,在应用系统和数据库之间缓存对象的数据,提高查询效率。

9.什么是微服务,spring cloud微服务组件有哪些

微服务是一种软件架构风格,将一个大型的应用系统拆分成多个小型、独立的服务单元,每个服务单元都可以独立部署、独立运行,并通过轻量级的通信机制进行交互。每个微服务都专注于实现某个具体的业务功能,彼此之间解耦,易于独立开发、测试、部署和维护。

spring cloud是基于spring boot开发的一套开源微服务框架,为开发者提供了一系列的组件和工具,简化了微服务架构的开发和部署。

常用组件:

1.nacos:注册中心,注册中心的核心功能:服务注册、服务发现、服务剔除。他记录了服务和服务地址之间的映射关系,在分布式架构中,服务会注册导致这里,当服务需要调用其他服务时,就到这里找到服务的地址并进行调用,注册中心本质上是为了解耦服务提供者和服务消费者。

2.Ribbon:负载均衡,负载均衡是我们处理高并发、缓解网络压力和进行服务器扩容的重要手段之一,但是一般情况下我们说的负载均衡是指服务端的负载均衡,目前主流的负载方案分为以下两种,集中式负载均衡在消费者和服务提供方中间使用独立的代理方式进行负载。客户端自己做负载均衡根据自己的情况做负载。

3.Feign:服务调用,Feign是一个声明式的REST客户端,他能让rest调用更加简单,Feign提供了HTTP请求模板通过编写简单的接口和插入注解就可以定义好HTTP请求的参数格式

10.数据库的三个范式

数据库设计中的三个范式是关系数据库理论中的基本规范,用于规定数据库表的结构和关系。这三个范式分别是:

  1. 第一范式(1NF):第一范式要求数据库表中的每个字段都是不可再分的最小数据单元,即每个字段不能包含多个值或重复的值。此外,表中的每个记录应该是唯一的,即每行数据必须具有唯一的标识符(主键)。

  2. 第二范式(2NF):第二范式在满足第一范式的基础上,要求表中的非主键字段必须完全依赖于主键,而不能部分依赖。换句话说,非主键字段必须直接与主键相关,而不能存在对主键的冗余依赖。

  3. 第三范式(3NF):第三范式在满足第二范式的基础上,进一步要求表中的非主键字段之间不能存在传递依赖关系。也就是说,如果一个非主键字段依赖于另一个非主键字段,那么这两个字段应该被拆分成两个独立的表。

通过遵循第一范式、第二范式和第三范式,可以减少数据冗余、提高数据的一致性和完整性,以及简化数据库的更新和查询操作。然而,在实际设计中,并不是所有情况都需要严格遵守所有范式,有时需要根据具体需求进行适当的冗余设计和优化。

11.JDK1.8新特性
  1. Lambda 表达式:Lambda 表达式是一种简洁的语法形式,用于处理函数式接口(Functional Interface)。它允许以紧凑的方式表示单个方法接口,并使代码更加可读、简洁。

  2. 方法引用:方法引用是另外一种新的语法形式,它提供了一种简便的方式调用一个类中已经存在的方法作为 Lambda 函数的实现。

  3. Stream API:Stream API 提供了一种高度抽象和独立于底层实现的处理序列数据的方式。它可以处理大量的数据,并充分利用多核 CPU 优势,同时使用非常简单、便利。

  4. Optional 类:Optional 类是一种包装器,它可以包含或不包含非空值。该类可以帮助开发人员避免空指针异常,并提供优雅的函数编程方式。

  5. 接口默认方法:当接口需要添加新的方法时,一般会破坏向后兼容性,所以 JDK 8 引入了接口默认方法的概念,可以在接口中实现方法,而不需要所有实现该接口的类都重新实现该方法。

  6. 时间日期 API:Java 8 引入了新的时间日期 API,它大大简化了处理日期、时间和时区的代码,并提供了新的 DateTimeFormatter 类型可以用来格式化和解析日期时间字符串。

12.TCP/IP分别在网络哪一层

TCP/IP协议族是一组用于网络通信的协议,它涵盖了通信中的各个层面。在TCP/IP模型中,它的协议分别在以下网络层次上进行操作:

  1. 网络接口层(Network Interface Layer):该层负责封装和解封装数据链路层数据,处理与物理网络介质之间的通信,以及网络适配器的控制和传输错误的检测。

  2. 互联网层(Internet Layer):该层主要负责网络互连和寻址,其中最核心的协议是Internet协议(IP),它定义了在网络上如何定位终端设备、路由选择和数据包传递等。

  3. 传输层(Transport Layer):该层提供端到端的可靠数据传输服务,其中最重要的协议是传输控制协议(TCP)。TCP协议通过建立连接、数据分段、流量控制和拥塞控制机制,确保数据可靠地从发送方传输到接收方。

总结起来,TCP/IP协议在网络层次中分别操作在网络接口层、互联网层和传输层。这些协议共同工作,实现了数据的可靠传输、寻址和互联等功能,是构建互联网的基础。

13.linkedlist和arraylist的区别,linkedlist的优势是什么

LinkedList和ArrayList是Java集合框架中List接口的两个实现类,它们在内部实现和使用场景上有一些区别。

  1. 内部实现方式:

    • LinkedList:LinkedList内部使用双向链表结构实现,每个节点包含了前一个节点和后一个节点的引用。
    • ArrayList:ArrayList内部使用数组结构实现,通过索引来访问和操作元素。
  2. 插入和删除操作:

    • LinkedList:由于LinkedList使用了双向链表的结构,插入和删除操作在LinkedList中具有较好的性能。对于在链表头或者链表尾进行插入和删除操作,时间复杂度为O(1)。
    • ArrayList:ArrayList是基于数组实现的,插入和删除操作可能需要移动其他元素,因此效率相对较低。在数组的中间位置进行插入和删除操作时,平均时间复杂度为O(n)。
  3. 随机访问:

    • LinkedList:由于LinkedList使用链表结构,无法直接根据索引进行随机访问,需要从头节点或尾节点开始遍历。因此,LinkedList的随机访问效率较低,时间复杂度为O(n)。
    • ArrayList:ArrayList使用数组作为内部存储结构,可以通过索引直接访问元素,因此具有较好的随机访问性能,时间复杂度为O(1)。
  4. 空间占用:

    • LinkedList:由于LinkedList的节点需要额外的空间存储前后节点的引用,所以相对于ArrayList会占用更多的内存空间。
    • ArrayList:ArrayList使用连续的数组存储元素,不需要额外的指针引用,因此相对来说空间效率较高。

基于以上特点,LinkedList在以下场景中有一些优势:

  • 需要频繁插入和删除操作的场景,特别是在链表头部或尾部进行操作。
  • 对于需要频繁修改列表的场景,如实现栈、队列等数据结构。
  • 不需要频繁随机访问元素的场景。

需要注意的是,LinkedList在某些操作上可能会比ArrayList效率低下,因此在具体使用时需根据实际情况选择合适的数据结构。

14.spring中的单例bean的线程安全问题

在spring中,单例bean的线程安全问题是需要考虑的。由于单例bean在容器启动时就会被创建,因此所有的请求都会共享通一个bean实例,如果bean有状态,即bean是属性回随着请求的处理而改变,那么在多线程并发访问的情况下,就需要考虑线程安全问题。

在单例bean中,如果有多个线程同时访问bean实例并且bean实例中有共享的数据,那么就可能出现数据不一致的情况,这是因为多个线程回竞争共享数据的访问权,导致数据出现冲突。为了解决这个问题,需要在多线程环境下保证bean实例的线程安全。

有多种方式可以保证spring中的单例bean的线程安全:

1.在bean对象中尽量避免定义可变的成员变量(不太现实)

2.在类中定义一个ThreadLocal成员变量,将需要的可变的成员变量保存在ThreadLocal中(推荐的一种方式)

需要注意的是,spring框架本身并不提供对单例bean的线程安全保证,因此需要在编写代码时自行考虑和实现安全的措施。

15.如何定义bean的范围

在spring中,可以使用@Scope注解来定义bean的作用域范围,常见的作用域范围:

  • singleton:单例模式,一个容器中只有一个实例,是spring默认的作用域
  • prototype:原型模式,每次获取bean时都会创建一个新的实例
  • request:每个http请求都会创建于一个新的bean实例,该作用域仅适合用于web应用程序上下问中。
  • session:每个http请求都会创建于一个新的bean实例,该作用域仅适合用于web应用程序上下问中。
  • global session:全局http会话作用域,仅在使用基于portle的web应用程序时才适用。
  1. @Component
    @Scope("prototype")
    public class MyBean {
        // ...
    }

    16.spring中的bean生命周期?

 1.实例化bean:

 Spring容器实例化bean的过程是通过BeanFactory接口的实现类DefaultListableBeanFactory来完成的。在DefaultListableBeanFactory中,具体的实例化过程中由AbstractAutowireCapableBeanFactory类的createBean()方法实现。该方法首先会调用doCreateBean()方法,创建bean的实例,并进行依赖注入,然后调用populateFactory()方法,为bean设置对象属性,最后调用initializeBean()方法,对bean进行初始化操作。

2.设置对象属性:

依赖注入的实现是通过DefaultListableBeanFactory类的autowireBean()方法完成的。该方法会根据bean的定义,自动注入对应的依赖项。具体的注入方式由AbstractAutowireCapableBeanFactoey类的populateBean()方法实现。在该方法中,会依次调用BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInititlization()方法,完成前置处理和后置处理的逻辑。

3.BeanPostProcessor的前置处理

BeanPostProcessor的前置处理是通过BeanPostProcessor接口的实现类完成的。spring容器在创建bean实例后,会扫描所有的BeanPostProcessor,并将其注册到容器中。在bean实例化和依赖注入完成后,spring容器会调用所有注册的BeanPostProcessor的postProcessBeforeInitialization()方法,执行前置处理的逻辑。具体的前置处理逻辑可以根据业务需求自定义实现。

4.初始化bean

Bean的初始化是通过AbstractAutowireCapableBeanFactory类的initializeBean()方法实现的。该方法会依次调用BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法,完成前置处理和后置处理的逻辑,然后调用invokeInitMethods()方法,执行bean的初始化方法。

5.BeanPostProcessor的后置处理:

BeanPostProcessor的后置处理是通过BeanPostProcessor接口的实现类来完成的。在bean的初始化方法执行完成后,Spring容器会调用所有注册的BeanPostProcessor的postProcessAfterInitialization()方法,执行后置处理的逻辑。具体后置处理逻辑可以根据业务需求自定义实现。

6.使用Bean:

使用Bean的过程是由应用程序完成的。应用程序可以通过Spring容器获取Bean的实例,并调用其方法来完成具体的业务逻辑。

7.销毁Bean:

Bean的销毁是通过AbstractAutowireCapableBeanFactory类的destoryBean()方法实现的。在容器关闭或调用destroy()方法时,spring容器会调用bean的销毁方法。具体的销毁逻辑可以根据业务需求自定义实现。在销毁方法执行完成后,spring容器会将bean实例从容器中移除。

18.bean的生命周期

首先会通过一个非常重要的类,叫做BeanDefinition获取bean的定义信息,这里面就封装看bean的所有信息,比如,类的全路径,是否是延迟加载,是否是单例等等这些信息

在创建bean的时候,第一步是调用构造函数实例化bean

第二步是bean的依赖注入,比如一些set方法注入,像平时开发用的@AutoWired都是这一步完成

第三步是处理Aware接口,如果某一个bean实现了Aware接口就会重写方法执行

第四步是bean的后置处理器BeanPostProcessor,这个是前置处理器

第五步是初始化方法,比如实现了接口InitializaingBean或者自定义了方法init-method标签或@PostContruct

第六步是执行了bean的后置处理器BeanPostProcessor,主要是对bean进行增强,有可能在这里产生代理对象,最后一步是销毁bean

19.Spring中的循环依赖(循环引用)

循环依赖:循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于A

循环依赖在spring中是允许存在的,spring框架依据三级缓存已经解决了大部分的循环依赖

1.一级缓存:单例池,缓存已经经历了完整的生命周期,已经初始化完成的bean对象

2.二级缓存:缓存早期的bean对象(生命周期还没走完)

3.三级缓存:缓存的是ObjectFactory,表示对象工厂,用来创建某个对象的

20.解决循环依赖的流程

第一,先实例化对象A,同时会创建ObjectFactory对象存入三级缓存singletonFactories

第二,A在初始化的时候需要B对象,这个走B的创建的逻辑

第三,B实例化完成,也会创建ObjectFactory对象存入三级缓存singletonFactories

第四,B需要注入A,通过三级缓存中获取ObjectFactory来生成一个A的对象同时存入二级缓存,这个是有两种情况,一个可能是A的普通对象,另外一个是A的代理对象,都可以让ObjectFactory来生产对应的对象,这也是三级缓存的关键

第五,B通过二级缓存earlySingletonObjects获得A的对象后可以正常注入,B创建成功,存入一级缓存singletonObjects

第六,回到A对象初始化,因为B对象已经创建完成,则可以直接注入B,A创建成功存入一次缓存singletonObjects

第七,二级缓存中的临时对象A清除

21.构造方法出现了循环依赖怎么解决

由于bean的生命周期中构造函数是第一个执行的,spring框架并不能解决构造函数的依赖注入,可以使用@Lazy懒加载,什么时候需要对象再进行bean对象的创建

22.SpringMVC的执行流程

1.用户发送出请求到前端控制器DispatcherSerclet,这是一个调度中心

2.DispatcherSerclet收到请求调用,HandlerMapping(处理器映射器)

3.HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有)DispatcherSerclet

4.DispatcherSerclet调用HandlerAdapter(处理器适配器)

5.HandlerAdaper经过适配调用具体的处理器(Handler/Controller)

6.Controller执行完成返回ModelAndView对象

7.HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherSerclet

8.DispatcherSerclet将ModelAndView传给ViewReslover(视图解析器)

9.ViewReslover解析后返回具体View(视图)

10.DispatcherSerclet根据View进行渲染视图(即将模型数据填充至视图中)

11.DispatcherSerclet响应用户

当然,现在的开发基本都是前后端分离开发的,并没有视图这些,一般都是用handler中说是使用Response直接返回结果

23.SpringBoot自动配置原理

在SpringBoot项目的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:

1.@SpringBootConfiguration

2.@EnableAutoConfiguration

3.ComponentScan

其中@EnableAutoConfiguration是实现自动化配置的核心注解

该注解通过@Import注解导入对应的配置选择器。关键的是内部读取了该项目和该项目引用的jar包的classpath路径下META-INF/spring.factories文件中锁配置的类的全类名

在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。一般条件判断会有像@ConditionalOnClass这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。

24.Spring框架常见的注解

第一类是:声明bean,有@Component、@Service、@Repository、@Controller

第二类是:依赖注入相关的,有@Autowired、@Qualifier、@Resource

第三类是:设置作用域@Scope

第四类是:spring配置相关的,比如@Configuration、@ComponentScan、@Bean

第五类是:跟aop相关做增强的注解@Aspect、@Before、@After、@Around、@Pointcut

25.springMVC常见的注解有哪些?

@ReaquestMapping:用于映射请求路径;

@RequestBody:注解实现接收http请求的json数据,将json转换成java对象;

@RequestParam:指定参数请求的名称;

@PathVariable:从请求路径下获取请求参数(/user/{id}),传递给方法的形式参数

@ResponseBody:注解实现将controller方法返回对象转换为json对象响应给客户端

@RequestHeader:获取指定的请求头数据,还有像@PostMapping、@GetMapping这些

26.SpringBoot常见的注解

SpringBoot的核心注解是@SpringBootApplication,它由几个注解组成:

1.@SpringBootConfiguration:组合了@Configuration注解,实现配置文件的功能

2.@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项

3.@ComponentScan:Spring组件扫描

27.MyBatis执行流程

1.读取MyBatis配置文件,mybatis-config.xml加载运行环境和映射文件

2.构造会话工厂SqlSessionFactory,一个项目只需要一个,单例的,一般由spring进行管理

3.会话工厂创建SqlSession对象,这里面就包含了执行SQL语句的所有方法

4.操作数据库的接口,Executor执行器,同时负责查询缓存的维护

5.Executor接口的执行方法中有一个MapperStatement类型的参数,封装了映射信息

6.输入参数映射

7.输出结果映射

延迟加载使用及原理

28.MyBatis是否支持延迟加载?

是支持的~,延迟加载的意思是:就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。

MyBatis支持一对一关联对象和一对多关联集合对象的延迟加载

在MyBatis配置文件中,可以配置是否启用延迟加载lazyLoadongEnable=true|false,默认是关闭的

29.延迟加载的底层原理知道吗?

延迟加载在底层主要使用的CGLIB动态代理完成的

第一是,使用CGLIB创建目标对象的代理对象,这里的目标对象就是开启了延迟加载的mapper

第二个是当调用目标方法时,进入拦截器invoke方法,发现目标方法是null值,再执行sql查询

第三个是获取数据以后,调用set方法设置属性值,再继续查询目标方法,就有值了

30.MyBatis的一级、二级缓存用过吗?

mybatis的一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Session进行flush或close之后,该Session中的所有Cache就被清空,默认打开一级缓存

关于二级缓存需要单独开启

二级缓存是基于namespace和mapper的作用域起作用的,不是依赖于SQL Session,默认也是采用PerpetualCache,HashMap存储。

如果想要开启二级缓存需要再全局配置文件和映射文件中开启配置才行。

31.MyBatis的二级缓存什么时候会清理缓存中的数据

当某一个作用域(一级缓存Session/二级缓存Namespace)进行了新增、修改、删除操作后,默认该作用域下所有select中的缓存被clear。

32.MySQL中,如何定位慢查询?

我们当时做压测的时候有的接口非常的慢,接口响应时间超过了2秒以上,因为我们当时的系统部署了运维的监控系统Skywalking,再展示的报表中可以看到哪一个接口比较慢,并且可以分析这个接口哪部分比较慢,这里可以看到SQL的具体的执行时间,所有可以定位是那个sql出了问题

如果,项目中没有这种运维的监控系统,其实在MySQL中也提供了慢日志查询的功能,可以在MySQL的系统配置文件中开启这个慢日志的功能,并且也可以设置SQL执行超过多少时间来记录到一个日志文件中,我记得上一个项目配置的是2秒,只要SQL执行的时间超过了2秒就会被记录到日志文件中,我们就可以在日志文件中找到执行比较慢的SQL了。

33.SQL语句执行很慢,如何优化/如何分析呢?

如果一条sql执行很慢的话,我们通常会使用mysql自动的执行计划explain去查看这条sql的执行情况,比如在这里可以通过key和key_len检查是否命中了索引,如果本身已经添加了索引,也可以判断索引是否有失效的情况,第二个,可以通过type字段查看sql是否有进一步的优化空间,是否存在全索引扫描,第三个可以通过extra建议来判断,是否出现了回表的情况,如果出现了,可以尝试添加索引或修改返回字段来修复

34.了解过索引吗?(什么是索引)

嗯,索引在项目中还是比较常见的,它是帮助MySQL高效获取数据的数据结构,主要是用来提高数据检索的效率,降低数据库的IO成本,同时通过索引列对数据进行排序,降低数据排序的成本,也能降低CPU的消耗

35.索引的底层数据结构了解过吗?

MySQL的默认的存储引擎InnoDB采用的B+树的数据结构来存储索引,选择B+树的主要原理是:第一阶数更多,路径更短,第二个磁盘读写代价B+树更低,非叶子节点只存储指针,叶子节点存储数据,第三是B+树便于扫库和区间查询,叶子节点是一个双向链表

36.B树和B+树的区别是什么?

第一:在B 树中,非叶子节点和叶子节点都会存放数据,而B+树的所有的数据都会出现在叶子节点,在查询的时候,B+树查找效率更加稳定

第二:在进行范围查询的时候,B+树效率更高,因为B+树都在叶子节点存储数据,并且叶子节点是一个双向链表

37.什么是聚族索引什么是非聚族索引?

聚族索引主要是指数据与索引放到一块,B+树的叶子节点保存了整行数据,有且只有一个,一般情况下主键在作为聚族索引非聚族索引指的是数据与索引分开存储,B+树的叶子节点保存对应的主键,可以有多个,一般我们自己定义的索引都是非聚族索引

38.知道什么是回表查询吗?

其实跟刚才介绍的聚族索引和非聚族索引是有关系的,回表的意思是通过二级索引找到对应的主键值,然后再通过主键值找到聚族索引中所对应的整行数据,这个过程就是回表

覆盖索引、超大分页优化

39.知道什么叫覆盖索引吗?

覆盖索引是指select查询语句使用了索引,再返回的列,必须在索引中全部能够找到,如果我们使用id查询,它会直接走聚集索引查询,一次索引扫描,直接返回

40.什么是反射?

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对下那个,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制

41.哪里用到反射机制?

1.JDBC中,利用反射动态加载了数据库驱动程序

2.Web服务器中利用反射调用了Sevlet的服务方法

3.Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。

4.很多框架都用到反射机制,注入属性,调用方法,入spring

42.反射机制的优缺点?

优点:可以动态执行,在运行期间根据业务功能动态执行方法、访问属性,最大限度发挥了java的灵活性

缺点:对性能有影响,这类操作总是慢于执行java代码

43.动态代理是什么?有哪些应用?

动态代理是运行时动态生成代理类

动态代理的应用有SPringleAOP数据查询、测试框架的后端mock、rpc、java注解对象获取等

44.怎么实现动态代理?

JDK原生动态代理和CGLIB动态代理

JDK原生动态代理是基于接口实现的,而CGLIB是基于继承当前类的子类实现的

45.怎么使用java的反射?

1)通过一个全限类名创建一个对象

Class.forName(“全限类名”)com.mysql.jdbc.Driver Driver类以及被加载到jvm中,并且完成了类的初始化工作就行了  类名.class

2)获取构造器对象,通过构造器new出一个对象

Clazz.getConstructor([String.class])

Con.newInstance([参数])

通过class对象创建一个实例对象(就相当于与new类名()无参构造器)

Cls.newwInstance();

3)通过class对象获得一个属性对象

Field c = cls.getFields():获得某个类的所有的公共的字段,包括父类中的字段

4)通过class对象获得一个方法对象

Cls.getMethod(“方法名”,class...parameaType);(只能获取公共的)

46.Python数据抓取怎么实现的?

Python数据抓取可以通过多种方法实现,其中包括正则表达式、B饿啊u提夫了Soup、

模块、lxml模块、Xpath选择器和CSS选择器等。其中,正则表达式是一种基于字符串匹配的方法,可以通过解析网页的HTML或XML代码来获取需要抓取的数据。lxml模块则是一种高效的XML

和HTML解析库,可以通过XPath或CSS选择器来定位需要抓取的数据。Xpath选择器则是一种基于XML和HTML文档结构的方法,可以通过类似于路径的方式来定位需要抓取的数据。总的来说,Python数据抓取的实现方法多种多样,可以根据情况选择最适合的方法来实现数据抓取。

47.JVM由那些部分组成、运行流程是什么?

在JVM中共有四部分,分别是ClassLoder(类加载器)、Runtime Data Area(运行时数据区,内存分区)、Excecution Engine(执行引擎)、Native Method Library(本地库接口)

他们的运行流程是:

第一,类加载器把java代码转换为字节码

第二,运行时数据区把字节码加载到内存中,而字节码文件只是JVM的一套指令集规范,并不能直接交给底层系统去执行,而是由执行引擎运行

第三,执行引擎将字节码翻译为底层系统指令,再交由cpu 去执行,此时需要调用其他语言的本地库接口来实现整个程序功能

48.你能详细说一下JVM运行时数据区吗?

运行时数据区包含了堆、方法区、栈、本地方法栈、程序计数器这几部分,每个功能作用都不一样

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

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

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

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

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

50.什么是程序计数器(你再详细介绍一下程序计数器的作用)

java虚拟机对于多线程是通过线程轮流切换并且分配线程执行时间。在任何是一个时间点上,一个处理器只会处理执行一个线程,如果当钱被执行的这个线程它所分配的执行时间用完了(挂起)处理器会切换到另外一个线程上来进行执行。并且这个线程的执行时间用完了,接着处理器就会又来执行被挂起的这个线程。这时候程序计数器就起到了关键作用,程序计数器在来回切换的线程中记录它上一次执行的行号,然后接着继续向下执行。

51.你能给我详细的介绍java堆吗?

java中的堆属于线程共享的区域。主要用来保存对象实例,数组等,当堆中没有内存空间可分配给实例,也无法再扩展时,则抛出OutOfMemoryError异常。

在Java8中堆内会存在年轻代、老年代

1.Young区被划分为三个部分,Eden区和两个大小严格相等的Survivor区,其中,Survivor区间中,某一时刻只有其中一个是被使用的,另外一个留垃圾收集时复制对象用。在Eden区变满的时候,GC就会将存活的对象移动到空闲的Survivor区间中,根据JVM的策略,在经过几次垃圾回收后,任然存活于Survivor的对象将被移动到Tenured区间

2.Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在Young复制转移一定的次数以后,对象就会被转移到Tenured区

52.jdk1.7和1.8的区别?

1.1.7中有一个永久代,存储的是类信息、静态变量、常量、编译后的代码

2.1.8移出了永久代,把数据存储到了本地内存的元空间中,防止内存溢出

53.什么是虚拟机栈?

虚拟机栈描述的是方法执行时的内存模型,是线程私有的,生命周期与线程相同,每个方法被执行的同时会创建栈帧。保存执行方法时的局部变量、动态连接信息、方法返回地址信息等等。方法开始执行的时候会进栈,方法执行完会出栈【相当于清空了数据】,所以这块区域不需要进行GC

54.能说一下堆栈的区别是什么吗?

第一,栈内存一般会用来存储局部变量和方法调用,但堆内存是用来存储java对象和数组的。堆会GC垃圾回收,而栈不会。

第二,栈内存是线程私有的,而堆内存是线程共有的

第三,两者异常错误不同,但如果栈内存或者堆内存不足都会抛出异常

栈空间不足:java.lang.StackOverFlowError

堆空间不足:java.lang.OutOfMemoryError

55.垃圾回收是否涉及栈内存?

垃圾回收主要指的就是堆内存,当栈帧弹栈以后,内存就会释放

56.栈内存分配越大越好吗?

未必,默认的栈内存通常为1024k,栈帧过大会导致线程数变少

57.方法内的局部变量是否线程安全?

1.如果方法内局部变量没有逃离方法的作用范围,它是线程安全的

2.如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

58.什么情况下会导致栈内存溢出?

1.栈帧过多会导致栈内存溢出,典型问题:递归调用

2.栈帧过大导致内存溢出

59.能不能介绍一下方法区?

1.方法区是各个线程共享的内存区域

2.主要存储类的信息、运行时常量池

3.虚拟机启动的时候创建,关闭虚拟机时释放

4.如果方法区域中的内存无法满足分配请求,则会抛出OutOfMemorError:Metaspace

60.介绍一下运行时常量池?

1.常量池:可以看作是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息

2.当类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

61.你听过直接内存吗?

1.并不属于JVM中的内存结构,不由JVM进行管理。是虚拟机的系统内存

2.常见于NIO操作时,用于数据缓冲区,分配回收成本较高,但读写性能高,不受JVM内存回收管理

62.什么是类加载器,类加载器有哪些?

JVM只会运行二进制文件,而类加载器(ClassLoader)的主要作用就是将字节码文件加载到JVM中,从而让java程序能够启动起来。

常见的类加载器有4个:

第一个是启动类加载器(BootStrap ClassLoader):其是由c++编写实现。用于加载JAVA_HOME/jre/lib目录下中的类库。

第二个是扩展类加载器(ExtClassLoader):该类是ClassLoader的子类,主要加载JAVA_HOME/jre/lib/ext目录中的类库

第三个是应用类加载器(AppClassLoader):该类是ClassLoader的子类,主要用于加载classPath下的类,也就是加载开发者自己编写的java类

第四个是自定义加载器:开发者自定义类继承ClassLoader,实现自定i有类加载规则。

63.什么是双亲委派模型?

如果一个类加载器收到了类加载器的请求,它首先不会自己尝试加载这个类,而是把这请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载器请求最终都应该传输到顶层的启动类加载器中,只有当父类加载器返回自己无法完成这个加载请求时,子类加载器才会尝试自己去加载

64.JVM为什么采用双清委派机制?

主要由两个原因:

第一,通过双清委派机制可以避免某一个类被重复加载,当父类已经加载后则无需重复加载,保证唯一性

第二,为了安全,保证类库API不会被修改

65.说一下类装载的执行过程?

类从加载到虚拟机中开始,直到卸载为止,它是整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载这7个阶段。其中,验证、准备和解析这三个部分统称为连接(linking)

1.加载:查找和导入class文件

2.验证:保证类的准确性

3.准备:为类变量分配内存并设置类变量初始值

4.解析:把类中的符号引用转换为直接引用

5.初始化:对类的静态变量,静态代码块执行初始化操作

6.使用:JVM开始从入口方法开始执行用户的程序代码

7.卸载:当用户程序代码执行完毕后,JVM便开始销毁创建的Class对象,最后负责运行的jvm也退出内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值