java面试题整理


springcloud alibaba 的组件 及其作用

nacos:Nacos实现了服务的配置中心与服务注册发现的功能,Nacos可以通过可视化的配置降低相关的学习与维护成本,实现动态的配置管理与分环境的配置中心控制

Feign:通过Feign, 我们能把HTTP远程调用对开发者完全透明,得到与调用本地方法一致的编码体验。

Ribbon:提供客户端的负载均衡算法,并提供了完善的配置项如连接超时,重试

gateway:网关是整个微服务API请求的入口,负责拦截所有请求,分发到服务上去。可以实现日志拦截、权限控制、解决跨域问题、限流、熔断、负载均衡,隐藏服务端的ip,黑名单与白名单拦截、授权等

Sentinel:Sentinel在网络依赖服务出现高延迟或者失败时,为系统提供保护和控制;可以进行快速失败,缩短延迟等待时间;提供失败回退(Fallback)和相对优雅的服务降级机制;提供有效的服务容错监控、报警和运维控制手段。

mq的作用是什么,项目中 mq 的 应用场景

一般用来解决应用解耦,异步处理,流量削峰等问题,实现高性能,高可用,可伸缩和最终一致性架构。

使用场景:

1.异步处理

场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有两种 1. 串行的方式 2. 并行的方式

串行方式: 将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。 这有一个问题是,邮件,短信并不是必须的,它只是一个通知,而这种做法让客户端等待没有必要等待的东西.

2.应用解耦

场景:双11是购物狂节,用户下单后,订单系统需要通知库存系统,传统的做法就是订单系统调用库存系统的接口.

订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。

库存系统:订阅下单的消息,获取下单消息,进行库操作。

就算库存系统出现故障,消息队列也能保证消息的可靠投递,不会导致消息丢失。

3.限流削锋
场景:秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。
作用:可以控制活动人数,超过此一定阀值的订单直接丢弃,可以缓解短时间的高流量压垮应用,用户的请求,服务器收到之后,首先写入消息队列,加入消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误页面.

redis的持久化方式,数据类型有哪些

RDB方式:

RDB(Redis DataBase),是redis默认的存储方式,RDB方式是通过快照( snapshotting )完成的。它保存的是某一时刻的数据并不关注过程。RDB保存redis某一时刻的数据的快照

优点

1.RDB是二进制压缩文件,占用空间小,便于传输(传给slaver);
2.主进程fork子进程,可以最大化Redis性能;
3.使用RDB文件来恢复数据较快。

缺点

1、不保证数据完整性,会丢失最后一次快照以后更改的所有数据;
2、父进程在fork子进程的时候如果主进程比较大会阻塞;
AOF方式:

以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以

打开文件看到详细的操作记录

优点:

1、数据安全,Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也

是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据

将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁

盘中。。

2、通过 append 模式写文件,即使中途服务器宕机也不会破坏已经存在的内容。

3、AOF 机制的 rewrite 模式。定期对AOF文件进行重写,以达到压缩的目的

缺点:

1、AOF 文件比 RDB 文件大,且恢复速度慢。

2、数据集大的时候,比 rdb 启动效率低。

3、运行效率没有RDB高

AOF文件比RDB更新频率高,优先使用AOF还原数据。

AOF比RDB更安全也更大

RDB性能比AOF好

如果两个都配了优先加载AOF

常用的集合及其特性

List集合:

1、ArrayList(最常用)

底层是动态数组,故优缺点同数组:查询快,增删慢
线程不安全,效率高

2、LinkedList

底层是链表,(巧记:和数组优缺点互补)查询慢,增删快
线程不安全,效率高

Set集合:

1、LinkedHashSet

底层是:哈希表(元素不可重复)+链表(有序,因为链表记录每个元素都记录后一个元素的地址)

1、去掉重复元素

2、具备先进先出的特点

1、HashSet

底层是哈希表
向HashSet集合中放入新元素的执行顺序:
1、调用Hashcode()函数,看是否已经有相同的哈希值,如果没有则放入该新元素,如果有则调用equals()函数逐个比对,如果没有,则放入,如果有,则不执行添加元素操作。

3、TreeSet

底层是红黑树,自然平衡二叉树,因为是二叉树结构所以集合内元素自动按升序排列(升序是其最大特征)。
元素不可重复

Map集合:

1、HashMap

底层是哈希表(哈希表的本质是数组+链表)
把它的键套用HashSet的规则即可,只是每个键都附带了其各自的值。

2、LinkedHashMap

底层是哈希表和链表
把它的键套用LinkedHashSet的规则即可,只是每个键都附带了其各自的值。

3、Hashtable

底层是哈希表(哈希表的本质是数组+链表),十分类似于HashMap
与HashMap唯一区别:Hashtable线程安全,效率低,不允许null键null值
HashMap线程不安全,效率高,允许null键null值

4、TreeMap

底层是红黑树,自然平衡二叉树
其键的存储类似于TreeSet,只是每个键都附带了其各自的值(value)。

springbean 的生命周期

1、解析类得到BeanDefinition

2、如果有多个构造方法,则要推断构造方法

3、确定好构造方法后,进行实例化得到一个对象

