最全、最通俗易懂的Java面试题积累【一】

文章目录


会从较为基础的逐渐递进,希望对大家有帮助,保护发际线,从你我开始

1.String和StringBuilder和StringBuffer的区别

String:是不可变得字符序列,底层是’private final char’
StringBuilder:可变字符序列,效率较高,但是线程不安全
StringBuffer:可变字符序列,效率低,但是线程安全
在这里插入图片描述

2.String为什么不可变StringBuilder为什么可变

从String类的源码中可以看到,String是通过char value[]保存字符的。而且声明为private final,并且不提供我们写value的接口。所以String类不可变。
而StringBuilder是底层是一个char数组,而且没有被final,初始化容量应该为16。

3.StringBuilder为什么线程不安全StringBuffer为什么不安全 , 什么场景下使用StringBuilder什么场景使用StringBuffer

StringBuffer中的方法都加了synchronized关键字
多线程不要用StringBuilder,使用StringBuffer,StringBuilder的效率要StringBuffer高。

4.HashMap底层用到了什么数据结构?

JDK1.7及之前:数组+链表
JDK1.8:数组+链表或红黑树
基本架构是使用得数组结构,但是如果发生哈希冲突得时候,就会启动链表,当链表结构长度大于8时,就转换为红黑树
数组的特点:查询效率高,插入、删除效率低。
链表的特点:查询效率低,插入、删除效率高。

5.Spring、SpringMVC、SpringBoot、SpringCloud的区别

spring: 是一个一站式的轻量级的java开发框架,核心是控制反转(IOC)和面向切面(AOP),针对于开发的WEB层(springMvc)、业务层(Ioc)、持久层(jdbcTemplate)等都提供了多种配置解决方案

springMVC: 是spring基础之上的一个MVC框架,主要处理web开发的路径映射和视图渲染,属于spring框架中WEB层开发的一部分

springBoot:(简化了spring配置流程)框架相对于springMVC框架来说,更专注于开发微服务后台接口,不开发前端视图,同时遵循默认优于配置,简化了插件配置流程,不需要配置xml,相对springmvc,大大简化了配置流程

springCloud:(用于微服务的服务治理)大部分的功能插件都是基于springBoot去实现的,springCloud关注于全局的微服务整合和管理,将多个springBoot单体微服务进行整合以及管理; springCloud依赖于springBoot开发,而springBoot可以独立开发

6.介绍一下Spring框架的组成

在这里插入图片描述

7.说一下你对IOC的理解

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

8.说一下你对AOP的理解

AOP即Aspect Oriented Program,面向切面编程,是面向对象编程(OOP)的一种增强模式,可以将项目中与业务无关的,却为业务模块所共同调用的非核心代码封装成(比如事务管理、日志管理、权限控制等等)一个个切面,然后在运行的时候通过动态代理的方式织入到核心业务功能中。

9.BeanFactory和ApplicationContext有什么区别

BeanFactory:
是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;
ApplicationContext:
应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;

两者装载bean的区别:
BeanFactory:
BeanFactory在启动的时候不会去实例化Bean,有从容器中拿Bean的时候才会去实例化(类似懒加载);

ApplicationContext:
ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean延迟实例化(迫切加载);

10.Spring的Bean被指定为singleton以及prototype有什么区别?

singleton: Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。换言之,当把一个bean定义设置为singleton作用域时,Spring IOC容器只会创建该bean定义的唯一实例。简单的说:当一个bean被标识为singleton时候,spring的IOC容器中只会存在一个该bean,并且默认使用得就是这种单例模式。

prototype: 每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当与一个new的操作。
有状态的bean都使用prototype作用域,而对无状态的bean则应该使用singleton作用域。可以理解为我们singleton是一种单线程,每次操作都会带着上次操作以后得状态,很危险!

11.Spring的Bean懒加载和非懒加载有什么区别

懒加载:对象使用的时候才去创建。节省资源,但是不利于提前发现错误;
非懒加载:容器启动时立马创建。消耗资源,但有利于提前发现错误

12.Spring的依赖注入方式有哪些?

一:目前使用最广泛的 @Autowired:自动装配
二:setter 方法注入
这是最简单的注入方式,假设有一个SpringAction类中需要实例化一个SpringDao对象,那么就可以定义一个private的SpringDao成员变量,然后创建SpringDao的set方法(这是ioc的注入入口)
三:构造器注入
这种方式的注入是指带有参数的构造函数注入,假设创建了两个成员变量SpringDao和User,但是并未设置对象的set方法,所以就不能支持第二种注入方式,这里的注入方式是在SpringAction的构造函数中注入,也就是说在创建SpringAction对象时要将SpringDao和User两个参数值传进来
四:静态工厂的方法注入
静态工厂顾名思义,就是通过调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过"工程类.静态方法()"来获取对象,而是依然通过spring注入的形式获取
五:实例工厂的方法注入
实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先new工厂类,再调用普通的实例方法

13.有哪些方式可以注册一个Bean到容器中

一,通过xml配置文件
二,通过注解,例如@Controller、@Component、@Service,@Bean等
三,通过实现FactoryBean接口
四,通过@Import注解
五,向BeanDefinitionRegistry 注册表中直接注册一个bean

14.将一个类声明为Spring的Bean的注解有哪些?

