Java后端面试八股文2024完整版(高频+源码+算法)

1.什么是Spring?

Spring是个轻量级的框架,通过IOC达到松耦合的目的,通过AOP可以分离应用业务逻辑和系统服务进行内聚性的开发,不过配置各种组件时比较繁琐,所以后面才出选了SpringBoot的框架。 

2.IOC是什么?

IOC是控制反转,是一种思想,把对象的创建和调用从程序员手中交由IOC容器管理,降低对象之间的依赖关系。

创建一个bean的方式有xml方式、@Bean注解方式、@Componte方式

我们在对一个bean进行实例化后,要对他的属性进行填充,大多数我们都是使用 @Autowire直接的填充依赖注入的,他是有限按照类型进行匹配。

3.AOP是什么? 

AOP是面向切面编程,可以将那些与业务不相关但是很多业务都要调用的代码抽取出来,思想就是不侵入原有代码的情况下对功能进行增强。

SpringAOP是基于动态代理实现的,动态代理是有两种,一种是jdk动态代理,一种是cglib动态代理;

jdk动态代理是原理是利用反射来实现的,需要调用反射包下的Proxy类的newProxyInstance方法来返回代理对象,这个方法中有三个参数,分别是用于加载代理类的类加载器,被代理类实现的接口的class数组和一个用于增强方法的InvocaHandler实现类。

cglib动态代理原理是利用asm开源包来实现的,是把被代理类的class文件加载进来,通过修改它的字节码生成子类来处理

jdk动态代理要求被代理类必须有实现的接口,生成的动态代理类会和代理类实现同样的接口,cglib则,生成的动态代理类会继承被代理类。Spring默认使用jdk动态代理,当被代理的类没有接口时就使用cglib动态代理

4.如何定义一个全局异常处理类?

想要定义一个全局异常处理类的话,我们需要在这个类上添加@ContaollerAdvice注解,然后定义一些用于捕捉不同异常类型的方法,在这些方法上添加@ExceptionHandler(value = 异常类型.class)和@ResponseBody注解,方法参数是HttpServletRequest和异常类型,然后将异常消息进行处理。

如果我们需要自定义异常的话,就写一个自定义异常类,该类需要继承一个异常接口,类属性包括final类型的连续id、错误码、错误信息,再根据需求写构造方法; 

5.如何使用aop自定义日志?

第一步:创建一个切面类,把它添加到ioc容器中并添加@Aspect注解

第二步: 在切面类中写一个通知方法,在方法上添加通知注解并通过切入点表达式来表示要对哪些方法进行日志打印,然后方法参数为JoinPoint

第三步:通过JoinPoint这个参数可以获取当前执行的方法名、方法参数等信息,这样就可以根据需求在方法进入或结束时打印日志

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记及答案【点击此处即可】免费获取

6.循环依赖是什么,怎么解决的?

循环依赖就是在创建 A 实例的时候里面包含着 B 属性实例,所以这个时候就需要去创建 B 实例,而创 建 B 实例过程中也包含着 A 实例。 这样 A 实例还在创建的过程当中,所以就导致 A 和 B 实例都创建不出来。

spring通过三级缓存来解决循环依赖:

一级缓存:单例池,缓存经过了已经初始化完毕的Bean

二级缓存 :半成品池,缓存还未初始化完毕的Bean

三级缓存:缓存的是获取Bean的代理对象的表达式

我们在创建 A 的过程中,先将 A 放入三级缓存 ,这时要创建B,B要创建A就直接去三级缓存中查找,并且判断需不需要进行 AOP 处理,如果需要就在三级缓存中获取A的代理对象,不需要就取A原始对象。然后将取出的对象放入二级缓存中,这个时候其他需要依赖 A 对象的直接从二级缓存中去获取即可。当B初始化完成进入一级缓存后,A 继续执行生命周期,当A完成了属性的注入后,就可以放入一级缓存了

