- 1、如何用 Java 实现简单的计算器?
- 2、怎么实现一个点赞功能?
- 3、Spring事务的理解?
- 4、线程怎么保持同步(在同步线程里怎么保证数据处理正确)
- 5、 商品上下架如何实现前后台消息同步?
- 6、Sql语句优化有哪些?数据库的优化怎样优化?
- 7、乐观锁和悲观锁的解释及其应用场景
- 8、socket的通信的原理 与TCP或UDP的关系
- 9、过滤器和拦截器区别和项目中如何应用
- 10、gc回收机制的原理
- 11、手写懒汉式和饿汉式,哪些是线程安全的,以及懒汉式和饿汉式的区别
- 12、ConcurrentHashMap实现原理及源码分析
- 13、Redis的缓存击穿及解决方案
- 14、srpingcloud常用组件
- 15、索引的建立原则, 如何避免索引失效
- 16、常见的线程池种类,线程池策略及线程池核心参数
- 17、HashMap的原理
- 18、springcloud中的熔断器具体讲一下
- 19、代理模式在项目中是怎么应用的?
- 20、String 是最基本的数据类型吗?
- 21、【JavaWeb基础-框架】持久层:简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系。
- 22、Java类加载过程
- 23、描述一下JVM加载Class文件的原理机制?
- 24、什么是类加载器,类加载器有哪些?
- 25、Java中垃圾收集的方法有哪些?
- 26、如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?
- 27、finalize()方法什么时候被调用?析构函数(finalization) 的目的是什么?
- 28、java常用的集合
- 29、请你解释为什么重写equals还要重写hashcode?
- 30、请你介绍一下map的分类和常见的情况
1、如何用 Java 实现简单的计算器?
抽象工厂模式,计算方式变成抽象工厂,拆分对应的工厂,
2、怎么实现一个点赞功能?
准备redis三种键分别为: product_comment_likes:{%d}【评论赞成数量维护】(hash结构,%d=productId,key=评论id,value=赞成数量) user_comment_likes:{%d}:%d 【用户侧评论赞成数量维护】(set结构,{%d}=userId,%d=productId,value=commentId) comment_like_logs【评论赞成数量日志】(list结构,value结构=productId:commentId|memoryAgreeCount) 说明:该接口全部采用基于redis缓存操作,使用lua脚本保证原子性。{}保证rediskey在分布式下数据保证在同一redis的哈希槽中。最后由定时器去同步到mysql 接口说明:用户请求一次点赞,再请求一次取消点赞。
3、Spring事务的理解?
Spring支持编程式事务管理以及声明式事务管理两种方式
- 编程式事务管理 编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。
- 声明式事务管理 声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
4、线程怎么保持同步(在同步线程里怎么保证数据处理正确)
通过Object的wait和notify 通过Condition的awiat和signal 通过一个阻塞队列 通过两个阻塞队列 通过SynchronousQueue 通过线程池的Callback回调 通过同步辅助类CountDownLatch 通过同步辅助类CyclicBarrier
5、 商品上下架如何实现前后台消息同步?
在分布式缓存中设置轮询位。例如在分布式缓存中,某个节点Prodct1: Normal表示商品1正常,后台订阅该位置,当值发生变动,回调业务操作即可。 当然也有复杂的,用MQ、AKKA等机制来通知等等..
6、Sql语句优化有哪些?数据库的优化怎样优化?
可以从几个方面考虑: 优化表结构
- 尽量使用数字型字段。若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
- 尽可能的使用 varchar 代替 char。可变长字段存储空间小,可以节省存储空间。
- 当索引列大量重复数据时,可以把索引删除掉。比如有一列是性别,只有男、女,这样的索引是无效的。
优化查询
- 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
- 应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描。建议使用 union 替换 or
- 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
- in 和 not in 也要慎用,否则会导致全表扫描
- 优化嵌套查询时可以将子查询尽量替换为多表连接查询(JOIN)
- 任何查询也不要出现select *!
索引优化
- 尽量使用复合索引,而少使用单列索引
- 最左前缀法则:如果索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列
- 对作为查询条件和 order by的字段建立索引
- 对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
其他优化 当进行数据的insert操作的时候,可以考虑采用以下几种优化方案:
- 如果需要同时对一张表插入很多行数据时,应该尽量使用多个值表的insert语句,这种方式将大大的缩减客户端与数据库之间的连接、关闭等消耗。使得效率比分开执行的单个insert语句快。
- 手动开启事务后在进行数据插入
- 数据有序插入(按主键)
7、乐观锁和悲观锁的解释及其应用场景
悲观锁:特点是总觉得正在操作的数据会被别人修改,所以自己在对数据处理的过程中会将数据进行加锁。 悲观锁实现:在MySQL中使用悲观锁,需要将自动提交关闭。使用select xxx from xxx for update这样就对查询结果加上了悲观锁(如果查询条件走了索引,则锁定的仅为查询结果集;若查询条件未走索引,则对全表加锁) 乐观锁:特点是在对数据进行操作时不会对数据或表加锁,而是在提交时判断操作过程中是否有第三方对数据进行了修改,如果发现冲突,则返回错误信息。 乐观锁实现: 1、利用数据版本号(version)机制是乐观锁常用的一种实现方式,一般是在数据库表中增加一列数据类型的“version”字段,查询数据时,将“version”字段读出,数据每更新一次就对version值+1。当提交更新时,判断数据库对应记录的version字段的当前值是否与第一次查询出的值一致,若相等,则允许更新,否则认为数据过期,返回更新失败。 2、利用时间戳,类似于version。在数据库中新增时间戳字段。提交前进行比较。 3、悲观锁用在写比较多的情况下,乐观锁用在读比较多的情况下。
8、socket的通信的原理 与TCP或UDP的关系
scoket的通信的原理 与TCP或UDP的关系
解析: 套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。 应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应 用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
tcp与udp区别: 基于连接与无连接 对系统资源的要求(TCP较多,UDP少) UDP程序结构较简单 流模式与数据报模式 TCP保证数据正确性,UDP可能丢包 TCP 保证数据顺序,UDP不保证
java编程区别: tcp是java.net.ServerSocket(用于服务器端)和java.net.Socket(用于客户端); UDP是java.net.DatagramSocket.
9、过滤器和拦截器区别和项目中如何应用
解析: 过滤器:在目标资源之前进行的操作 过滤所有的内容,比如 action、servlet、jsp、html 拦截器:在目标资源之前进行的操作 不能拦截所有的内容,拦截 action,不能拦截 jsp,不能拦截 html 拦截器和过滤器之间有很多相同之处,但是两者之间存在根本的差别。其主要区别为以下几点: 1)拦截器是基于 JAVA 反射机制的,而过滤器是基于函数回调的。 2)拦截器不依赖于 Servlet 容器,而过滤器依赖于 Servlet 容器 3)拦截器只能对 Action 请求起作用,而过滤器可以对几乎所有的请求起作用。 4)拦截器可以访问 Action 上下文、值栈里的对象,而过滤器不能 5)在 Action 的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调 用一次。
分析: 从灵活性上说拦截器功能更强大些,过滤器能做的事情,他都能做,而且可以在请求前,请求后执行,比较灵活。过滤器主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用拦截器。不过还是根据不同情况选择合适的。
10、gc回收机制的原理
解析: 用户Java程序运行过程中,Java虚拟机提供了另外一个系统级的线程,专门负责回收不再被使用的对象占用的内存,这一过程称为垃圾回收。垃圾回收需要对堆内存中的对象进行标记,并对堆内存进行整理。这一过程的某些阶段需要暂时终止用户Java线程,等回收工作完成后再恢复执行。因此,频繁地触发虚拟机垃圾回收操作的行为会影响程序的运行效率。那么什么情况下会频繁地出发垃圾回收操作呢?- 比如:堆内存设置过小- 再比如:程序频繁地分配大型局部对象数组 分析: 在有些公司的面试中,可能会问到一些虚拟机的底层原理,而gc回收机制就是常问的一种,当问及这类问题的时候,面试官实际上是想考察求职者对底层原理的了解程度,从而推断出作为一名程序员的自主学习能力,在平时开发中,在敲代码之外,是否有时间去研究代码的源码,和JVM的原理,如果想了解JVM原理和机制方面的知识,推荐大家看《深入了解Java虚拟机》。 拓展: 涉及到底层原理,还有一种常考的方式就是,虚拟机的内存划分,堆、栈、方法区等,所以面试者要对这些底层概念分清楚,明确他们存储的是什么,以及堆栈的特点。
11、手写懒汉式和饿汉式,哪些是线程安全的,以及懒汉式和饿汉式的区别
饿汉式
class LazyMan{
private LazyMan(){};
private static LazyMan lazyMan = new LazyMan();
public static LazyMan getLazyMan(){
return lazyMan;
}
}
懒汉式
class LazyMan{
private LazyMan(){};
private static LazyMan lazyMan;
public static LazyMan getLazyMan(){
if(lazyMan==null)
return new LazyMan();
}
}
双重检测锁,同时对象需要添加volatile关键词确保对他的操作是原子性的。为什么需要这样做是因为指令重排的存在,实例化操作实际上是三步,分配内存,指向空间,实例化,然而这三步的顺序不是一成不变的,可能是1,2,3,也可能是3,1,2。那么两个线程的操作就会出现问题了,需要volatile防止指令重排。代码如下:
class LazyMan {
private LazyMan() {};
private static volatile LazyMan lazyMan;
public static LazyMan getLazyMan() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
12、ConcurrentHashMap实现原理及源码分析
. HashMap不是线程安全:
在并发环境下,可能会形成环状链表(扩容时可能造成,具体原因自行百度google或查看源码分析),导致get操作时,cpu空转,所以,在并发环境中使用HashMap是非常危险的
- HashTable是线程安全的: HashTable和HashMap的实现原理几乎一样, 差别:1.HashTable不允许key和value为null; 2.HashTable是线程安全的。 HashTable线程安全的策略实现代价却比较大,get/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁,多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,见下图: 3.ConcurrentHashMap是线程安全的:
13、Redis的缓存击穿及解决方案
缓存击穿, 这个跟缓存雪崩有点像, 但是又有一点不一样, 缓存雪崩是因为大面积的缓存失效, 数据库崩溃了, 而缓存击穿不同的是缓存击穿是指存在一个热点数据Key, 有请求不断来访问这个Key,这么多请求在同一段时间内访问这个热点数据, 当这个 Key 失效时间到了的时候, 持续的这么多请求直接怂到数据库上了, 就在这个 Key 值上击穿了缓存. 缓存击穿是指某一个热点数据缓存中没有但数据库中有数据(一般是缓存时间到期)。这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
- 设置热点数据永远不过期。
- 接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些服务不可用时候,进行熔断,失败快速返回机制。
- 设置互斥锁。在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,直接走缓存。(可以使用 Redis 分布式锁)
14、srpingcloud常用组件
Springcloud的主要组件及各自的作用
解析:
- Eureka
提供服务注册和发现, 是注册中心. 有两个组件: Eureka服务端和Eureka客户端 Eureka服务端: 作为服务的注册中心, 用来提供服务注册, 支持集群部署. Eureka客户端: 是一个java客户端, 将服务注册到服务端, 同事将服务端的信息缓存到本地, 客户端和服务端定时交互.
- Ribbon
Ribbon是Netflix发布的云中服务开源项目. 给客户端提供负载均衡, 也就是说Ribbon是作用在消费者方的. 简单来说, 它是一个客户端负责均衡器, 它会自动通过某种算法去分配你要连接的机器. SpringCloud认为Ribbon这种功能很好, 就对它进行了封装, 从而完成负载均衡. Ribbon默认负责均衡策略是轮询策略.
- Hystrix熔断器
有时候可能是网络问题, 一些其它问题, 导致代码无法正常运行, 这是服务就挂了, 崩溃了. 熔断器就是为了解决无法正常访问服务的时, 提供的一种解决方案. 解决因为一个服务崩溃而引起的一系列问题, 使问题只局限于这个服务中,不会影响其他服务. Hystrix提供了两种功能, 一种是服务降级, 一种是服务熔断.
- Feign: 远程调用组件
后台系统中, 微服务和微服务之间的调用可以通过Feign组件来完成. Feign组件集成了Ribbon负载均衡策略(默认开启的, 使用轮询机制), Hystrix熔断器(默认关闭的, 需要通过配置文件进行设置开启) 被调用的微服务需要提供一个接口, 加上@@FeignClient(“url”)注解 调用方需要在启动类上加上@EnableFeignClients, 开启Feign组件功能.
- Zuul: 路由/网关
对于项目后台的微服务系统, 每一个微服务都不会直接暴露给用户来调用的, 但是如果用户知道了某一个服务的ip:端口号:url:访问参数, 就能直接访问你. 如果再是恶意访问,恶意攻击, 就会击垮后台微服务系统.因此, 需要一个看大门的大boss, 来保护我们的后台系统. Zuul类似Nginx, 反向代理功能. 用户访问服务, 首先会到Zuul中心, Zuul去Eureka中拉取服务, 获取服务列表, 获取具体的地址, 再通过具体的地址去访问目标微服务. Zuul提供了过滤和路由两个功能, 过滤可以分配权限, 允许谁可以访问谁不可以访问, 路由则是去Eureka拉取服务提访问地址. Zuul网关也继承了Ribbon负载均衡和Hystix熔断机制.
15、索引的建立原则, 如何避免索引失效
解析:
一. 索引的建立原则
索引的设计需要遵循一些已有的原则, 这样便于提升索引的使用效率, 更高效的使用索引.
1. 对查询频次较高, 且数据量比较大的表, 建立索引.
2. 索引字段的选择, 最佳候选列应当从where子句的条件中提取, 如果where子句中的组合比较多, 那么应当挑选最常用, 过滤效果最好的列的组合.
3. 如果where后有多个条件经常被用到, 建议建立符合 索引, 复合索引需要遵循最左前缀法则, N个列组合而成的复合索引, 相当于创建了N个索引.
复合索引命名规则 index表名列名1列名2列明3
比如:create index idx_seller_name_sta_addr on tb_seller(name, status, address)
复合索引需要遵循最左前缀法则, N个列组合而成的复合索引, 相当于创建了N个索引. 最左前缀法则后面会详细介绍~
3. 使用唯一索引, 区分度越高, 使用索引的效率越高.
4. 索引并非越多越好, 如果该表赠,删,改操作较多, 慎重选择建立索引, 过多索引会降低表维护效率.
5. 使用短索引, 提高索引访问时的I/O效率, 因此也相应提升了Mysql查询效率.
6. 多表连接的字段上需要建立索引,这样可以极大提高表连接的效率.
7. 排序字段上, 因为排序效率低, 添加索引能提高查询效率.
二.如何避免索引失效
1. 遵循最左前缀法则
如果在查询的时候, 使用了复合索引, 要遵循最左前缀法则, 也就是查询从索引的最左列开始, 并且不能跳过索引中的列.如果不包含最左边的索引列, 则其他索引不生效;如果包含了最左边的索引列, 但是跳过了一列直接索引了复合索引的第三列, 则第三列的索引不生效.
2. 不要在索引上使用运算, 否则索引也会失效.
3. 字符串不加引号, 造成索引失效.
4. 尽量使用覆盖索引, 避免select *, 这样能提高查询效率.
5. or关键字连接
用or分割开的条件, 如果or前面的列有索引, or后面的列没有索引, 那么查询的时候索引会失效, 如果一定要用or查询, 可以考虑下or连接的条件列都加索引, 这样就不会失效了.
6. like模糊查询
在使用like模糊查询时, 如果like%也就是%加在后面索引不会失效, 如果%lik或%like%也就是%加在前面, 索引会失效.
如果查询的列占整张表的绝大多数, 那么就会全表扫描, 不会走索引. 所以在查询的时候走不走索引不能百分百肯定.
也就是说如果查找一个稀有数据, 如果建立了索引, 就会走索引. 如果查询的数据表达绝大多数都符合条件, 就全表扫描, 不走索引.
16、常见的线程池种类,线程池策略及线程池核心参数
解析:
一. 常见线程池有六种
1. newCachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小.
2. newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
3. newSingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序 按照任务的提交顺序执行。
4. newSingleThreadScheduleExecutor: 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期执行.
5. newScheduledThreadPool:创建一个线程池,它可安排在给定延迟后运行命令或者定期的执行, 线城池数量不固定.
6. newWorkStealingPool: 创建一个带并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如不传并行级别参数,将默认为当前系统的CPU个数.
二. 线程池拒绝策略
线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了, 再也塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。
1. ThreadPoolExecutor.AbortPolicy(系统默认): 丢弃任务并抛出RejectedExecutionException异常,让你感知到任务被拒绝了,我们可以根据业务逻辑选择重试或者放弃提交等策略
2. ThreadPoolExecutor.DiscardPolicy: 也是丢弃任务,但是不抛出异常,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。
3. ThreadPoolExecutor.DiscardOldestPolicy: 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程),通常是存活时间最长的任务,它也存在一定的数据丢失风险
4. ThreadPoolExecutor.CallerRunsPolicy: 由调用线程处理该任务
三. 线程池核心参数
1. corePoolSize(核心线程数)
(1)核心线程会一直存在,即使没有任务执行;
(2)当线程数小于核心线程数的时候,即使有空闲线程,也会一直创建线程直到达到核心线程数;
(3)核心线程数代表我能够维护常用的线程开销.
2. maxPoolSize(最大线程数)
(1)线程池里允许存在的最大线程数量;
(2)线程池里允许存在的最大线程数量。当任务队列已满,且线程数量大于等于核心线程数时,会创建新的线程执行任务。
3. queueCapacity(阻塞队列)
当核心线程都在运行,此时再有任务进来,会进入任务队列,排队等待线程执行。
4. keepAliveTime(线程空闲时间)
(1)当线程空闲时间达到keepAliveTime时,线程会退出(关闭).
(2)如果allowCoreThreadTimeout=true,则线程会继续退出直到为0个。
(3)默认allowCoreThreadTimeout=false, 也就是说线程池中的线程退出, 直到=核心线程数.
5. unit: 和keepAliveTime配合使用, 时间单位, 可以为秒, 分钟.
6. allowCoreThreadTimeout(允许核心线程超时, 默认为false, jdk1.6后加的新特性)
7. ThreadFactory:
线程创建的工厂,新的线程都是由ThreadFactory创建的,系统默认使用的是
Executors.defaultThreadFactory创建的,用它创建出来的线程的优先级、组等都是一样
的,并且他都不是守护线程。我们也可以使用自定义的线程创建工厂,并对相关的值进
行修改
8. rejectedExecutionHandler(任务拒绝处理器)
(1)当线程数量达到最大线程数,且任务队列已满时,会拒绝任务;
(2)调用线程池shutdown()方法后,会等待执行完线程池的任务之后,再shutdown()。如果在调用了shutdown()方法和线程池真正shutdown()之间提交任务,会拒绝新任务。
17、HashMap的原理
一.JDK1.8之前(数组+链表) (1) new HashMap 创建一个空对象, 如果不指定数组长度, 默认数组长度16. 如果指定了数组长度, 会找一个和该数组临近的2的n次方数据作为长度. 数组中存入的是Entry
的entry对象. 创建的时候还加入了0.75的负载因子. 这个负载因子和扩容rehash()方法有关. (2) map.put(k,v)方法 首先,先判断key存放的位置, 判断出位置了, 然后将entry对象放到数组对应的位置中. key通过hashcode方法(hashmap内部的hashcode扰动函数)算出hash值, 然后通过(数组长度-1)&hash值, 得到一个位于0-15区间的数字, 这就是对应数组中的下标了. 如果equals()方法为true, 则说明就是同一个key了, value值就会覆盖; 如果equals()方法为false,则不是一个key, 就在数组对应索引位置变为链表存储新Entry . 上一步说到的链表是拉链法: 将链表和数组相结合.也就是说创建一个链表数组,数组中每一格就是一个链表.若约到哈希冲突,则将冲突的值加到链表中即可. 插入链表的时候是首插法, 也就是链表中的新元素排在旧元素前面. 因为都会认为新存入的数据是被应用最多的, 所以新数据排在旧数据前面. (3) map.get(k)实现原理 先调用k的hashCode()方法得出hash值,并通过hash值&(数组长度-1)运算转换成数组的下标。 在通过数组下标快速定位到某个位置上, 这个位置上什么都没有,则返回null. 如果这个位置上有单向链表,那么它就会拿着参数K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value 二. JDK1.8之后(数组+链表+红黑树) (1) new HashMap JDK1.8以后只有在put数据的时候才会创建对象, 而且每个数组中存的是node对象. 如果不指定数组长度, 默认数组长度16. 如果指定了数组长度, 会找一个和该数组临近的2的n次方数据作为长度. 数组中存入的是Node 的node对象. 创建的时候还加入了0.75的负载因子. 这个负载因子和扩容rehash()方法有关. 后续详细介绍. (2) map.put(k,v)方法 和JDK1.8之前不同的是, 但是这里的链表当达到一定程度后会转为红黑树. 如果链表的长度超过8则转为红黑树, 也就是该位置存储的是数组+红黑树结构. 当链表长度小于等于6时又变为链表, 也就是该位置存储的是数组+链表结构. 链表法采用的是尾插法. 也就是新的元素排在当前元素的后面. JDK1.8 的时候, 数组中存的是node对象, 而不是entry对象了, node对象包括三部分(如下图所示), (3) map.get(k)原理 和JDK1.8之前一样. 2个回答 写回答
- 2022-01-12 23:23HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。当链表长度超过树化阈值 8 时,先尝试扩容来减少链表长度,如果数组容量已经 >=64,才会进行树化。
LL.LEBRON
- JDK1.7:数组 + 链表
- JDK1.8:数组 + (链表 | 红黑树)
18、springcloud中的熔断器具体讲一下
雪崩效应常见场景
- 硬件故障:如服务器宕机,机房断电,光纤被挖断等。
- 流量激增:如异常流量,重试加大流量等。
- 缓存穿透:一般发生在应用重启,所有缓存失效时,以及短时间内大量缓存失效时。大量的缓存不命中,使请求直击后端服务,造成服务提供者超负荷运行,引起服务不可用。
- 程序BUG:如程序逻辑导致内存泄漏,JVM长时间FullGC等。
- 同步等待:服务间采用同步调用模式,同步等待造成的资源耗尽。
19、代理模式在项目中是怎么应用的?
事务管理
20、String 是最基本的数据类型吗?
解析:
不是,他是引用类型。基本数据类型只有8个,char、byte、short、int、long、float、double和boolean。
【整型】byte、short、int和long,分别是1、2、4和8字节。
【浮点型】float和double,分别是4和8字节。
【字符型】char,2字节。
【布尔型】boolean,只有true和false。
21、【JavaWeb基础-框架】持久层:简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系。
Mybatis将所有Xml配置信息都封装到All-In-One重量级对象Configuration内部。在Xml映射文件中,
标签会被解析为ParameterMap对象,其每个子元素会被解析为ParameterMapping对象。 标签会被解析为ResultMap对象,其每个子元素会被解析为ResultMapping对象。每一个