@Component :通用的注解,可标注任意类为 Spring 组件。如果一个Bean不知道属于哪个层,可以使用
@Repository : 对应持久层即 Dao 层,主要用于数据库相关操作。
@Service :对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao层。
@Controller : 对应 Spring MVC控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。

15.Spring的IOC启动原理?

  1. 加载配置XML;或者配置类
  2. 解析配置(xml Dom解析);或者扫描要被管理得类(@Service等等)
  3. 对Bean进行定义或者封装成BeanDefinition
  4. 通过BeanDefinitionRegistry注册BeanDefinition到一个ConcurrentHashMap中
  5. 如果Bean是迫切初始化就会根据BeanDefinition通过反射创建Bean得实力,缓存到一个HashMap中
  6. 当通过容器getBean得时候,如果是单例得,就会从缓存得Map中获取Bean,获取不到就用反射创建,并添加到缓存,返回
  7. 当通过容器getBean得时候,如果不是单例得,就会使用反射重新创建Bean得实例返回

16.Spring是如何管理事务的?

Spring的事务机制包括声明式事务和编程式事务。

编程式事务管理:Spring推荐使用TransactionTemplate,实际开发中使用声明式事务较多。

声明式事务管理:将我们从复杂的事务处理中解脱出来,获取连接、关闭连接、事务提交、回滚、异常处理等这些操作都不用我们处理了,Spring都会帮我们处理。

声明式事务管理使用了AOP面向切面编程实现的,本质就是在目标方法执行前后进行拦截。
Spring事务管理主要包括3个接口,Spring的事务主要是由他们三个共同完成的。

1)PlatformTransactionManager:事务管理器–主要用于平台相关事务的管理
2)TransactionDefinition:事务定义信息–用来定义事务相关的属性,给事务管理器PlatformTransactionManager使用
3)TransactionStatus:事务具体运行状态–事务管理过程中,每个时间点事务的状态信息。

@Translation

  1. 首先Spring使用得是AOP来管理事务得,实现原理是动态代理
  2. 事务本身是MySQL得,Spring只是通过JDBC得API去操作MySQL得事务命令、提交、回滚等
  3. Spring只是制定了事物得一些API,实现还是JDBC
  4. 通常我们得Service需要打@Translation得注解,Spring扫描这样得标签,就知道那些类、方法需要事务
  5. 使用Aop思想,基于动态代理得原理来为这些Service生成代理类
  6. 代理类会对原生得Service做增强,比如会在代理类中执行开启事务(设置手动提交),然后执行原生Service中得方法,接下来就是手动提交事务,如果失败了就执行回滚

17.Spring用到了哪些设计模式?

1、代理模式:在AOP和remoting中被用的比较多
2、单例模式:在spring配置文件中定义的bean默认为单例模式
3、模板方法模式:解决代码重复问题
4、前端控制器模式:spring提供了DispatcherServlet来对请求进行分发
5、依赖注入模式:贯穿于BeanFactory和ApplicationContext接口的核心理念
6、工厂模式:BeanFactory用来创建对象的实例

18.RequestMapping 和 GetMapping有什么区别?

@RequestMapping可以指定GET、POST请求方式
@GetMapping等价于@RequestMapping的GET请求方式

19.SpringMVC怎么样设定重定向和转发的

1.地址栏
转发:不变,不会显示出转向的地址
重定向:会显示转向之后的地址

2.请求

转发:一次请求
重定向:至少提交了两次请求

3.数据
转发:对request对象的信息不会丢失,因此可以在多个页面交互过程中实现请求数据的共享
重定向:request信息将丢失

4.原理
转发:是在服务器内部控制权的转移,是由服务器去请求,客户端并不知道是怎样转移的,因此客户端浏览器的地址不会显示出转向的地址。
重定向:是服务器告诉了客户端要转向哪个地址,客户端再自己去请求转向的地址,因此会显示转向后的地址,也可以理解浏览器至少进行了两次的访问请求。

(1)转发:在返回值前面加"forward:",
譬如"forward:user.do?name=method4"
(2)重定向:在返回值前面加"redirect:",
譬如"redirect:http://www.baidu.com"

20.SpringMVC如何对时间格式的参数进行格式化?有哪些方式?

方式一:在需要日期转换的Controller中使用SpringMVC的注解@initbinder和Spring自带的WebDateBinder类来操作。
方法二:使用@DateTimeFormat 注解方式,建议使用此方式,简单且代码量少

21.SpringMVC常用的注解有哪些

@ResponseBody:该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区

@PathVariable:用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数

@GetMapping:是@RequestMapping(method = RequestMethod.GET)的缩写。

@ControllerAdvice:统一处理异常

@Controller:用来定义控制器类

@RestController:是@ResponseBody和@Controller的合集

@RequestParam:用于将请求参数区数据映射到功能处理方法的参数上

22.如何定义SpringMVC的拦截器

第一种通过实现HandleInterceptor接口,或者继承HandleInterceptor接口的实现类HandleInterceptorAdapter来定义;

第二种通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。

拦截器的定义:以实现HandleInterceptor接口为例
(1)、preHandle()方法
该方法会在控制器方法前执行,其返回值表示是否中断后续操作。当返回值为true时,表示继续向下执行;当返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)。

(2)、postHandle()方法
该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步的修改。

(3)、afterCompletion()方法
该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作。

注册拦截器有两种:springMVC需要在XML中进行配置,而spring boot需要写一个配置类WebMvcConfigurer