spring2.6之前默认会解决循环依赖。在spring2.6之后需要通过配置开启解决循环依赖

7.Bean 的作用域

(1)Singleton:一个IOC容器只有一个

(2)Prototype:每次调用getBean()都会生成一个新的对象

(3)request:每个http请求都会创建一个自己的bean

(4)session:同一个session共享一个实例

(5)application:整个serverContext只有一个bean

(6)webSocket:一个websocket只有一个bean

8.Bean 生命周期

实例化 Instantiation->属性赋值 Populate->初始化 Initialization->销毁 Destruction
在这四步的基础上面,Spring 提供了一些拓展点:

*Bean 自身的方法: 包括了 Bean 本身调用的方法和通过配置文件中的 init-method 和 destroy-method 指定的方法
*Bean 级生命周期接口方法:包括了 BeanNameAware、BeanFactoryAware、InitializingBean 和 DiposableBean 这些接口的方法
*容器级生命周期接口方法:包括了 InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。
*工厂后处理器接口方法: 包括了 AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer 等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。

9.Spring 事务原理?

spring事务有编程式和声明式,我们一般使用声明式,在某个方法上增加@Transactional注解,这个方法中的sql会统一成功或失败。

原理是:

当一个方法加上@Transactional注解,spring会基于这个类生成一个代理对象并将这个代理对象作为bean,当使用这个bean中的方法时,如果存在@Transactional注解,就会将事务自动提交设为false,然后执行方法,执行过程没有异常则提交,有异常则回滚、

10.spring事务失效场景

(1)事务方法所在的类没有加载到容器中

(2)事务方法不是public类型

(3)同一类中,一个没有添加事务的方法调用另外以一个添加事务的方法,事务不生效

(4)spring事务默认只回滚运行时异常,可以用rollbackfor属性设置

(5)业务自己捕获了异常,事务会认为程序正常秩序

11.spring事务的隔离级别

default:默认级别,使用数据库自定义的隔离级别

其它四种隔离级别与mysql一样

12.spring事务的传播行为

(1)支持当前事务,如果不存在,则新启一个事务

(2)支持当前事务,如果不存在,则抛出异常

(3)支持当前事务,如果不存在,则以非事务方式执行

(4)不支持当前事务,创建一个新事物

(5)不支持当前事务,如果已存在事务就抛异常

(6)不支持当前事务,始终以非事务方式执行

13.Spring IoC

20200904160557786.png

12.spring用了哪些设计模式

BeanFactory用了工厂模式,AOP用了动态代理模式,RestTemplate用来模板方法模式,SpringMVC中handlerAdaper用来适配器模式,Spring里的监听器用了观察者模式

14.SpringMV工作原理

SpringMVC工作过程围绕着前端控制器DispatchServerlet,几个重要组件有HandleMapping(处理器映射器)、HandleAdapter(处理器适配器)、ViewReslover(试图解析器)

工作流程:

(1)DispatchServerlet接收用户请求将请求发送给HandleMapping

 (2)HandleMapping根据请求url找到具体的handle和拦截器,返回给DispatchServerlet

(3)DispatchServerlet调用HandleAdapter,HandleAdapter执行具体的controller,并将controller返回的ModelAndView返回给DispatchServler

(4)DispatchServerlet将ModelAndView传给ViewReslover,ViewReslover解析后返回具体view

(5)DispatchServerlet根据view进行视图渲染,返回给用户

15.springboot自动配置原理

在spring—boot—autoconfigura包下存放了spring内置的自动配置类和spring.factories文件,这个文件中存放了这些配置类的全类名 ;

启动类@SpringbootApplication注解下,有三个关键注解

(1)@springbootConfiguration:表示启动类是一个自动配置类

(2)@CompontScan:扫描启动类所在包下及子包的组件到容器中

