本文主要是介绍0527面试题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1.bean的生命周期:
生命周期:实例化-->数据装配-->初始化方法-->使用-->销毁方法-->从容器中销毁
2.工具类如何引入bean实例:
实际开发中,并不是所有类都需要交由IoC容器管理,某些类并不需要交由容器管理,如各种工具类,本身都提供了静态方法
-
问题:没有交由容器管理的类,容器是无法进行数据装配的,如何在这些类中获取容器中的bean呢?
-
解决:使用ApplicationContextAware获取容器,然后从容器中获取bean
定义IoC容器工具类,步骤:
-
定义一个类,实现ApplicationContextAware接口
-
将该类添加到IoC容器中,当实例化时会自动注入当前的ApplicationContext
-
调用IoC容器工具类,从容器中获取bean
3.基于构造器注入bean实例的好处:
//基于constructor注入(优点:可以为final属性注入、保证属性不为null、确保属性完成初始化),Spring官方推荐// @Autowired // 如果类中只有一个构造方法,可以省略该注解// public UserController(UserService userService) {// this.userService = userService;// }
4.AOP技术:
-
在不改变原有代码的基础上动态添加新的功能
-
模块化,将分散在各层中的相同代码,通过横向切割的方式抽取到单独的模块中
-
方便维护
-
可扩展性强
AOP的原理是使用动态代理技术
动态代理的含义:
-
代理类是在程序运行期间由JVM根据反射等机制动态生成的,自动生成代理类和代理对象
-
所谓动态是指在程序运行前不存在代理类的字节码文件,代理类和委托类的关系是在程序运行时确定
动态代理的两种技术:
-
jdk技术:适用于有接口时使用,目标对象必须实现一个或多个接口,否则无法使用jdk动态代理(Spring)
-
cglib技术:适用于无接口时使用(有接口时也可以使用)(SpringBoot)
注:Spring默认使用的是jdk动态代理,SpringBoot默认使用的是cglib动态代理
5.SpringMVC:
M:model 数据模型,封装了业务逻辑,对业务数据进行处理
V:view 视图,封装了显示逻辑,如HTML、JSP、Excel、PDF等
C:controller 控制器,控制整个网站的处理流程,协调视图与模型
好处:
简单,使用注解配置替代原生XML配置
效率高,单例的,将Controller层对象交给IoC容器管理
扩展性好,方便用户自定义
SpringMVC和Spring无缝衔接
常用注解:
@RequestParam 表示参数来源于请求参数,默认为所有参数添加该注解,参数值来源于同名称的请求参数
@PathVariable 表示参数来源于URL(URL就是请求路径)
@RequestHeader 表示参数来源于请求头
@CookieValue 表示参数来源于Cookie
@RequestBody 表示参数来源于请求体,用来接收前端传递给后端的 json 格式的数据
-
请求的方式必须为post,post请求才会有请求体
-
请求的内容类型必须为 json格式字符串,需要设置
contentType
为application/json;charset=utf8
6.全局异常处理
步骤:
-
定义一个异常处理类,添加@ControllerAdvice或@RestControllerAdvice
-
定义异常处理方法,添加@ExceptionHandler
SpringMVC为文件上传提供了支持,基于`commons-fileupload
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version> </dependency>
-
通过参数CommonsMultipartFile接收文件
-
必须在参数前添加@RequestParam注解,否则无法接收文件
7.SpringBoot是什么?
SpringBoot是一个用来简化Spring应用的初始创建和开发过程的框架,简化配置,实现快速开发
融合了整个Spring技术栈,JavaEE开发的一站式解决方案
优点:
-
快速创建独立运行的Spring项目并与主流框架集成
-
内置Servlet容器,应用无需打成WAR包
-
使用starter(启动器)管理依赖并进行版本控制
-
大量的自动配置,简化开发
-
无需配置XML,没有冗余代码生成,开箱即用
-
提供准生产环境的运行时监控,如指标、健康检查、外部配置等
@SpringBootApplication注解中包含了哪些注解:
-
@SpringBootConfiguration
标注在类上,表示这个类是SpringBoot的配置类
层级关系:@SpringBootConfiguration——>@Configuration——>@Component
@Configuration标注在类上,表示这个类是Spring的配置类,相当于是一个xml配置文件
-
@EnableAutoConfiguration
开启自动配置功能,SpringBoot会自动完成许多配置,简化了以前繁琐的配置
-
@ComponentScan
标注在类上,指定要扫描的包,默认只扫描主程序类所在的包及其子包
可以使用@ComponentScan手动指定要扫描的包
7.Redis是什么?
Redis是一个开源的高性能键值对(key-value)数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求。
Redis数据就是以key-value形式来存储数据:
-
key只能是字符串类型
-
value可以是以下五种类型:String、List、Set、Sorted-Sets、Hash
优点:
-
应用广泛,技术成熟,简单易用
-
基于内存,高性能(Redis读的速度是110000次/s,写的速度是81000次/s )
-
丰富的数据类型
-
数据持久化
-
支持主从备份和读写分离
-
支持集群
Redis的应用场景
-
数据缓存(数据查询、短连接、权限菜单、新闻内容、商品内容等)
-
分布式锁
-
消息队列/任务队列(秒杀、抢购、12306等)
-
分布式集群架构中的session分离
-
聊天室的在线好友列表
-
应用排行榜
-
网站访问统计
-
数据过期处理(可以精确到毫秒)
Redis的架构模式有以下几种:
-
单机模式(Standalone)
最简单的模式,不涉及数据同步,所以天然具备一致性。
单机Redis能够承载的QPS(每秒查询速率)大概在几万左右。
如果用户访问量太大,可能会导致redis直接挂掉。缺乏可靠性,单机有宕机的风险。
-
主从模式(Master-Slave)
也称为主从复制,允许根据一个Redis服务器来创建任意多个该服务器的复制品。
其中被复制的服务器为主服务器/节点(master),而通过复制创建出来的服务器复制品则为从服务器/节点(slave)。
特点:
-
主从结构:一个主节点可以有多个从节点,从节点可以有从节点,形成级联结构。
-
数据同步:主节点上的数据更新后,会自动将更新的数据同步到从节点,并且这种同步是异步进行的。
-
读写分离:主从模式可以实现读写分离,主节点主要负责写入操作,而从节点则主要负责读取操作。
-
数据备份:从节点可以作为主节点的备份节点,保存主节点的数据备份,提高数据的安全性和可靠性。
-
-
哨兵模式(Sentinel)
哨兵模式是在主从模式的基础上添加了故障检测和自动故障转移的功能。
在哨兵模式下,哨兵进程会监视主从节点的运行状态,当主节点出现故障时,会自动将其中一个从节点提升为新的主节点,并通知其他从节点修改配置文件,以将数据复制修改为主节点。这样可以实现自动的故障转移,无需人工干预,提高系统可用性和可扩展性。
哨兵模式不可以单独使用,它需要与主从模式一起使用。
-
集群模式(Cluster)
集群模式是为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器上。
在集群模式下,每个节点都负责存储数据的一部分,并且可以处理来自其他节点的请求。当一个节点出现故障时,其他节点可以接管它的职责,继续处理请求。
集群模式可以提高并发处理能力和数据容量,但实现复杂度较高,需要处理数据分片和节点间的通信等问题。
8.Sentinel服务熔断和服务降级
服务雪崩:在微服务架构中服务之间会相互调用和依赖,如果某个服务发生故障,可能会导致多个服务故障,从而导致整个系统故障
解决服务雪崩的方式:
-
服务熔断
当服务出现不可用或响应超时时,为了防止整个系统出现雪崩, 暂时停止对该服务的调用,直接返回一个结果,快速释放资源。
如果检测到目标服务情况好转,则恢复对目标服务的调用。
-
服务降级
为了防止核心业务功能出现负荷过载或者响应慢的问题,将非核心服务进行降级,暂时性的关闭或延迟使用,保证核心服务的正常运行
9.Redis的持久化方式:
Redis提供了两种数据持久化的方式:
-
RDB(Redis Database)
该机制是指在指定的时间间隔内将内存中的数据集快照写入磁盘(默认)。
-
缺点:可能会丢失数据
-
优点:效率比较高
-
-
AOF(Append Only File)
该机制将以日志的形式记录服务器所处理的每一个写操作。
-
缺点:效率比较差
-
优点:丢失数据量比较少
-
在Redis服务器启动之初会读取文件来重新构建数据库,以保证启动后数据库中的数据是完整的。
10.常用集合的分类:
Collection 接口的接口 对象的集合(单列集合) ├—— List 接口:元素按进入先后有序保存,可重复 │————— LinkedList │————— ArrayList │————— Vector 接口实现类 数组, 同步, 线程安全 │ ——————— Stack 是Vector类的实现类 └—— Set 接口: 仅接收一次,不可重复,并做内部排序 ├————— HashSet 使用hash表(数组)存储元素 │———————— LinkedHashSet 链表维护元素的插入次序 └ —————-TreeSet 底层实现为二叉树,元素排好序
Map 接口 键值对的集合 (双列集合) ├——— Hashtable 接口实现类, 同步, 线程安全 ├——— HashMap 接口实现类 ,没有同步, 线程不安全- │—————– LinkedHashMap 双向链表和哈希表实现 │—————– WeakHashMap ├ ——– TreeMap 红黑树对所有的key进行排序 └——— IdentifyHashMap
11.ArrayList的底层实现原理,以及扩容机制?
ArrayList以数组作为底层。
-
当使用无参构造创建集合时,直接赋值一个空的数组;创建集合时也可以指定集合大小,或者将另一个集合作为参数传入;
-
当使用无参构造第一次添加元素时,会扩容数组为默认给定数组大小为10,第一次扩容;
-
当数组满后再次添加元素就会扩容;(扩容临界点)
-
每次扩容为扩容前的大小+扩容前大小*0.5,相当于扩容后的数组是扩容前数组的1.5倍;(扩容因子)
[经典面试题]:当使用无参构造创建集合时,向里面添加了11个元素后,此时数组扩容了几次?
两次。因为使用无参构造创建集合后,第一次添加元素时会默认创建大小为10的数组,然后当添加元素满10个后再次添加元素时,再次扩容到之前数组的1.5倍。
12.LinkedList的底层实现原理,以及扩容机制?
LinkedList以链表作为底层。
使用无参构造创建集合后,第一次添加元素会将该元素记录为first节点和last节点;
当不是第一次添加元素且集合中有元素时,会默认将元素添加到最后位置;
当插入元素时,会先判断插入的元素是否为最后一个,是最后一个则直接调用addLast,若不是则底层会先得到插入之前该位置的节点,然后创建新节点(插入的元素节点),新节点的上一节点地址记录插入之前该位置的节点的上一节点地址,新节点的下一节点地址记录插入之前该位置的节点;然后将插入之前该位置的节点的上一节点地址改为新节点;将插入之前该位置的节点的上一节点的下一节点地址改为新节点;(简单来说就是将插入位置的上一节点地址记录新的节点,将插入位置的上一个节点的下一节点地址记录新的节点,新节点的上一节点地址记录插入前该位置的上一节点地址,下一节点地址记录插入前该位置节点。)
当删除节点时,会先判断删除节点是否为第一个或最后一个,若是第一个或者最后一个就只需要将first或last记录删除节点的下一节点或上一节点即可;若不是第一个则会将删除节点的上一节点地址所记录的下一节点地址改为删除节点的下一节点地址,将删除节点的下一节点地址所记录的上一节点地址改为删除节点的上一节点地址,让链表都不记录删除的节点地址,然后后将删除的节点置为空,便于GC垃圾回收。
13.HashMap的底层实现原理,以及扩容机制?
1、用HashMap存储数据( put(key,value) )时,会先操作key调用.hashcode()方法得出hash值,然后再通过哈希算法转换成数组的一个下标,对应的就是在数组上的的存储位置。
2、如果该位置没有数据,则直接存储。如果该位置有数据,则会发生数据碰撞。
3、数据碰撞的时候遍历该位置上链表中的所有数据,并且通过equals()方法来比对每个数据的key。如果key相同的话,会将链表上该位置的数据进行覆盖。如果key不相同的话,在JDK1.8之前是实行的头插法,数据存储在链表头部,1.8之后实行的是尾插法数据存储在链表尾部。
4、JDK1.8之后,当链表上的节点个数(数据个数)大于等于8时并且数组长度不小于64的时候,链表数据结构自动进行树化转化成红黑树,当链表上的数据小于8个时,又会自动退化成链表
5、如果数组的长度小于64的话链表数据个数达到了8的话也不会转化成红黑树,而是先进行扩容,直到数组长度达到64
6、HashMap默认的数组长度是16,扩容的话有两种情况: 1)一种是数组上的元素达到了阈值,16*默认负载因子0.75,也就是12个元素的时候,数组长度扩容为两倍32,阈值变为24 2)还有一种是在没有红黑树的情况下,添加元素后数组中某个链表的长度超过了8,数组会扩容为两倍(比如创建HashMap集合后刚开始添加元素全都在一个链表中,当链表长度是9的时候数组扩容成32,链表长度是10的时候数组扩容成64,此时再添加元素,满足了数组长度为64链表长度到达8的两个条件,链表转换成红黑树)
14.hashMap和hashTable区别
共同点:
都是实现了Map接口;
异同点:
hashMap允许键值为空,hashTable不允许键值为空;
hashMap不是安全的,hashTable是线程安全的;
hashMap继承自AbstractMap,HashTable继承自己背废弃的Dictionary;
15.Vue的生命周期:
Vue生命周期总结(四个阶段,八个钩子函数)-CSDN博客
vue生命周期分为四个阶段 第一阶段(创建阶段):beforeCreate,created 第二阶段(挂载阶段):beforeMount(render),mounted 第三阶段(更新阶段):beforeUpdate,updated 第四阶段(销毁阶段):beforeDestroy,destroyed
16.v-if 和 v-show 的区别:
-
v-if:通过动态地向DOM树添加或删除元素来控制元素的显示与隐藏。当表达式结果为false时,对应的DOM元素会被完全移除,即在DOM树中不存在该元素。
-
v-show:通过设置CSS的display属性为none来隐藏元素,而元素本身依然存在于DOM树中。当表达式结果为false时,元素仍然在页面上,只是被隐藏起来。
17.什么是分布式锁:
-
分布式应用中所有线程都去获取同一把锁,但只有一个线程可以成功的获得锁,其他没有获得锁的线程必须全部等待,直到持有锁的线程释放锁。
-
分布式锁是可以跨越多个tomcat实例,多个JVM进程的锁,所以分布式锁都是设计在第三方组件中的
-
分布式锁都是通过第三方组件来实现的,目前主流的解决方案是使用Redis或Zookeeper来实现分布式锁
使用Redisson实现流程:
-
只要线程加锁成功(默认锁的超时时间为30s),Redisson就会启动一个用于监控锁的看门狗,它是一个守护线程,会每隔10秒检查一下,如果线程还持有锁,就会不断的延长锁的有效期(即每到20s就会自动续借成30s),也称为自动续期机制
-
当业务执行完,释放锁后,会关闭守护线程。
-
从而防止了线程业务还没执行完,而锁却过期的问题 。
18.java中如何实现序列化,有什么意义?
-
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
-
序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)
19.Java中异常分为哪些,具体列举:
Java中的异常主要分为两大类:检查型异常(Checked Exception)和运行时异常(Runtime Exception)。
检查型异常(Checked Exception):
-
FileNotFoundException:当试图打开指定路径名表示的文件失败时抛出。
-
ClassNotFoundException: 类没找到时抛出。
-
SQLException:处理数据库操作过程中发生错误时抛出。
-
IOException:在输入输出操作失败或中断时抛出。
-
EOFException:表示输入过程中意外到达文件或流的末尾。
-
NumberFormatException:当应用程序试图将字符串转换成一种数值类型但该字符串不能转换为适当格式时抛出。
运行时异常(Runtime Exception):
-
NullPointerException:当应用程序试图在需要对象的地方使用 null 时,抛出该异常。
-
ArrayIndexOutOfBoundsException:用非法索引访问数组时抛出。
-
ClassCastException:当试图将对象强制转换为不是实例的子类时抛出。
-
ArithmeticException:当出现异常的算术条件时抛出,如除以零。
-
NegativeArraySizeException:如果应用程序试图创建大小为负的数组,则抛出。
20.多线程的优缺点:
优点:提高CPU的利用率、提高响应性、共享资源、性能提升 缺点:数据不一致、循环等待、产生死锁、并行处理任务
21.ThreadLocal
ThreadLocal是Java中一种特殊的线程绑定机制,它能够为每个线程存储一个独立的变量副本,从而实现线程间的数据隔离。下面将详细探讨ThreadLocal的各个方面:
-
线程本地变量:ThreadLocal为每个使用它的线程提供独立的变量副本,这些变量仅在当前线程内可见。
-
线程安全:由于每个线程拥有自己的变量副本,因此不存在多线程竞争访问的问题,确保了线程安全性。
-
无需同步:使用ThreadLocal避免了使用同步机制(如synchronized),减少了锁的开销。
22.线程同步和线程异步
1、同步就是指一个线程要等待上一个线程执行完之后才开始执行当前的线程。
2、异步是指一个线程去执行,它的下一个线程不必等待它执行完就开始执行。
23.Java线程常用的方法有哪些?
-
start():此方法是启动线程的关键,当调用一个线程的
start()
方法时,系统会为该线程分配CPU时间片,并执行其run()
方法中的代码。这是新线程开始执行的必要条件。 -
run():这是每个线程都必须实现的方法,它包含了线程执行时要完成的任务。需要注意的是,直接调用
run()
方法并不会启动新的线程,而只是在当前线程中执行run()
方法的代码。 -
setName(String name):此方法用于设置线程的名称,便于识别和调试线程。
-
setPriority(int priority):此方法用于设置线程的优先级。Java中的线程优先级范围是从1到10,其中1表示最低优先级,10表示最高优先级。默认优先级为5。
-
setDaemon(boolean on):此方法用于将线程设置为守护线程。当所有的用户线程都结束时,守护线程会自动退出。
-
join(long millisec):此方法用于让当前线程等待,直到指定的线程终止为止。这通常用于同步线程执行的顺序。
-
interrupt():此方法用于中断线程。当调用一个线程的
interrupt()
方法时,会设置该线程的中断状态,并可能抛出InterruptedException
异常。 -
isAlive():此方法用于检测线程是否处于活动状态,即线程是否已经启动且尚未结束。
-
yield():此方法用于让当前线程释放CPU控制权,使其他同优先级的线程有机会执行。这是一个“礼让”机制,并不保证线程将被挂起或恢复执行。
-
sleep(long millisec):此方法用于让当前线程暂停执行指定的毫秒数。与
yield()
不同,sleep()
会让线程进入阻塞状态,直到指定的时间结束或者被中断。 -
currentThread():此静态方法用于获取当前正在执行的线程对象的引用。
24.悲观锁和乐观锁:
-
悲观锁
悲观锁是一种悲观思想,它认为数据很可能会被别人所修改
所以总会对数据进行上锁,读操作和写操作都会上锁,性能较低,使用较少!
-
乐观锁
乐观锁是一种乐观思想,它认为数据并不一定会被别人所修改
所以读操作不会上锁,但写操作时会先判断当前数据是否被修改过(一般采用版本号机制来实现 )
25.公平锁和非公平锁:
-
公平锁
-
定义与原理:公平锁(Fair lock)确保等待时间最长的线程会优先获得锁的机会,这避免了线程饥饿的问题。
-
优点:公平锁保证了线程获取资源的公平性,有助于维护系统的稳定性和可预测性。
-
缺点:由于需要维护一个等待队列,并在每次释放锁时唤醒所有等待的线程,公平锁的性能开销较大。
-
应用场景:适用于需要保证线程获取资源公平性的场景,例如在资源竞争激烈且需要避免线程饥饿的情况下。
-
-
非公平锁
-
定义与原理:非公平锁(Non-fair lock)允许新到达的线程尝试获取锁,即使有其他线程正在等待,这可能导致某些线程等待时间较长。
-
优点:非公平锁不需要维护等待队列的顺序,因而它的性能通常比公平锁要好。
-
缺点:非公平锁不保证线程获取资源的公平性,可能会出现线程饥饿的情况。
-
应用场景:适用于对性能要求较高且竞争不是非常激烈的场景。
-
26.WebSocket:
WebSocket 是基于 TCP 的一种新的网络协议。
-
它实现了浏览器与服务器全双工通信
-
浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输。
HTTP协议和WebSocket协议对比:
-
HTTP是短连接;WebSocket是长连接
-
HTTP通信是单向的,基于请求响应模式;WebSocket支持双向通信
27.SpringCloud自动刷新:
@RefreshScope // nacos配置自动刷新
这篇关于0527面试题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!