23.HandlerInterceptor和HandlerInterceptorAdapter的区别

一、相同
都是用来实现Spring拦截器
可以作为日志记录和登录校验来使用

二、区别
HandlerInterceptorAdapter需要继承,HandlerInterceptor需要实现

注:在JDK1.8以后HandlerInterceptorAdapter过时了,因为在HandlerInterceptor中提供了default方法

24.SpringMVC的执行原理

看图说话!!!!!
在这里插入图片描述

25.SpringMVC的Controller是单例还是多例,有没有并发安全问题,如何解决?

controller默认是单例的,不要使用非静态的成员变量,否则会发生数据逻辑混乱。
正因为单例所以不是线程安全的。
解决方法:
1、不要在controller中定义成员变量。
2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope(“prototype”),将其设置为多例模式。
3、在Controller中使用ThreadLocal变量,ThreadLocal底层是使用map方法,将当前线程作为key值传入,而设置得value作为value来实现线程之间得隔离

26.Spring Boot有哪些优点

1、简化开发流程,减少时间,实现快速开发
2、避免大量jar包得导入与冲突问题,方便管理
3、减少配置,对于配置类,可以使用@configuration注解配置类;通过注解@Bean添加类;通过@Autowired注入需要得类,使用spring的自动装配
4、Spring Boot内嵌有tomcat,方便使用

27.SpringBoot如何做全局异常处理?

我们需要写一个xxExceptionHandler类,用@ControllerAdvice标注和@ExceptionHandler(需要捕获得Exception.class)标记具体使用哪个类

@ControllerAdvice是controller的一个辅助类,最常用的就是作为全局异常处理的切面类
@ControllerAdvice可以指定扫描范围
@ControllerAdvice约定了几种可行的返回值,如果是直接返回model类的话,需要使用@ResponseBody进行json转换

28.SpringBoot如何读取配置文件中的配置项?

  1. 使用注解@Value映射
    通过@Value注解将配置文件中的值映射到一个Spring管理的Bean的字段上,会自动为字段赋值
 	@Value("${jdbc.driverClassName}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

  1. 使用注解@ConfigurationProperties映射
    @ConfigurationProperties注解声明当前类为配置读取类,prefix="jdbc"表示读取前缀为jdbc的属性会将配置文件中前缀为prefix的属性赋给类中同名的字段
    注意:必须保证属性名称和字段一模一样,且类需要提供字段的setter方法
@ConfigurationProperties(prefix = "jdbc")
public class JdbcProperties {
    private String driverClassName;
    private String url;
    private String username;
    private String password;

    public String getDriverClassName() {
        return driverClassName;
    }

    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }
......
}

29.@SpringBootApplication标签的组成

较为主要得为下面三个标签

@SpringBootConfiguration  //里面有个@Configuration :定义是一个JavaConfig配置类。
@EnableAutoConfiguration //告诉Spring Boot开始根据类路径的设置去自动加载其他的bean及各种属性.
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) //自动扫描并加载符合条件
		的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。

30.SpringBoot是如何整合SpringMVC的?

  1. 启动类的主标签@SpringBootApplication注解中包含一个@EnableAutoConfiguration注解,他的作用就是开启SpringBoot自动配置
  2. @EnableAutoConfiguration注解中会导入一个选择器@Impot(AutoConfigurationImportSelector.class)
  3. 接下来这个选择器会使用 SpringFactoriesLoader ,加载 classpath下的jar中的spring.factories 中的很多的自动配置的类(使用全限定名)
  4. 比如说:DispatcherServletAutoConfiguration 就是对前端控制器的自动配置
  5. 最后DispatcherServletAutoConfiguration通过定义Bean的方式就把DispatcherServlet定义好了,我们就不用配置了