(3)@EnableConfigutarion,下面有个子注解@Import会导入上面所说的自动配置类,这些配置类会根据元注解的装配条件生效,生效的类就会被实例化,加载到ioc容器中;这些自动配置类还会通过xxxProperties文件里配置来进行属性设值

16 .springboot常用注解

@RestController                  :修饰类,该控制器会返回Json数据 

@RequestMapping("/path") :修饰类,该控制器的请求路径

@Autowired                         :  修饰属性,按照类型进行依赖注入

@PathVariable                     :  修饰参数,将路径值映射到参数上

@ResponseBody                 :修饰方法,该方法会返回Json数据

@RequestBody(需要使用Post提交方式) :修饰参数,将Json数据封装到对应参数中
@Controller@Service@Compont:  将类注册到ioc容器

@Transaction:开启事务

17.spring的bean是线程安全的吗?

spring的默认bean作用域是单例的,单例的bean不是线程安全的,但是开发中大部分的bean都是无状态的,不具备存储功能,比如controller、service、dao,他们不需要保证线程安全。

如果要保证线程安全,可以将bean的作用域改为prototype,比如像Model View。

另外还可以采用ThreadLocal来解决线程安全问题。ThreadLocal为每个线程保存一个副本变量,每个线程只操作自己的副本变量。

18.springcloud主要解决什么问题?

解决服务之间的通信、容灾、负载平衡、冗余问题,能方便服务集中管理,常用组件有注册中心、配置中心、远程调用。服务熔断、网关

19.CAP理论

C:一致性,这里指的强一致性,也就是数据更新完,访问任何节点看到的数据完全一致

A:可用性,就是任何没有发生故障的服务必须在规定时间内返回合正确结果

P:容灾性,当网络不稳定时节点之间无法通信,造成分区,这时要保证系统可以继续正常服务。提高容灾性的办法就是把数据分配到每一个节点当中,所以P是分布式系统必须实现的,然后需要在C和A中取舍

20.为什么不能同时保证一致性和可用性呢?

当网络发生故障时,如果要保障数据一致性,那么节点相互间就只能阻塞等待数据真正同步时再返回,就违背可用性了。如果要保证可用性,节点要在有限时间内将结果返回,无法等待其它节点的更新消息,此时返回的数据可能就不是最新数据,就违背了一致性了

21.熔断限流的理解? 

SprngCloud中用Hystrix组件来进行降级、熔断、限流

熔断是对于消费者来讲,当对提供者请求时间过久时为了不影响性能就对链接进行熔断,

限流是对于提供者来讲,为了防止某个消费者流量太大,导致其它更重要的消费者请求无法及时处理。限流可用通过拒绝服务、服务降级、消息队列延时处理、限流算法来实现

22.常用限流算法

计数器算法:使用redis的setnx和过期机制实现

漏桶算法:一般使用消息队列来实现,系统以恒定速度处理队列中的请求,当队列满的时候开始拒绝请求。

令牌桶算法:计数器算法和漏桶算法都无法解决突然的大并发,令牌桶算法是预先往桶中放入一定数量token,然后用恒定速度放入token直到桶满为止,所有请求都必须拿到token才能访问系统

六.Redis系列

1.redis为什么快?

(1)完全基于内存操作

(2)数据结构简单,对数据操作简单

(3)redis执行命令是单线程的,避免了上下文切换带来的性能问题,也不用考虑锁的问题

  (4) 采用了非阻塞的io多路复用机制,使用了单线程来处理并发的连接;内部采用的epoll+自己实现的事件分离器

其实Redis不是完全多线程的,在核心的网络模型中是多线程的用来处理并发连接,但是数据的操作都是单线程。Redis坚持单线程是因为Redis是的性能瓶颈是网络延迟而不是CPU,多线程对数据读取不会带来性能提升。

2.redis持久化机制

(1)快照持久化RDB

