文章目录
- 前言
- 面经3
- 能说一下HashMap的数据结构吗?
- HashMap的put流程知道吗?
- HashMap 的 get 方法的执行过程?
- 为什么HashMap的容量是2的倍数呢?
- 你还知道哪些哈希函数的构造方法呢?
- 解决哈希冲突有哪些方法呢?
- ConcurrentHashMap 的实现原理是什么?
- HashSet 的实现原理?
- LinkedHashMap 的实现原理?
- Spring 中应用了哪些设计模式呢?
- 说一说什么是 IOC?什么是 DI?
- 说说 BeanFactory 和 ApplicantContext?
- 你知道 Spring 容器启动阶段会干什么吗?
- 能说一下 Spring Bean 生命周期吗?
- 单例 Bean 线程安全问题怎么解决呢?
- 那 Spring 怎么解决循环依赖的呢?
- 为什么要三级缓存?⼆级不⾏吗?
- 说说什么是 AOP?
- 说说 JDK 动态代理和 CGLIB 代理 ?
- Spring 事务的种类?
- 介绍一下 SpringBoot,有哪些优点?
- SpringBoot 自动配置原理了解吗?
- Springboot 启动原理?
- 全部系列
前言
记录收集到的常用Java面试题(系列3)
面经3
能说一下HashMap的数据结构吗?
JDK1.8的数据结构是数组+链表+红黑树。
其中,桶数组是用来存储数据元素,链表是用来解决冲突,红黑树是为了提高查询的效率。
数据元素通过映射关系,也就是散列函数,映射到桶数组对应索引的位置
如果发生冲突,从冲突的位置拉一个链表,插入冲突的元素
如果链表长度>8&数组大小>=64,链表转为红黑树
如果红黑树节点个数<6 ,转为链表
HashMap的put流程知道吗?
当我们想往一个HashMap中添加一对key-value时,系统首先会计算key的hash值,然后根据hash值确认在table中存储的位置。若该位置没有元素,则直接插入。否则迭代该处元素链表并依次比较其 key 的 hash 值如果两个 hash 值相等且 key 值相等,则用新的 Entry 的 value 覆盖原来节点的 value。如果两个 hash 值相等但 key 值不等 ,则进行插入操作。
HashMap 的 get 方法的执行过程?
通过 key 的 hash 值找到在 table 数组中的索引处的 Entry,然后返回该 key 对应的 value 即可。
为什么HashMap的容量是2的倍数呢?
第一个原因是为了方便哈希取余:
第二个方面是在扩容时,利用扩容后的大小也是2的倍数,将已经产生hash碰撞的元素完美的转移到新的table中去
你还知道哪些哈希函数的构造方法呢?
直接定址法
直接根据key来映射到对应的数组位置,例如1232放到下标1232的位置。
数字分析法
取key的某些数字(例如十位和百位)作为映射的位置
平方取中法
取key平方的中间几位作为映射的位置
折叠法
将key分割成位数相同的几段,然后把它们的叠加和作为映射的位置
解决哈希冲突有哪些方法呢?
链地址法:在冲突的位置拉一个链表,把冲突的元素放进去。
开放定址法:开放定址法就是从冲突的位置再接着往下找,给冲突元素找个空位。
再哈希法:换种哈希函数,重新计算冲突元素的地址。
建立公共溢出区:再建一个数组,把冲突的元素放进去。
ConcurrentHashMap 的实现原理是什么?
JDK 8:中 ConcurrentHashMap 参考了 JDK 8 HashMap 的实现,采用了数组 + 链表 + 红黑树的实现方式来设计,内部大量采用 CAS 操作。
HashSet 的实现原理?
HashSet 的实现是依赖于 HashMap 的,HashSet 的值都是存储在 HashMap 中的。在 HashSet 的构造法中会初始化一个 HashMap 对象,HashSet 不允许值重复。因此,HashSet 的值是作为 HashMap 的 key 存储在 HashMap 中的,当存储的值已经存在时返回 false。
LinkedHashMap 的实现原理?
LinkedHashMap 也是基于 HashMap 实现的,不同的是它定义了一个 Entry header,这个 header 不是放在 Table 里,它是额外独立出来的。LinkedHashMap 通过继承 hashMap 中的 Entry,并添加两个属性 Entry before,after 和 header 结合起来组成一个双向链表,来实现按插入顺序或访问顺序排序。
LinkedHashMap 定义了排序模式 accessOrder,该属性为 boolean 型变量,对于访问顺序,为 true;对于插入顺序,则为 false。一般情况下,不必指定排序模式,其迭代顺序即为默认为插入顺序。
Spring 中应用了哪些设计模式呢?
工厂模式 : Spring 容器本质是一个大工厂,使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
代理模式 : Spring AOP 功能功能就是通过代理模式来实现的,分为动态代理和静态代理。
单例模式 : Spring 中的 Bean 默认都是单例的,这样有利于容器对 Bean 的管理。
模板模式 : Spring 中 JdbcTemplate、RestTemplate 等以 Template 结尾的对数据库、网络等等进行操作的模板类,就使用到了模板模式。
观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
适配器模式 :Spring AOP 的增强或通知 (Advice) 使用到了适配器模式、Spring MVC 中也是用到了适配器模式适配 Controller。
策略模式:Spring 中有一个 Resource 接口,它的不同实现类,会根据不同的策略去访问资源。
说一说什么是 IOC?什么是 DI?
所谓的IOC(控制反转):就是由容器来负责控制对象的生命周期和对象间的关系。以前是我们想要什么,就自己创建什么,现在是我们需要什么,容器就给我们送来什么。也就是说,控制对象生命周期的不再是引用它的对象,而是容器。对具体对象,以前是它控制其它对象,现在所有对象都被容器控制,所以这就叫控制反转。
DI(依赖注入):指的是容器在实例化对象的时候把它依赖的类注入给它。有的说法 IOC 和 DI 是一回事,有的说法是 IOC 是思想,DI 是 IOC 的实现。
为什么要使用 IOC 呢? 最主要的是两个字解耦,硬编码会造成对象间的过度耦合,使用 IOC 之后,我们可以不用关心对象间的依赖,专心开发应用就行。
说说 BeanFactory 和 ApplicantContext?
BeanFactory(Bean 工厂)是 Spring 框架的基础设施,面向 Spring 本身。 BeanFactory 是类的通用工厂,可以创建并管理各种类的对象。
ApplicantContext(应用上下文)建立在 BeanFactoty 基础上,面向使用 Spring 框架的开发者。ApplicationContext 由 BeanFactory 派生而来,提供了更多面向实际应用的功能。可以这么说,使用 BeanFactory 就是手动档,使用 ApplicationContext 就是自动档。
你知道 Spring 容器启动阶段会干什么吗?
Spring 的 IOC 容器工作的过程,其实可以划分为两个阶段:容器启动阶段和Bean 实例化阶段。
其中容器启动阶段主要做的工作是加载和解析配置文件,保存到对应的 Bean 定义中。
能说一下 Spring Bean 生命周期吗?
Spring IOC 中 Bean 的生命周期大致分为四个阶段:实例化(Instantiation)、属性赋值(Populate)、初始化(Initialization)、销毁(Destruction)。
单例 Bean 线程安全问题怎么解决呢?
将 Bean 中的成员变量保存在 ThreadLocal 中
那 Spring 怎么解决循环依赖的呢?
注入就发生在第二步,属性赋值,结合这个过程,Spring 通过三级缓存解决了循环依赖:
一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例
二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例
三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。
为什么要三级缓存?⼆级不⾏吗?
不行,主要是为了⽣成代理对象。如果是没有代理的情况下,使用二级缓存解决循环依赖也是 OK 的。但是如果存在代理,三级没有问题,二级就不行了。
因为三级缓存中放的是⽣成具体对象的匿名内部类,获取 Object 的时候,它可以⽣成代理对象,也可以返回普通对象。使⽤三级缓存主要是为了保证不管什么时候使⽤的都是⼀个对象。
假设只有⼆级缓存的情况,往⼆级缓存中放的显示⼀个普通的 Bean 对象,Bean 初始化过程中,通过 BeanPostProcessor 去⽣成代理对象之后,覆盖掉⼆级缓存中的普通 Bean 对象,那么可能就导致取到的 Bean 对象不一致了。
说说什么是 AOP?
AOP:面向切面编程。简单说,就是把一些业务逻辑中的相同的代码抽取到一个独立的模块中,让业务逻辑更加清爽。
AOP 有哪些环绕方式?
AOP 一般有 5 种环绕方式:
前置通知 (@Before)
返回通知 (@AfterReturning)
异常通知 (@AfterThrowing)
后置通知 (@After)
环绕通知 (@Around)
说说 JDK 动态代理和 CGLIB 代理 ?
JDK 动态代理
Interface:对于 JDK 动态代理,目标类需要实现一个 Interface。
InvocationHandler:InvocationHandler是一个接口,可以通过实现这个接口,定义横切逻辑,再通过反射机制(invoke)调用目标类的代码,在次过程,可能包装逻辑,对目标方法进行前置后置处理。
Proxy:Proxy 利用 InvocationHandler 动态创建一个符合目标类实现的接口的实例,生成目标类的代理对象。
CgLib 创建的动态代理对象性能比 JDK 创建的动态代理对象的性能高不少,但是 CGLib 在创建代理对象时所花费的时间却比 JDK 多得多,所以对于单例的对象,因为无需频繁创建对象,用 CGLib 合适,反之,使用 JDK 方式要更为合适一些。同时,由于 CGLib 由于是采用动态创建子类的方法,对于 final 方法,无法进行代理。
Spring 事务的种类?
Spring 支持编程式事务管理和声明式事务管理两种方式:
编程式事务
编程式事务管理使用 TransactionTemplate,需要显式执行事务。
声明式事务
声明式事务管理建立在 AOP 之上的。其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
优点是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过 @Transactional 注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
介绍一下 SpringBoot,有哪些优点?
Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。
SpringBoot 自动配置原理了解吗?
EnableAutoConfiguration 只是一个简单的注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector类
AutoConfigurationImportSelector实现了ImportSelector接口,这个接口的作用就是收集需要导入的配置类,配合@Import()就可以将相应的类导入到 Spring 容器中
获取注入类的方法是 selectImports(),它实际调用的是getAutoConfigurationEntry
Springboot 启动原理?
全部系列
面经1:https://blog.csdn.net/Oliver__Twist/article/details/125459141?spm=1001.2014.3001.5501
面经2:https://blog.csdn.net/Oliver__Twist/article/details/125461522?spm=1001.2014.3001.5501
面经3:https://blog.csdn.net/Oliver__Twist/article/details/125461610?spm=1001.2014.3001.5501
面经4:https://blog.csdn.net/Oliver__Twist/article/details/125461632?spm=1001.2014.3001.5501
面经5:https://blog.csdn.net/Oliver__Twist/article/details/125461655?spm=1001.2014.3001.5501