31.SpringBoot是如何整合DataSource的?

  1. 启动类的主标签@SpringBootApplication注解中包含一个@EnableAutoConfiguration注解,他的作用就是开启SpringBoot自动配置
  2. @EnableAutoConfiguration注解中会导入一个选择器(@Impot(AutoConfigurationImportSelector.class)
  3. 接下来这个选择器会使用 SpringFactoriesLoader ,加载 classpath下的jar中的spring.factories 中的很多的自动配置的类(使用全限定名)
  4. 其中就有一个 DataSourceAutoConfiguration 针对于DataSource的配置
  5. 加载自动配置类 DataSourceAutoConfiguration ,里面通过@Bean标签方式注册DataSource的 Bean 。默认会使用 Hikari , 如果配置了type属性,会根据datasource的配置项来创建一个DataSource的实例交给Spring

32.SpringBoot的启动流程

1.开启秒表
2.获取Spring启动的监听器,调用starting()开启监听
3.创建了一个ApplicationArguments参数对象
4.准备环境对象 ConfigurableEnvironment环境对象中有配置,profile
5.打印banner
6.创建上下文对象ApplicationContext(AnnotationConfigApplicationContext)
7.获取异常报告
8.准备上下文
9.刷新Spring上下文
10.刷新上下文的后续工作
11.启动listeners.started
12.通过CommandLineRunner启动项目

开启秒表---->配置监听器---->创建参数对象---->环境对象的配置---->打印banner---->创建上下文对象---->收集异常报告---->准备上下文对象---->刷新上下文(扫描Bean,注册Bean,初始化Bean,等等) ---->启动监听器---->通过runner启动项目

33.SpringBoot和SpringCloud的区别?

1、Spring boot 是 Spring 的一套轻量级的开发框架,可以基于spring boot 快速开发单个微服务;Spring Cloud是一个基于Spring Boot实现的云应用开发工具;

2、Spring boot专注于快速、方便集成的单个个体,Spring Cloud是关注全局的服务治理框架;

3、Spring boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring boot,属于依赖的关系。

34.SpringCloud常用组件和作用?

服务注册与发现中心——Netflix Eureka
客服端负载均衡——Netflix Ribbon/Feign
断路器——Netflix Hystrix
服务网关——Netflix Zuul
配置中心——Spring Cloud Config

35.Eureka心跳和服务剔除机制是什么

服务提供者在启动后,周期性(默认30秒)向Eureka Server发送心跳,以证明当前服务是可用状态。Eureka Server在一定的时间(默认90秒)未收到客户端的心跳,则认为服务宕机,注销该实例。

36.Eureka的服务注册与发现的工作流程

微服务(EurekaClient)在启动的时候会向EurekaServer提交自己的服务信息(通信地址如:服务名,ip,端口等),在 EurekaServer会形成一个微服务的通信地址列表存储起来。 --------这叫服务注册

微服务(EurekaClient)会定期(RegistryFetchIntervalSeconds:默认30s)的从EurekaServer拉取一份微服务通信地址列表缓存到本地。当一个微服务在向另一个微服务发起调用的时候会根据目标服务的服务名找到其通信地址,然后基于HTTP协议向目标服务发起请求。--------这叫服务发现

37.对于CAP理论,Eureka选择的是AP还是CP?它保证了一致性还是可用性?

CAP指的是 一致性(Consistency),可用性(Availability),分区容错性(Partition tolerance)
Eureka选择的是AP,保证了可用性和分区容错性。

38.说一下Eureka的自我保护

默认情况下,当EurekaServer接收到服务续约的心跳失败比例在15分钟之内低于85%,EurekaServer会把这些服务保护起来,即不会把该服务从服务注册地址清单中剔除掉,但是在此种情况下有可能会出现服务下线,那么消费者就会拿到一个无效的服务,请求会失败,那我们需要对消费者服务做一些重试,或者熔断策略。

39.说下Ribbon和Feign的区别呢?

  1. Feign是基于Ribbon进行封装的,底层其实都是一样的,需要用到RestTemplate
  2. 启动类注解不同,Ribbon是@RibbonClient;Feign的是@EnableFeignClients
  3. 调用方式不同
    Ribbon需要自己构建http请求,模拟http请求然后使用RestTemplate发送给其他服务,步骤相当繁琐。
    Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建http请求。不过要注意的是抽象方法的注解、方法签名要和提供服务的方法完全一致。

40.如何实现Eureka的高可用集群?

  1. 需要搭建多个EurekaServer 并且相互进行注册,这时Eureka即使客户端也是服务端
  2. 采用SpringBoot单配置文件多环境配置
  3. 为每个EurekaServer进行不一样的配置:端口(port) ,环境名字(profiles),主机名(hostname),实例id(instance-id:)
  4. 启动多个Eureka进行测试

41.Hystrix的限流有几种方式,有什么区别?

Hystrix的限流有线程池与信号量两种:
线程池:使用一个线程池来存储当前请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求先入线程池队列。因为需要重新创建线程所以有资源损耗,但是可以应对突发流量。

信号量:使用一个原子计数器(或信号量)记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。不会重新创建线程,所以没有多余的资源损耗,但是因为没有缓存的空间,所以不能应对突发流量。

42.Hystrix的熔断有几种状态?这几种状态是怎么变换的?

Hystrix的熔断分为:关闭状态(Closed)、熔断状态(Open)、半熔断状态(Half-Open)
之间变换:正常情况下,Hystrix处于关闭状态,如果调用持续出错或者超时达到设定阈值,熔断器进入熔断状态,这是针对这个服务的请求会触发快速降级,返回降级数据,避免线程堵塞,在间隔一段时间(withCircuitBreakerSleepWindowInMilliseconds=5s),保护器会进入半熔断状态,允许少量的请求访问,如果调用还是失败,继续进入熔断状态,如果成功则熔断器进入关闭状态
在这里插入图片描述

43.服务A使用ribbon调用服务B,Ribbon是如何工作的?

Ribbon会根据服务通讯地址清单中根据服务名找到对应的服务通讯地址,如果目标服务有集群(找到多个通讯地址),则ribbon会按照负载均衡算法(默认轮询)选择其中的某一个通信地址,发起http请求实现服务的调用

44.为什么Feign的接口可以直接注入进来用?

主配置类通过@EnableFeignClients(value="")注解开启Feign,同时为Feign编写客户端接口,接口上需要注解@FeignClient标签。当程序启动,注解了@FeignClient的接口将会被扫描到然后交给Spring管理。
当请求发起,会使用jdk的动态代理方式代理接口,生成相应的RequestTemplate,Feign会为每个方法生成一个RequestTemplate同时封装好http信息。
最终RequestTemplate生成request请求,交给Http客户端(UrlConnection ,HttpClient,OkHttp)。然后Http客户端会交给LoadBalancerClient,使用Ribbon的负载均衡发起调用

45.Ribbon的负载均衡算法有哪些?

  1. RoundRobinRule: 默认轮询的方式
  2. RandomRule: 随机方式
  3. WeightedResponseTimeRule: 根据响应时间来分配权重的方式,响应的越快,分配的值越大。
  4. BestAvailableRule: 选择并发量最小的方式
  5. RetryRule: 在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server
  6. ZoneAvoidanceRule: 根据性能和可用性来选择。
  7. AvailabilityFilteringRule: 过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)

