面试题总结1

ArrayList 和 linkedList 的区别

首先:ArrayList 增删慢 ,查询快。LinkedList 增删快 ,查询慢

看到了一个比较好的回答:

ArrayList 基于动态数组实现,底层的数据结构是数组,并且支持随机访问,有扩容机制
LinkedList 基于双向链表实现,只能顺序访问,也可以作为栈或队列

ArrayList 底层结构是基于动态数组实现的,对内存的要求比较高,需要连续的内存空间,ArrayLsit一般用于查询,它可以根据下标查询,查询比较快,时间复杂度是O(1),但因为它是连续的空间,增删需要移动index后面的数据所以增删比较慢,时间复杂度O(N)。

LinkedList: 底层结构是双向链表数据结构,不需要连续的内存空间,对内存的要求不是很高,LinkedList一般用于增删,因为它是基于双向链表实现的,增删的时候只要断开两个节点的指针,重新建立链路,就能很快插入或删除,时间复杂度是O(1),但是LinkedList是线性的数据存储方式,需要移动指针从前往后依次查找,所有查询起来就比较慢,时间复杂度O(N)。

ArrayList在扩容的时候需要申请新的空间,拷贝原数组的数据,释放旧空间。会有不小的消耗。

你数组只有100的空间时,满了数组扩充到200,此时你只需要存储102个数据,就会浪费98个空间,造成空间的剩余。但是对于链表来说,我们存一个数据,才开辟一个空间给我们存储数据,就避免了空间上的浪费。

使用场景:

  • 如果应用程序对各个索引位置的元素进行大量的存取或删除操作,ArrayList对象要远优于LinkedList对象;
  • 如果应用程序主要是对列表进行循环,并且循环时候进行插入或者删除操作,LinkedList对象要远优于ArrayList对象。

创建线程的方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口
  4. 线程池创建线程

redis 有哪些数据结构

Redis 提供了丰富的数据类型,常见的有五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。

mysql 的 sql 优化的思路:

优化的思路:

第一步:先要确定哪些SQL需要优化,需要借助MySQL提供的本地慢查询日志

第二步:需要借助explain 或 show profiles工具进行慢查询的检测

第三步:根据诊断的情况进行相应的优化

第四步:DBA进行MySQL参数调优

SQL优化的军规

最左前缀法则:主要是针对联合索引,要求查询的条件中必须包含索引字段最左侧的值范围右侧索引失效

不要在索引列上做函数计算,否则索引失效,左模糊索引失效

字符串不加单引号会造成索引失效

不等于条件判断索引失效

分组查询:分组中包含了排序

索引失效的原因:

  1. 符合索引没有遵循最左原则
  2. 索引包含类型转换
  3. 对索引使用了函数比如sum(),round()等
  4. 对索引进行了+、-、*、/、!=、<>、is null、is not null、or等计算

模糊查询索引失效有什么解决办法

ThreadLocal 讲一下

Synchronized 和 Lock 的区别

synchronized 是 JVM 提供的一个关键字,他能对某个代码块或方法自动的加锁解锁,但只能有一个锁对象,而且无法知道是否成功获取锁。
Lock 是 JUC 包下提供的接口,它可以调用 lock、unlock 方法来加锁和解锁,并且 Lock 里面还存在读写锁,锁的灵活性更高,并且可以调用 trylock 来查看锁是否成功获取

它们是悲观锁还是乐观锁

synchronize 和 Lock 都是悲观锁。因为在访问共享资源时,都要先加锁,确保其他线程无法访问。至于 JVM 对 synchronized 有个锁升级的过程,但 synchronized 的设计思想还是悲观锁的。锁升级的本质是为了减少加锁的开销

ThreadLocal 讲一下

ThreadLocal 是通过线程隔离的方式防止任务在共享资源上产生冲突,他是一种本地化存储机制。内部是通过 ThreadLocalMap 实现的,

为什么 ThreadLocal 会出现内存泄露的现象,如何解决

如果用线程池来操作 ThreadLocal 对象就会造成内存泄露,因为线程池在达到核心线程数后,他就会复用创建好的线程。那线程中的 ThreadLocalValuable 对象因为强引用也会一直存在。所以就造成了内存泄露。

解决:ThreadLocal 提供了一个 remove 方法,内部调用 ThreadLocalMap 的 remove 方法,首先找到 key 对应的 entry 也就是 ThreadLocak 对象,然后清除,之后清除没有引用的 key 就好了。

讲一下 MySQL 的隔离级别

MySQL 有四种隔离级别:读未提交、读已提交、可重复读和串行化。读未提交有脏读、不可重复读和幻读问题,读已提交解决了脏读的问题,可重复读解决的是不可重复的问题,串行化就解决了幻读问题

读已提交为什么不能解决不可重复读的问题?

