这段时间一直在忙着面试,好久没更了,忙完之后把最近这几天遇到的面试题整理一下,建议收藏
我主要是Java后端,主要包括以下两个方面:
这里我只对被常问到的面试题进行一下总结:
个人觉得能否面试成功,不仅需要能回答出自己之前做的项目负责的模块,还要将Java中常见的知识问题熟练地回答出来。
那么下面我就针对面试题进行下总结,还有的话欢迎大家在评论区补充~~
MySQL
Redis
RabbitMQ、Kafka
Spring、SpringMVC、Mybatis、SpringBoot
SpringCloud
一些组件在MySQL中也叫做“键”,它是一个特殊的文件,它保存着数据表里所有记录的位置信息,更通俗的来说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度 索引的创建: -- 创建索引的语法格式 -- alter table 表名 add index 索引名(列名, ..) -- 给name字段添加索引 alter table classes add index my_name (name);
索引有四种类型: 1. 唯一索引:唯一索引是不允许其中任何两行具有相同索引值的索引。当现有数据中存在重复的键值时,大多数数据库不允许将新创建的唯一索引与表一起保存。 2. 主键索引:数据库表经常有一列或多列组合,其值唯一标识表中的每一行。该列称为表的主键。在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。 3. 聚焦索引:在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同。一个表只能包含一个聚集索引。如果某索引不是聚集索引,则表中行的物理顺序与键值的逻辑顺序不匹配。 4. 普通索引
1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引 2.对于多列索引,不是使用的第一部分,则不会使用索引 3.like查询是以%开头 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引 5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引
聚簇索引:将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 非聚簇索引:将数据与索引分开存储,索引结构的叶子节点指向了数据对应的位置
1 查看慢查询,优化SQL语句 2 建立视图 3 创建索引 4 分库分表
id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra
InnoDB MyISAM 1. InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一个事务; 2. InnoDB是聚集索引,使用B+Tree作为索引结构,数据文件是和(主键)索引绑在一起的(表数据文件本身就是按B+Tree组织的一个索引结构),必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。
左连接:左链接是以左边为参照,右边少了补NULL,多了删除 右链接:右链接是以右边为参照,左边少了补NULL,多了删除
第一范式(1NF): 数据表中的每一列(每个字段)必须是不可拆分的最小单元,也就是确保每一列的原子性 第二范式(2NF): 满足1NF后,要求表中的所有列,都必须依赖于主键,而不能有任何一列与主键没有关系,也就是说一个表只描述一件事情; 第三范式(3NF): 必须先满足第二范式(2NF),要求:表中的每一列只与主键直接相关而不是间接相关,(表中的每一列只能依赖于主键); 第四范式(4NF): 略 第五范式(5NF): 略
事务是一组连续的操作,这些操作要么全部成功,要么全部失败回滚
原子性: 一致性: 隔离性: 持久性:
读未提交: 读已提交: 可重复读: 串行化:
编程式事务: 声明式事务:
1 继承Thread类 2 实现Runnable()接口 3 实现Callable()接口 4 使用线程池
corePoolSize 线程池核心线程大小 maximumPoolSize 线程池最大线程数量 keepAliveTime 空闲线程存活时间 threadFactory 线程工厂 workQueue 工作队列 unit 空间线程存活时间单位 handler 拒绝策略
共同点: 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。 不同点: Thread.sleep(long)可以不在synchronized的块下调用,而且使用Thread.sleep()不会丢失当前线程对任何对象的同步锁 wait()会释放锁,并且等待notify()和notifyAll()唤醒
创建 就绪 阻塞 运行 死亡
实现Runnable()接口,因为java是单继承,Thread()已经继承了Runnable接口,所以这里推荐前者
static修饰:锁的是当前类 没有static修饰:锁的是当前对象this
指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。 ReentrantLock和synchronized都是可重入锁
死锁是由于两个或以上的线程互相持有对方需要的资源,导致这些线程处于等待状态,无法执行。 1.破坏“请求和保持”条件 想办法,让进程不要那么贪心,自己已经有了资源就不要去竞争那些不可抢占的资源。比如,让进程在申请资源时,一次性申请所有需要用到的资源,不要一次一次来申请,当申请的资源有一些没空,那就让线程等待。不过这个方法比较浪费资源,进程可能经常处于饥饿状态。还有一种方法是,要求进程在申请资源前,要释放自己拥有的资源。 2.破坏“不可抢占”条件 允许进程进行抢占,方法一:如果去抢资源,被拒绝,就释放自己的资源。方法二:操作系统允许抢,只要你优先级大,可以抢到。 3.破坏“循环等待”条件 将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号顺序(升序)提出
脏读: 不可重复读: 幻读:
接口:里面只定义了方法的声明,当只有一个方法的时候又称为函数式接口(jdk1.8之后),接口可以多继承 抽象类:方法体中既可以有方法的声明,又可以有方法的实现
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 (LinkedList是双向链表,有next也有previous) 2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。 3.对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。
jdk1.8之前:底层是数据+链表 jdk1.8之后:底层是数据+链表+红黑树
面向对象编程,具体可以自行天马星空的脑补。 特点:封装、继承、多态
&:会将判断条件左右都进行判断,即使左边判断已经为false &&:对左右都判断,假如左边为false,那么右边就不判断了
不可以,因为String里面已经有final修饰了,所以不可以再被继承
String: StringBuilder:线程不安全,效率高 StringBuffer:线程安全,效率略低
== 是java提供的等于比较运算符,用来比较两个变量指向的内存地址是否相同.而equals()是Object提供的一个方法.Object中equals()方法的默认实现就是返回两个对象==的比较结果.但是equals()可以被重写,所以我们在具体使用的时候需要关注equals()方法有没有被重写.
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载; 重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。 重载对返回类型没有特殊的要求,不能根据返回类型进行区分。
Throwable包括Error和Exception error:错误,不能编译通过 exception:分为checkException和RuntimeException,RuntimeException可以进行捕获和抛出处理
字符串 哈希 List Set ZSet
字符型和哈希 具体看个人
set key value nx ex 120 实现分布式锁
RDB:将内存中数据磁盘记录下来,可能有些数据的丢失 AOF:只追加写文件
定时 定期 懒汉式删除
缓存穿透:略 缓存击穿:略 缓存雪崩:略
RabbitMQ RocketMQ ActiveMQ Kafka
RabbitMQ 看个人
消息堆积问题产生的原因往往是因为消息发送的速度超过了消费者消息处理的速度。因此解决方案无外乎以下三点: - 提高消费者处理速度 - 增加更多消费者 - 增加队列消息存储上限
消息重复消费的原因多种多样,不可避免。所以只能从消费者端入手,只要能保证消息处理的幂等性就可以确保消息不被重复消费。 而幂等性的保证又有很多方案: - 给每一条消息都添加一个唯一id,在本地记录消息表及消息状态,处理消息时基于数据库表的id唯一性做判断 - 同样是记录消息表,利用消息状态字段实现基于乐观锁的判断,保证幂等 - 基于业务本身的幂等性。比如根据id的删除、查询业务天生幂等;新增、修改等业务可以考虑基于数据库id唯一性、或者乐观锁机制确保幂等。本质与消息表方案类似。
要实现RabbitMQ的高可用无外乎下面两点: - 做好交换机、队列、消息的持久化 - 搭建RabbitMQ的镜像集群,做好主从备份。当然也可以使用仲裁队列代替镜像集群。
RabbitMQ针对消息传递过程中可能发生问题的各个地方,给出了针对性的解决方案: - 生产者发送消息时可能因为网络问题导致消息没有到达交换机: - RabbitMQ提供了publisher confirm机制 - 生产者发送消息后,可以编写ConfirmCallback函数 - 消息成功到达交换机后,RabbitMQ会调用ConfirmCallback通知消息的发送者,返回ACK - 消息如果未到达交换机,RabbitMQ也会调用ConfirmCallback通知消息的发送者,返回NACK - 消息超时未发送成功也会抛出异常 - 消息到达交换机后,如果未能到达队列,也会导致消息丢失: - RabbitMQ提供了publisher return机制 - 生产者可以定义ReturnCallback函数 - 消息到达交换机,未到达队列,RabbitMQ会调用ReturnCallback通知发送者,告知失败原因 - 消息到达队列后,MQ宕机也可能导致丢失消息: - RabbitMQ提供了持久化功能,集群的主从备份功能 - 消息持久化,RabbitMQ会将交换机、队列、消息持久化到磁盘,宕机重启可以恢复消息 - 镜像集群,仲裁队列,都可以提供主从备份功能,主节点宕机,从节点会自动切换为主,数据依然在 - 消息投递给消费者后,如果消费者处理不当,也可能导致消息丢失 - SpringAMQP基于RabbitMQ提供了消费者确认机制、消费者重试机制,消费者失败处理策略: - 消费者的确认机制: - 消费者处理消息成功,未出现异常时,Spring返回ACK给RabbitMQ,消息才被移除 - 消费者处理消息失败,抛出异常,宕机,Spring返回NACK或者不返回结果,消息不被异常 - 消费者重试机制: - 默认情况下,消费者处理失败时,消息会再次回到MQ队列,然后投递给其它消费者。Spring提供的消费者重试机制,则是在处理失败后不返回NACK,而是直接在消费者本地重试。多次重试都失败后,则按照消费者失败处理策略来处理消息。避免了消息频繁入队带来的额外压力。 - 消费者失败策略: - 当消费者多次本地重试失败时,消息默认会丢弃。 - Spring提供了Republish策略,在多次重试都失败,耗尽重试次数后,将消息重新投递给指定的异常交换机,并且会携带上异常栈信息,帮助定位问题。
其实RabbitMQ是队列存储,天然具备先进先出的特点,只要消息的发送是有序的,那么理论上接收也是有序的。不过当一个队列绑定了多个消费者时,可能出现消息轮询投递给消费者的情况,而消费者的处理顺序就无法保证了。 因此,要保证消息的有序性,需要做的下面几点: - 保证消息发送的有序性 - 保证一组有序的消息都发送到同一个队列 - 保证一个队列只包含一个消费者
前者会在预编译之前先对sql语句进行编译,判断语法是否错误,等到真正执行将值替换?占位符,不会导致sql注入,因为将传入的值作为字符串看待,会自动加上引号 后者会导致sql注入,传过来什么就是什么类型的值
一级缓存:同一个sqlsession中 二级缓存:同一个namespace中,默认不开启
使用resultmap 其中association一对一 而collection一对多
默认scope为单例
IOC:控制反转,是一种设计思想。 在没有实现IOC的项目中,我们对象的创建和对象之间的依赖是通过硬编码的方式在程序中,当实现了IOC之后,我们只需要定义的接口,被动的接收对象就可以了。 Spring中实现IOC的方式是DI依赖注入。 AOP:面向切面编程。 传统我们是竖直方向开发我们的项目,DAO-Service-Controller层,而通过AOP,我们可以在某一个切入点增强我们想要的业务和逻辑,并且不影响原来的业务,例如实现日志、事务等
1 一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理 2 DispatcherServlet 接收到请求后, 将根据 请求信息 交给 处理器映射器 (HandlerMapping) 3 HandlerMapping 根据用户的url请求 查找匹配该url的 Handler,并返回一个执行链 4 DispatcherServlet 再请求 处理器适配器(HandlerAdapter) 调用相应的 Handler 进行处理并返回 ModelAndView 给 DispatcherServlet 5 DispatcherServlet 将 ModelAndView 请求 ViewReslover(视图解析器)解析,返回具体 View 6 DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中) 7 DispatcherServlet 将页面响应给用户
@Component @Repository @Mapper @Service @Controller @RequestMapping @GettingMapping @PostMapping 等等
单例模式 工厂模式 观察者模式 代理模式
1 PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是默认的事务传播行为 2 PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。 3 PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。 4 PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。(一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。) 5 PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。、 6 PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。 7 PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。(外层事务抛出异常回滚,那么内层事务必须回滚,反之内层事务并不影响外层事务)
通过@Aspect注解或者配置文件编写 具体 略
前者首先通过类型注入,是Spring包提供的,经常配合@Qualifiler 后者通过名字注入,是有java jdk提供的
1 通过SpringbootApplication启动类,启动项目 2 里面有三个注解@SpringbootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解加载 3 @EnableAutoConfiguration里面有@import注解告诉我们去autoconfigure包下找到META-INF下面的spring.factories下获取全局加载配置类 4 按照条件加载全局配置类 5 生效的全局配置类向容器中注入大量的Bean 6 这时候就可以使用注入的Bean了 7 如果我们想要覆盖某个默认值,只需要去配置文件properties中覆盖就可以了
@Conditional @ConditionalOnBean @ConditionalOnClass @ConditionalOnMissBean @SpringBootApplication 等等
Nacos Eureka Seata Sentinel SpringBootGateway Feign
略
mkdir 创建目录 cp 复制 rm -rf 删库专属 等等
这里是你除了上述面试题之外,你用到的其他技术,比如ELK等等,作为你跟面试官谈资的条件了
上述是我在这两天遇到面试题,部分答案有的也是网上查询的,如有雷同,请联系我删除。
PS:希望大家有什么问题可以补充,有什么不足的也可以在评论区提出来,谢谢大家~