46.你们项目是如何做服务降级的?

使用fallbackFactory属性,使用工厂方式指定托底
在Feign的客户端进行配置fallbackFactory = 降级类.class
然后工厂方式的托底类需要去实现 FallbackFactory接口 ,并指定泛型为“Feign客户端接口”(UserFeignClient )。FallbackFactory的create方法返回了Feign客户端接口的实例,该方法的throwable是参数是Feign调用失败的异常信息

47.Zuul有哪几类Filter,他们的执行顺序是怎么样的?

看图说话!!!
在这里插入图片描述
正常流程:

  1. 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

异常流程:

  1. 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
  2. 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
  3. 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。

48.在Zuul中做登录检查如何实现?

  1. 继承ZuulFilter类,在类顶部加注解@Component, 让Spring扫描
  2. 重写里面的四个方法
    3.1. 过滤器类型,前置过滤器
    3.2. 过滤器顺序,越小越先执行
    4.3. 过滤器是否生效,是否执行run方法
    3.4. 业务逻辑核心run方法

49.在Zuul中如何做限流?

方式一:漏桶算法
漏桶算法思路很简单,请求先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。

方式二:令牌桶算法(可以应对突发传输)
令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

方式三:限流工具类RateLimiter
Google开源工具包Guava提供了限流工具类RateLimiter,该类基于令牌桶算法来完成限流,非常易于使用。

50.介绍一下Java中的集合体系

看图说话!!!!
collection中主要需要记住:
List:有序可重复
Set:无序不可重复
在这里插入图片描述
Map中主要就是HashMap以及LinkedHashMap
在这里插入图片描述

51.如果需要从一个List集合中频繁的删除和添加元素,是选择ArrayList还是LinkedList?为什么?

频繁的删除和添加元素可以使用LinkedList对象,因为LinkedList是基于双链表(不仅实现List,还实现了Deque接口),因为Array是基于数组(index)的数据结构,要插入、删除数据却是开销很大的,因为这需要移动数组中插入位置之后的的所有元素。而LinkedList中插入或删除的时间复杂度仅为O(1)

52.什么是Hash冲突(碰撞)?

Hash冲突(碰撞)就是不同的数据计算(散列算法)得 出的hash值是一样的。

53.HashMap为什么要使用链表,又为什么要使用红黑树?

HashMap底层是通过散列算法(哈希算法)计算哈希值然后进行存储位置的计算,但是存在哈希值相同的情况(哈希冲突),为了解决哈希冲突,我们就在数组的基础上对哈希值相同的数据使用链表进行存储,但是当链表长度达到8(默认长度),就会将链表变为红黑树,解决链表查询慢的问题

54.HashMap是否是线程安全的,如果不是,那么在多线程环境中我们应该使用哪个集合?

HashMap是非线程安全,但是JDK在其并发包中为我们提供了线程安全的ConcurrentHashMap。
ConcurrentHashMap大部分的逻辑代码和HashMap是一样的,主要通过synchronized来保证节点插入扩容的线程安全

55.HashMap扩容机制?

这里要明确HashMap初始数组容量是16,负载因子为0.75,每次扩容是位运算,通俗讲就是乘2

56.创建线程的3种方式?

(1)继承Thread类,Thread它实现了Runnable
(2)实现Runnable接口
(3)使用ExecutorService、Callable、Future接口实现有返回结果的多线程

57.Synchronized和Lock的区别?

在这里插入图片描述
(1)Lock的加锁和解锁都是由java代码实现的,而synchronize的加锁和解锁的过程是由JVM管理的;
(2)Synchronized能锁住类、方法和代码块,而Lock是块范围内的;
(3)Lock能提高多个线程读操作的效率;

58.线程的生命周期?

  1. 新建状态:当使用new关键字和Thread类或者其子类创建对象后,该线程就处于新建状态
  2. 就绪状态:当线程对象调用了start()方法后,该线程就进入了就绪状态(就绪状态的线程处于就绪队列后,要等待JVM里线程调度器的调度)
  3. 运行状态:当就绪状态里的线程获取到了CPU中的资源,可以执行run()方法,此时的线程便处于运行状态
  4. 阻塞状态:当一个线程失去所占用资源之后,线程就从运行状态进入到阻塞状态,阻塞又分为以下三种:
    4.1. 等待阻塞:运行状态下执行了wait()方法,使线程进入等待阻塞状态
    4.2. 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)
    4.3. 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态
  5. 死亡状态:一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态

59.synchronized 锁的原理,JDK1.5之后做了什么样的优化?

synchronized是一种内部锁,也是一种重量级的性能较低的锁,synchronized是通过内部对象Monitor(监视器锁)实现,通过进入(进入+1,同一线程可重复进入)与退出(减1,减到0就为退出)对象的Monitor来实现方法与代码块同步
JDK1.5之后 ,Synchronized会从无锁升级为偏向锁,再升级为轻量级锁,最后升级为重量级锁

60.乐观锁的使用场景?