读已提交他只能读已经提交的数据,但是不能保证每次读的数据都是一样的,因为可能有其他事务已经修改数据并提交了。

这时读的数据就和之前的不一样。

原理就是在读已提交级别下,每次查都会生成一个 readView。其他事务也会生成一个 readvirew,事务就会通过这个 undolog 的版本链去找数据。如果其他事务提交后,undolog 也会更新,那么原先事务读到的数据就是提交了的,所以就有不可重复读的问题。在可重复读隔离级别下事务会比较 undolog 中的事务 id,如果大于当前事务的 id,他就会顺着链找到下一个版本,知道事务 id=当前事务 id,这样就解决了不可重复读的问题。那为什么不可重复读了还不能解决幻读问题呢?幻读主要是在插入数据时,如果有唯一索引,要先检查这个数据是否存在。问题就出现在这里,如果不存在,当要执行插入操作时,其他事务先插入了这个位置,那当前事务就插入失败了。这就是幻读。为了解决幻读问题,mysql 就有了临间锁。

有联合索引(a,b,c)使用查询 where b=1 and c=2的方式,这条索引会失效吗?

根据最左前缀匹配来看,条件不包含左列 a,可能会导致索引失效,可以查看执行计划看有没有用到索引。如果条件为 where b=1 and c=2 and a=1。就会用到索引,mysql 有个优化器会重新排列查询条件,使他匹配索引的最左前缀。

什么叫聚集索引和非聚集索引有什么区别

聚簇索引通常只有一个索引列,主键是聚簇索引的默认选择,非聚簇索引就是除聚簇索引外创建的索引

B+树的叶子节点的数据结构是什么,保存的是什么

B+树叶子节点存储的是实际的数据,非聚簇索引保存的就是聚簇索引的关键字的值。叶子节点之间形成的是一个单向链表。

Redis 有哪些数据结构

redis 的数据结构有 String、List、set、Zset、Hash、btimap、HyperLoglogs、Stream。

Redis 的过期策略有哪些

redis 有两种过期策略,定期删除和惰性删除:惰性删除就是服务器不会主动删除过期 key,只有当客户端查询某个数据才判断 ttl 是否过期,如果过期则删除。定期删除就是服务区执行一个定时任务从数据中随机选一些 key 去判断是否过期,如果过期 key 占的比例达到 25%,那么再选一些 key 去判断,重复 5 次之后就结束。

Spring 里面他的的作用域有哪些?没说全

Spring 的作用域:单例,prototype 原型 :每次请求 bean 都会创建一个新的实例,Request 请求:每个 Http请求都会创建一个 bean,session 会话:每个 Http 会话都会创建一个实例,global-session 全局 session。

Spring Boot 自动装配的原理了解吗?

@SpringBootApplication 会触发自动装配 或者@EnableAutoConfiguration会自动去maven中读取每个starter中的spring.factories文件,该文件里配置了所有需要被创建的Spring容器中的bean

在启动 SpringBoot 项目前,我想做一些初始化操作,有哪一些方式?我说 AOP,但不知道怎么实现

使用 @PostConstruct 注解标识 spring 容器初始化时要执行该方法。或者@bean 可以在初始化时调用方法的名字。

MyBatis 的一级缓存和二级缓存的原理或区别讲一下?没了解过

一级缓存就是在每次会话中,mybatis 会将每次的查询的结果放入 SqlSession 对象的缓存中保存,当下一次查询的时候,如果和之前的查询条件是一样的,那就直接调用缓存返回结果,这样就避免了查询数据库。

二级缓存和一级缓存差不多,他是在 Application 级别的缓存。

MQ 如何保证消息的可靠性

消息可靠性就是,生产者发送消息给服务端的时候,MQ 就会给生产者返回一个 ACK 回执,生产者拿到 ACK 后就知道服务端收到了消息。消费者去拉去消息时,他不会立刻返回 ack,而是消费者消费完消息后,再返回 ack 确认

JVM 的内存结构

JVM 内存结构分为线程私有的是虚拟机栈,本地方法栈,程序计数器。线程共享的是堆和方法区。

堆和方法区是线程安全的吗?不是

堆和方法区是线程共享的,所以他不是线程安全的。

说一下类加载机制

类加载机制就是类由一个类加载器负责某个类,类加载器收到一个类的加载请求后,不会立马去加载他,而是将这个请求委托给父类加载器。只有在父类加载器无法加载该类时,才会尝试从当类路径加载。然后他还有一个双亲委派机制,就是他这个请求会一直委托给顶级加载器也就是启动类加载启,启动类加载起无法加载就交给下一级扩展类加载器然后一直向下,应用程序类加载来加载。

最近有没有学习新的技术

对未来的规划

反问环节

posted @ 2025-05-29 08:51  枕月听风  阅读(22)  评论(0)    收藏  举报