redis的默认持久化机制,通过父进程fork一个子进程,子进程将redis的数据快照写入一个临时文件,等待持久化完毕后替换上一次的rdb文件。整个过程主进程不进行任何的io操作。持久化策略可以通过save配置单位时间内执行多少次操作触发持久化。所以RDB的优点是保证redis性能最大化,恢复速度数据较快,缺点是可能会丢失两次持久化之间的数据

(2)追加持久化AOF

以日志形式记录每一次的写入和删除操作,策略有每秒同步、每次操作同步、不同步,优点是数据完整性高,缺点是运行效率低,恢复时间长

3.Redis如何实现key的过期删除?

采用的定期过期+惰性过期

定期删除 :Redis 每隔一段时间从设置过期时间的 key 集合中,随机抽取一些 key ,检查是否过期,如果已经过期做删除处理。
惰性删除 :Redis 在 key 被访问的时候检查 key 是否过期,如果过期则删除。

4.Redis数据类型应用场景

String:可以用来缓存json信息,可以用incr命令实现自增或自减的计数器

Hash:与String一样可以保存json信息

List:可以用来做消息队列,list的pop是原子性操作能一定程度保证线程安全

Set:可以做去重,比如一个用户只能参加一次活动 ;可以做交集求共友

SortSet :有序的。可以实现排行榜

5.Redis缓存穿透如何解决?

缓存穿透是指频繁请求客户端和缓存中都不存在的数据,缓存永远不生效,请求都到达了数据库。

解决方案:

(1)在接口上做基础校验,比如id<=0就拦截

(2)缓存空对象:找不到的数据也缓存起来,并设置过期时间,可能会造成短期不一致

(3)布隆过滤器:在客户端和缓存之间添加一个过滤器,拦截掉一定不存在的数据请求

6.Redis如何解决缓存击穿?

缓存击穿是值一个key非常热点,key在某一瞬间失效,导致大量请求到达数据库

解决方案:

(1)设置热点数据永不过期

(2)给缓存重建的业务加上互斥锁,缺点是性能低

7.Redis如何解决缓存雪崩?

缓存雪崩是值某一时间Key同时失效或redis宕机,导致大量请求到达数据库

解决方案:

(1)搭建集群保证高可用

(2)进行数据预热,给不同的key设置随机的过期时间

(3)给缓存业务添加限流降级,通过加锁或队列控制操作redis的线程数量

(4)给业务添加多级缓存

 篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记及答案【点击此处即可】免费获取

8.Redis分布式锁的实现原理

原理是使用setnx+setex命令来实现,但是会有一系列问题:

(1)任务时常超过缓存时间,锁自动释放。可以使用Redision看门狗解决

(2)加锁和释放锁的不是同一线程。可以在Value中存入uuid,删除时进行验证。但是要注意验证锁和删除锁也不是一个原子性操作,可以用lua脚本使之成为原子性操作

(3)不可重入。可以使用Redision解决(实现机制类似AQS,计数)

(4)redis集群下主节点宕机导致锁丢失。使用红锁解决

9.Redis集群方案

(1)主从模式:个master节点,多个slave节点,master节点宕机slave自动变成主节点

(2)哨兵模式:在主从集群基础上添加哨兵节点或哨兵集群,用于监控master节点健康状态,通过投票机制选择slave成为主节点

(3)分片集群:主从模式和哨兵模式解决了并发读的问题,但没有解决并发写的问题,因此有了分片集群。分片集群有多个master节点并且不同master保存不同的数据,master之间通过ping相互监测健康状态。客户端请求任意一个节点都会转发到正确节点,因为每个master都被映射到0-16384个插槽上,集群的key是根据key的hash值与插槽绑定

10.Redis集群主从同步原理

主从同步第一次是全量同步:slave第一次请求master节点会根据replid判断是否是第一次同步,是的话master会生成RDB发送给slave。

后续为增量同步:在发送RDB期间,会产生一个缓存区间记录发送RDB期间产生的新的命令,slave节点在加载完后,会持续读取缓存区间中的数据