由于乐观锁的不上锁特性,所以在性能方面要比悲观锁好,比较适合用在DB的读大于写的业务场景(高并发、多读少写的场景)。

61.AtomicInterger怎么保证并发安全性的?

底层使用的是Unsafe 方法基于的是CPU的CAS指令(CAS就是Compare and Swap的意思,比较并操作,一种乐观锁计数)来实现的
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

62.ThreadLocal的作用和原理?

ThreadLocal线程本地变量,用于线程间的数据隔离,简单来说ThreadLocal是一种key-value结构,ThreadLocal会把当前线程的副本作为Key,而Value就是我们存储的值,在高并发的情况下,因为线程不同所以Key不同,以此实现线程隔离。

63.synchronized 和 volatile 的区别是什么?

(1)、volatile只能作用于变量,使用范围较小。synchronized可以用在变量、方法、类、同步代码块等,使用范围比较广。
(2)、volatile只能保证可见性和有序性,不能保证原子性。而可见性、有序性、原子性synchronized都可以包证。
(3)、volatile不会造成线程阻塞。synchronized可能会造成线程阻塞。

64.常用的线程池有几种,分别是什么意思?

  1. CachedThreadPool:可缓存线程池
  2. FixedThreadPool :固定长度线程池
  3. SingleThreadPool:单个线程池
  4. ScheduledThreadPool:可调度定时线程池

65.线程池的执行流程?

核心线程(corePoolSize) => 等待队列 => 非核心线程(队列已满就创建非核心线程) => 拒绝策略

66.解释一下线程池构造器的7个参数?

corePoolSize:核心的线程数,不会被销毁的
maximumPoolSize:最大线程数,核心+非核心的,非核心的在用完以后空闲时间会被销毁
keepAliveTime:非核心线程的最大空闲时间
unit:空闲时间单位
workQueue:是一个阻塞队列,超过核心线程数的任务会进入队列排队
threadFactory:创建线程,推荐使用Executors.defaultThreadFactory
handler:拒绝策略,超过核心线程+队列排队数+非核心线程

67.EurekaClient拉取注册表&心跳续约用到了什么技术来实现?

EurekaClient使用到ScheduledThreadPool及逆行注册表拉取与心跳的续约,是一个定时任务的线程

68.Redis如何实现分布式锁?

方式一:自己封装Redis分布式锁,Redis提供了一个命令setnx 可以来实现分布式锁,还需要Lua脚本保证原子性(避免误删锁)
方式二:Redisson的实现分布式锁,对上面获取锁和原子性进行保证(Redisson内部提供了一个监控锁的看门狗)

69.Zookeeper实现分布式锁的原理?

方式一:临时节点+重试,只有一个节点,当前节点释放锁后,后面自旋的才能获取锁
方式二:临时顺序节点+watch(下一个节点监听上一个节点),创建多个节点

70.说下你对Hash算法的理解?

Hash算法(或者称为散列函数):就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值

71.说一下ES用到了什么数据结构?

用到了倒排索引文档
倒排索引

72.说一下你对树的理解(二叉查找树的优缺点,平衡树的优缺点,红黑树的优缺点,B-树的优缺点 ,B+树的优缺点)

二叉查找树的优点:方便查找,可以使用二分查找
二叉查找树的缺点:在某种极端情况下可能退化成为一个链表

平衡树的优点:避免退化成链表,每个节点左右子树差比高于1
平衡树的缺点:每次进行插入/删除节点的时候,为了保证平衡需要通过自旋来维持平衡,使得性能降低

红黑树的优点:具备平衡树的优点,减少因为维持平衡所作的开销
红黑树的缺点:树的层级不能控制,在数据较多的情况下,查询性能较低

B-树的优点:解决二叉树每个节点只有两个子节点,造成数的高度往往较高
B-树的缺点:因为节点需要同时存储key和value,所以存储的数据还是有限的

B+树的优点:所有数据都保存在叶子节点,所以非叶子节点只用来存取索引,所以可以保存更多的数据
B+树的缺点:空间使用率还可以提升

可以参考文章:推荐文章

73.HashMap底层用到了那些数据结构?为什么要用到这些结构?为什么要用到链表结构?为什么要用到红黑树?链表和红黑树在什么情况下转换的?

HashMap在JDK1.8以后的采用的数据结构:数组+链表+红黑树
采用链表为了解决哈希冲突,引入红黑树是为了避免HashMap退化成一条长的链表,降低查询效率,为解决该问题链表长度大于8引入红黑树
链表长度大于8变为红黑树,红黑树节点小于6变为链表结构

74.HashMap在什么情况下扩容?HashMap如何扩容的?

HashMap中的元素个数超过负载因子(默认0.75)*数组大小时,会进行两倍扩容

75.什么是Hash冲突?

对应不同的关键字可能获得相同的hash地址,即 key1≠key2,但是f(key1)=f(key2),简单说就是通过哈希运算后,结果一致,存放数据位置相同,好的哈希函数会尽量避免哈希冲突,不可能完全解决。

76.HashMap是如何Put一个元素的?

  1. 计算哈希值:先计算key的hashCode
  2. 数组初始化:首次table为null,首先通过resize()进行数组初始化
  3. 数组存储:如果索引位置无元素,则创建Node对象,存入数组该位置中
  4. 解决哈希冲突:如果索引位置已有元素,说明hash冲突,存入单链表或者红黑树中
  5. hash值和key值都一样,则进行value值的替代
  6. 判断存入链表还是红黑树
  7. 数组扩容:当元素个数大于新增阈值,则通过resize()扩容