4、对对象中的加了@Autowired注解的属性进行属性填充

5、回调Aware方法,比如BeanNameAware,BeanFactoryAware

6、调用BeanPostProcessor的初始化前的方法

7、调用初始化方法

8、调用BeanPostProcessor的初始化后的方法,在这里会进行AOP

9、如果当前创建的bean是单例的则会把bean放入单例池

10、使用bean

11、Spring容器关闭时调用DisposableBean中destory()方法

aop的底层原理

Spring AOP使用动态代理技术在运行期织入增强的代码,他使用了两种代理机制:一种是基于JDK的动态代理;另一种是基于CGLib的动态代理。

JDK动态代理

JDK动态代理主要涉及两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。

CGLib动态代理

CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。需要注意的是,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final或private方法进行代理。

elk 是什么

ELK是一套针对日志数据做解决方案的框架,分别代表了三款产品: - E: ElasticSearch(ES),负责日志的存储和检索; - L:Logstash,负责日志的收集,过滤和格式化; - K:Kibana,负责日志的展示统计和数据可视化;

arraylist和 hashmap 底层特征

arraylist

底层实现:是基于数组实现的,是一个动态数组,其容量能自动增长。

ArrayList进行添加元素的操作的时候是分两个步骤进行的,即第一步先在object[size]的位置上存放需要添加的元素;第二步将size的值增加1。由于这个过程在多线程的环境下是不能保证具有原子性的,因此ArrayList在多线程的环境下是线程不安全的。他的扩容机制就是:当第一次插入元素时才分配10(默认)个对象空间。假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10 (如下图一);之后扩容会按照1.5倍增长。也就是当添加第11个数据的时候,Arraylist继续扩容变为10*1.5=15(如下图二);当添加第16个数据时,继续扩容变为15 * 1.5 =22个

hashmap

底层实现:数组+链表+红黑树

jdk8开始链表高度到8、数组长度超过64,链表转变为红黑树,元素以内部类Node节点存在

计算key的hash值,二次hash然后对数组长度取模,对应到数组下标,

如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组,

如果产生hash冲突,先进行equal比较,相同则取代该元素,不同,则判断链表高度插入链表,链

表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表

key为null,存在下标0的位置

内存泄漏与内存溢出????

内存泄露 memory leak :是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存 ,迟早会被占光 会最终会导致 内存溢出。

内存溢出 out of memory :是指程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory

引起内存溢出的原因:

1、内存中加载的数据量过于庞大,如一次从数据库取出过多数据;

2、 集合类中有对对象的引用,使用完后未清空,使得 JVM 不能回收;

3、代码中存在死循环或循环产生过多重复的对象实体;

4、启动参数内存值设定的过小

内存溢出的解决方案:

第一步,修改 JVM 启动参数,直接增加内存。 (-Xms , -Xmx 参数一定不要忘记加。 )

第二步,检查错误日志,查看 “ OutOfMemory ”错误前是否有其它异常或错误。

第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

重点排查以下几点:

1、检查对数据库查询中,是否有一次获得全部数据的查询

2、检查代码中是否有死循环或递归调用。

3、检查是否有大循环重复产生新对象实体。

4、 检查 List 、 MAP 等集合对象是否有使用完后,未清除的问题

延时消息的实现方式

队列ttl+死信exchange

使用两个队列,一个队列接收消息不消费,等待指定时间后消息死亡,再由该队列绑定的死信exchange再次将其路由到另一个队列提供业务消费。

我这里也简单介绍下:
rabbitmq 可以给 消息 和 队列 设置 ttl(生存时间)

  • 队列设置:x-message-ttl=60000 (队列中所有消息都只有60s存活时间)

  • 指定消息设置:expire=60000 指定消息只有60s存活时间

  • 先声明一个消费队列 queue_dlx,用来接收死信消息,并提供消费;

然后声明一个死信exchange_dlx, 绑定 queue_dlx,接收消息后路由至queue_dlx;

声明一个延迟队列,queue_delay, 用来接收业务消息,但不提供消费,等待消息死亡后转至死信exchange。(即延迟)

声明一个exchange,由业务发送消息到exchange,然后转至queue_delay.

将不同延时等级的消息放入不同queue中,然后转发到目标topic

先声明一个topic类型的死信exchange,用来转发死信(延时后)的消息。

定义一组用来暂存消息的延时delayQueue,并设置死信exchange,死信路由默认为消息原始路由。

再声明一个topic类型的延时exchange与延时queue绑定,绑定路由为$delayQueueName.#, 即通过routing key前缀识别应转发到哪个延时队列。

然后声明需要消费的业务队列 bizQueue,再将其绑定到死信exchange上,路由关系为*.$bizQueueName (也可以是正常的routingKey, 这个死信exchange的绑定关系就和普通定义发送消息的定义差不多了)

最后发送消息时,根据其延时等级,在routingKey前面拼接上对应的 delayQueueName 就可以了。

img

什么情况下消息会进死信队列

  • 消息被拒绝(basic.reject / basic.nack),并且requeue = false

  • 消息TTL过期

  • 队列达到最大长度

相关