11.Redis缓存一致性解决方案

Redis缓存一致性解决方案主要思考的是删除缓存和更新数据库的先后顺序

先删除缓存后更新数据库存在的问题是可能会数据不一致,一般使用延时双删来解决,即先删除缓存,再更新数据库,休眠X秒后再次淘汰缓存。第二次删除可能导致吞吐率降低,可以考虑进行异步删除。

先更新数据库后删除缓存存在的问题是会可能会更新失败,可以采用延时删除。但由于读比写快,发生这一情况概率较小。

但是无论哪种策略,都可能存在删除失败的问题,解决方案是用中间件canal订阅binlog日志提取需要删除的key,然后另写一段非业务代码去获取key并尝试删除,若删除失败就把删除失败的key发送到消息队列,然后进行删除重试。

12.Redis内存淘汰策略

当内存不足时按设定好的策略进行淘汰,策略有(1)淘汰最久没使用的(2)淘汰一段时间内最少使用的(3)淘汰快要过期的

七.计算机网络系列

1.TCP/IP模型20200904160558176.png
 2.浏览器输入地址后做了什么?

20200904160558322.png

 3.TCP三次握手

20200904160558436.png

4.为什么TCP不能两次握手

假设是两次握手,若客户端发起的连接请求阻塞在网络中,会造成该报文的重传,这时服务收到连接请求后会立刻进入连接状态,当双方传输完数据结束连接后,第一次阻塞的请求突然又到达了服务端,此时服务端又进入连接状态,而客户端不会响应服务端的连接确认报文

5.TCP四次挥手

20200904160558691.png

6.为什么要进入时间等待状态?

若客户端发送确认释放包后直接关闭,而服务端因为某种原因没有收到客户端的确认释放包,就会一直发送确认请求,而客户端永远不会再响应该请求。

7.TCP 滑动窗口

TCP 流量控制,主要使用滑动窗口协议,滑动窗口是接受数据端使用的窗口大小,用来告诉发送端接收端的缓存大小,以此可以控制发送端发送数据的大小,从而达到流量控制的目的。如果TCP发送方收到接收方的零窗口通知后,会启动持续计时器。计时器超时后向接收方发送零窗口探测报文,如果响应仍为0,就重新计时,不为0就打破死锁

8.TCP拥塞控制

发送方会维护一个拥塞窗口大小的状态变量,大小取决于网络的拥塞程度。发送方的发送窗口大小是取接收方接收窗口和拥塞窗口中较小的一个

拥塞控制有四种算法:

慢开始:从小到大主键发送窗口,每收到一个确认报文窗口大小指数增长

拥塞避免:当窗口大小到达一定阈值时,转为拥塞避免,每收到一个确认报文窗口大小+1。若此时网络超时,就把阈值调小一半,重新慢开始

快重传:要求接收方收到请求后要立即回复

快恢复:发送方连续收到多个确认时,就把拥塞避免阈值减小,然后直接开始拥塞避免

9.TCP超时重传

发送方在发送按数据后一定时间内没有收到接收方响应报文,就会重新发送刚刚的报文,接收到收到报文后会对该报文的序列号进行检验,已存在就抛弃

10.TCP可靠传输的实现

TCP是靠滑动窗口协议和连续ARQ协议配合流量控制和拥塞控制来保证的可靠传输。

ARQ是停止等待协议和自动重传请求,它规定TCP要为每一次传输的包编号,每发送一个包,要等待对方确认后才能发送下一个分组,若一段时间对方没有确认,就重新发送刚刚的报文。接收方会对数据包排序,把有序数据传给应用层,返回缺失的第一个ACK确认序列号给发送方,接收到收到报文后会对该报文的序列号进行检验,重复就丢弃。

流量控制是.....拥塞窗口上......(上面已经说了)

11.TCP报头有哪些信息

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjczNjAyNA==,size_16,color_FFFFFF,t_70

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值