77.HashMap是如何Get一个元素的?

  1. HashMap会计算传入key的哈希值,并对HashMap的容量进行求余,通过hash函数将hash值转换为数组下标,找到key所对应的位置。
  2. 获取到HashMap中key所对应的位置之后,调用equals判断我们get(key)中的key和单向链表中的每个节点的key
  3. 若保存的是链表,则从链头开始遍历,直到发现链表中的一个的节点上保存的key等于传入的Key。
  4. 若保存的是红黑树,则从树中查找节点的key等于传入的key。若查找成功,返回节点保存到值,即为查找结果。
  5. 若查找失败,返回null。

78.还有哪些解决Hash冲突的方式?

开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法(HashMap用的就是此方法)

79.LinkedHashMap与HashMap的关系?

LinkedHashMap继承自HashMap,在父类的基础上做了增强,HashMap存取数据是无序的,此时如果需要维护一个顺序就要使用LinkedHashMap双向链表 (doubly-linked list),LinkedHashMap保证了元素迭代的顺序。

80.HashSet和HashMap是什么关系?

HashSet底层是采用HashMap实现的,HashSet 的绝大部分方法都是通过调用 HashMap 的方法来实现的,因此 HashSet 和 HashMap 两个集合在实现本质上是相同的。
一句话:HashSet实际上为(key,null)类型的HashMap

81.TreeSet底层用到了什么结构来实现?

TreeSet 里绝大部分方法都是直接调用 TreeMap 的方法来实现的,TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点。

82.TreeSet默认是如何排序的?如何自定义排序规则?

默认是自然排序,可以自定义排序,有两种方式
方式一:需要排序的对象实现Comparable接口,复写compareTo方法
方式二:在构造器中指定一个Comparator比较器

83.手写一个冒泡排序

public static void main(String[] args) {
    int[] nums = {5,7,8,4,3};
    for (int i = 0; i < nums.length -1; i++) {
        for (int j = 0; j < nums.length-i-1; j++) {
            if (nums[j]>nums[j+1]){
                 int tmp  = nums[j+1];
                 nums[j+1] = nums[j];
                 nums[j] = tmp;
            }
        }
    }
    System.out.println(Arrays.toString(nums));
}

84.手写一个二分查找

//二分查找 
    public static Employee search(Employee[] employees,Long id){
        int low = 0;
        int high = employees.length - 1;
        int middle = 0;          //定义middle

        if(id < employees[low].getId() || id > employees[high].getId() || low > high){
            return null;
        }

        while(low <= high){
            middle = (low + high) / 2;
            if(employees[middle].getId() > id){
                //比关键字大则关键字在左区域
                high = middle - 1;
            }else if(employees[middle].getId() < id){
                //比关键字小则关键字在右区域
                low = middle + 1;
            }else{
                return employees[middle];
            }
        }
        return null;      //最后仍然没有找到,则返回-1

    }

85.线程的三种创建方式?

(1)继承Thread类,Thread它实现了Runnable
(2)实现Runnable接口
(3)使用ExecutorService、Callable、Future实现有返回结果的多线程

三种方式的详细解释:推按文章

86.ConcurrentHashMap原理?

ConcurrentHashMap 是线程安全且高效的HashMap
JAVA1.7之前ConcurrentHashMap主要采用锁机制,在对某个Segment进行操作时,将该Segment锁定,不允许对其进行非查询操作,而在JAVA1.8之后采用CAS无锁算法

87.说一下常见的数据结构,以及他们的特点?

集合:同类型元素,元素之间没有其他关系
线性:一对一的关系
树形:一对多的关系
图形:多对多的关系

88.ArrayList和 LinkedList的区别,使用的时候如何选择?

ArrayList 基于数组,LinkedList基于链表;
数组擅长随机查找,链表擅长增加删除;

89.常见的设计模式,哪些地方涉及到这些设计模式?

创建型模式(五种):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

结构型模式(七种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式

行为型模式(十一种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

单例(饱汉模式、饥汉模式)
1、构造方法私有化,除了自己类中能创建外其他地方都不能创建
2、在自己的类中创建一个单实例(饱汉模式是一出来就创建创建单实例,而饥汉模式需要的时候才创建)
3、提供一个方法获取该实例对象(创建时需要进行方法同步)

工厂模式:Spring IOC就是使用了工厂模式
对象的创建交给一个工厂去创建。

代理模式:Spring AOP就是使用的动态代理

工厂(Factory结尾的如:Spring的BeanFactory,FallbackFactory降级工厂)、
单例模式(Spring的Bean,一些工具类)、
建造者模式(Builder结尾的如:NativeSearchQueryBuilder),
适配器模式(Adapter结尾的如:HandlerInterceptorAdapter , WebMvcConfigurerAdapter,AuthorizationServerConfigurerAdapter)、
装饰器模式(Wrapper结尾:XmlRequestWrapper,HttpRequestWrapper,BufferedInputStream)、
代理模式(Spring的Aop就是基于代理模式实现的,Feign的接口,MyBatis的Mapper接口都是基于代理的),
策略模式(Strategy结尾的:HystrixConcurrencyStrategy)、
模板方法模式(Template结尾的:RedisTemplate)、
观察者模式(Spring的监听器:ApplicationListener , MQ消费者监听器RabbitListener)

90.单例有几种实现方式?

饿汉模式、懒汉模式、枚举…
参考文章

91.讲一讲JDK动态代理如何实现?

首先要明确JDK动态代理只允许完成有接口的代理

  1. 对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。
  2. 需要复写invoke方法,这里就是代理类对真实类的增强处理,这里需要传入三个参数(proxy:经过jdk的代理对象(基本上没有作用、 method:实际执行的方法、args:方法中的参数)
  3. 创建代理对象Proxy.newProxyInstance(targetObject.getClass().getInterfaces() -------> 实现类的接口集合(因为一个类可以实现多个接口))

92.事务隔离级别主要解决什么问题?

1.脏读: 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。比如:

事务B把用户的年龄更新成22岁,但是事务还未提交。此时事务A来读用户的年龄,读到了22岁,然后去做自己的处理。之后事务B把用户的年龄回滚到21岁。这个例子中事务A读到的22岁就是脏数据。

2.不可重复读: 事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。比如:

事务B先把用户的余额更新为100元,事务A来读的时候读到的是100元,然后事务B又把余额更新为90元,此时事务A来读的时候读到的就是90元,也就是事务A读到的数据前后不一致。

3.幻读: 事务A对表中的数据进行了修改,涉及到表中的全部行。同时,事务B也修改这个表中的数据,向表中插入一行新数据。那么,事务A发现表中还有自己没有修改的行,就好象发生了幻觉一样。比如:

A先把所有人的余额清零,同时,B往表中插入了一条余额为100的数据。A提交以后,发现竟然还有100的。就好像发生了幻觉一样,这就叫幻读。

4.数据库丢失更新: 数据库在多任务并发的时候会发生两类数据丢失的问题,第一类数据丢失的问题是关于回滚覆盖的,第二类数据丢失的问题是更新覆盖。(详情下滑至100)

93.Mysql的事务隔离级别有哪些,每种隔离级别能解决什么问题?

MySQL的事务隔离级别有四种:
1.读未提交(read-uncommitted):能读到未提交的数据。会出现脏读、不可重复读、幻读。
2.读已提交(read-committed):读已提交的数据。会出现不可重复读和幻读。
3.可重复读(repeatable-read):mysql默认的事务隔离级别,查询的都是事务开始时的数据。只会出现幻读。
4.串行读(serializable):完全串行化读,每次都会锁表,读写互相阻塞。最高隔离级别,不会出现脏读,不可重复读,幻读。但会大大影响系统的性能,一般不用。

94.事务的隔离级别是如何实现的?(InnoDB事务隔离的实现原理是什么)

可以通过锁和MVCC机制(依赖undo log)实现
我们把数据库的读操作分为两类,一是当前读,使用锁机制;一是快照读,使用mvcc

  • 当前读
    • 数据的修改操作(insert update delete)和查询时显示加锁 select(查询条件后加上 lock in share mode & for update)
    • 会锁住要读取的数据以保障数据的一致
  • 快照读 使用的是mvcc机制,就是多版本并发控制。
    • 除当前读之外,普通的select查询为快照读,顾名思义,就是读取的是一个快照版本,以隔离多个事务之间的数据

95.MVCC机制是什么?

MVCC的全称是“多版本并发控制”(Multi Version Concurrency Control),是现代数据库(包括 MySQL、Oracle、PostgreSQL 等)引擎实现中常用的处理读写冲突的手段,目的在于提高数据库高并发场景下的吞吐性能
参考文章

96.Redis 持久化存储方案?

RDB和AOF
RDB中文名为快照/内存快照,它的过程很好理解,就是Redis按照一定的时间周期将目前服务中的所有数据全部写入到磁盘中,一般用作备份。主从复制也是依赖于rdb持久化功能。

AOF全称是Append Only File,从字面上理解就是“只进行增加的文件”,以追加的形式记录redis操作日志的文件。可以最大程度保证redis数据安全,类似于mysql的binlog。

参考文章
参考文章

97.什么是事务?

指作为单个逻辑工作单元(Service方法)执行的一系列操作(数据库操作),要么完全地执行,要么完全地不执行

98.事务的四大特性?

ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
(1)原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。(要么都成功要么都失败)
(2)一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。(可以看成是正确性)
(3)隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
(4)持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。(事务一经提交完成,当前事务状态下就不会再被修改了)

99.InnoDB如何保证原子性和持久性的?

通过undo log(记录了操作前旧的数据,用于回滚)保证原子性,通过redo log(记录了操作之后的数据,用来提交后保存到磁盘)保证持久性

100.解释一下数据库丢失更新问题,如何解决?

数据库更新丢失分为一类(关于回滚覆盖)和二类(更新覆盖)
第一类:由于某个事务的回滚操作,参与回滚的旧数据将其他事务的数据更新覆盖了。
第二类:多个事务同时更新一行数据导致的问题

解决办法:
第一类: SQL92没有定义这种现象,标准定义的所有隔离界别都不允许第一类丢失更新发生。
第二类:基本两种思路,一种是悲观锁,另外一种是乐观锁;悲观锁事务提交之前不让其他事务对该行数据做任何操作(行锁);乐观锁在并发的表上加一个version字段,更新的时候只有版本号大于当前版本号才能更新成功
参考文章


上述问题欢迎大家补充指正!相互学习!


  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值