1. ==和equals的区别?
面试版:
== 比较引用是否相同,equals 比较内容是否相等
真正理解版:== 比较是不是同一个对象;equals 比较这个类定义的"相等规则"
2. 说一下HashMap的底层原理
数组:找到大概位置
↓
链表:解决多个数据撞到同一个位置
↓
红黑树:解决链表太长导致查询慢
3. 说一下ArrayList和LinkedList的区别
数组擅长找位置,链表擅长改连接。
ArrayList 底层基于动态数组实现,查询通过下标直接定位,时间复杂度 O(1),但是中间插入删除需要移动元素,效率较低。LinkedList 底层基于双向链表实现,插入删除只需要修改指针,效率较高,但是查询需要遍历,时间复杂度 O(n)。另外 LinkedList 每个节点需要保存前后指针,所以占用内存更多。实际开发中查询和遍历较多一般选择 ArrayList。
4. 怎么搭建一个springboot项目?还有你们用的springboot的版本是什么?
我们项目一般通过 Spring Initializr 创建 Spring Boot 项目,选择 Maven 管理依赖,引入 Spring Web、MyBatis、数据库驱动等依赖。项目启动类使用 @SpringBootApplication 注解,通过 SpringApplication.run 启动 Spring 容器。配置文件使用 application.yml 管理端口、数据库等配置。
我们项目目前使用 Spring Boot 2.7.x(或者3.x,根据实际情况),配合 JDK 8/17,主要使用 Spring MVC、MyBatis、MySQL 等技术。
5. @RestController和@Controller的区别
@Controller 是 Spring MVC 中用于处理页面请求的控制器,方法返回值默认会被视图解析器解析成页面。@RestController 是 Spring 4 提供的注解,本质上等于 @Controller 加 @ResponseBody,返回值会直接写入 HTTP 响应,通常返回 JSON 数据,用于开发 RESTful 接口
6. 什么是IOC?什么是DI?
IOC(控制反转)是指把对象的创建和管理交给 Spring 容器,而不是由代码主动创建。DI(依赖注入)是 IOC 的具体实现方式,Spring 容器创建 Bean 后,通过构造方法、Setter 或字段方式,把对象依赖注入到需要它的类中。简单来说,IOC 解决对象由谁创建,DI 解决对象如何传递。
7. @Autowired和@Resource的区别
@Autowired 是 Spring 提供的注解,默认按照类型进行依赖注入,如果存在多个相同类型的 Bean,需要配合 @Qualifier 指定 Bean 名称。@Resource 是 Java 提供的注解,默认按照名称注入,如果名称找不到再按照类型匹配。实际 Spring Boot 开发中两者都可以使用,现在更推荐使用构造方法注入。
8. get请求和post请求的区别
GET主要用于获取资源,POST主要用于提交数据。
ET 和 POST 都是 HTTP 请求方法。GET 主要用于获取资源,请求参数通常放在 URL 中,具有幂等性,适合查询。POST 主要用于提交数据,请求参数通常放在请求体中,常用于新增、修改操作。两者安全性主要取决于 HTTPS,而不是 GET 或 POST 本身。
9. 你说一下过滤器和拦截器的区别
过滤器(Filter)是 Servlet 层面的,拦截器(Interceptor)是 Spring MVC 层面的。
Filter 和 Interceptor 都可以实现请求拦截,但是作用层级不同。Filter 属于 Servlet 规范,在请求进入 Spring MVC 之前执行,能处理所有请求,但是不知道具体 Controller 信息。Interceptor 属于 Spring MVC,在 DispatcherServlet 之后、Controller 执行之前执行,可以获取 Handler 信息,更适合做登录认证和权限控制。执行顺序上 Filter 比 Interceptor 更早。
10. 你们公司是怎么管理分支的?遇到代码冲突怎么解决?
们项目一般采用 Git 分支管理,比如 master/main 作为生产分支,develop 作为开发分支,每个需求创建独立 feature 分支。开发完成后提交 Merge Request,由其他成员 Code Review 后合并。遇到代码冲突时,首先通过 git status 查看冲突文件,然后手动比较双方修改,根据业务保留或合并代码,解决后 git add 和 git commit 完成合并。平时通过及时拉取最新代码、小功能提交来减少冲突。
11. 你们是怎么保存用户登录信息的?你们的jwt需要在服务端存储吗?说一下jwt的组成部分有哪些?如何防篡改?
我们用户登录成功后,服务端生成 JWT Token 返回客户端,客户端保存 Token,后续请求通过 Authorization Header 携带 Token。JWT 本身是无状态认证,正常情况下服务端不需要保存 Token,服务端通过签名校验 Token 是否被篡改。JWT 由 Header、Payload、Signature 三部分组成,Header 保存算法信息,Payload 保存用户信息和过期时间,Signature 用密钥对前两部分签名,防止数据被修改。如果需要实现主动注销、踢下线等功能,可以在 Redis 中保存 Token 黑名单或者维护 Token 状态。
12. 你们项目是怎么处理异常的?你说一下怎么实现全局自定义异常处理器?
我们项目使用 Spring Boot 的全局异常处理机制统一处理异常,通过 @RestControllerAdvice 创建全局异常处理类,通过 @ExceptionHandler 捕获不同类型异常。业务异常会自定义 BusinessException,在 Service 层发现业务问题时主动抛出,由全局处理器转换成统一响应格式返回给前端。同时会区分业务异常、参数校验异常和系统异常,避免 Controller 中大量 try-catch,提高代码可维护性。
13. 内连接和外连接有什么区别?
内连接 INNER JOIN 是返回两个表中满足连接条件的数据,只返回匹配成功的记录。外连接分为左外连接和右外连接,左连接保留左表全部数据,右表没有匹配时显示 NULL;右连接反之。实际开发中左连接使用比较多,例如查询所有用户以及用户对应的数据,即使用户没有相关记录也需要展示。
LEFT JOIN 后面 WHERE 条件写错,会变成 INNER JOIN。
14. char和varchar的区别
CHAR 和 VARCHAR 都是 MySQL 中存储字符串的数据类型。CHAR 是固定长度类型,创建时指定长度,存储不足会使用空格填充,适合存储长度固定的数据,例如手机号、身份证号。VARCHAR 是可变长度类型,只保存实际数据长度,节省空间,适合用户名、地址等长度不固定的数据。实际开发中 VARCHAR 使用更多。
15. 什么是事务?你说一下事务有哪些特性?
事务是一组数据库操作的集合,这些操作要么全部成功,要么全部失败,通过事务可以保证数据的一致性。事务具有 ACID 四个特性:原子性表示事务不可分割,要么成功要么失败;一致性表示事务执行前后数据保持正确状态;隔离性表示多个事务并发执行时互不影响;持久性表示事务提交后的数据会永久保存。
16. mybatis的#和$的区别
#{} 是预编译参数,占位符替换,安全;${} 是字符串直接拼接,有 SQL 注入风险
MyBatis 中 #{} 和 KaTeX parse error: Expected 'EOF', got '#' at position 21: ...以传递参数,但是实现方式不同。#̲{} 使用预编译方式,通过 P...{} 是直接字符串替换,会把参数拼接到 SQL 中,存在 SQL 注入风险,一般只用于动态表名、字段名等场景,并且需要严格校验参数来源。
17. mybatis的接口方法支持重载吗?为什么?
MyBatis 的 Mapper 接口方法不支持重载。
MyBatis 的 Mapper 接口方法不支持重载。虽然 Java 方法支持重载,但是 MyBatis 是通过 Mapper 的 namespace 加方法名 id 来绑定 SQL 的,并不会根据参数列表区分方法。如果出现同名方法,会导致 SQL 映射冲突。因此 Mapper 接口中的方法名必须唯一。
18. 你在项目中有使用过多线程吗?
项目中使用过多线程,主要用于处理一些耗时任务,例如文件上传、图片处理、异步通知等,避免阻塞主业务流程。在 Spring Boot 中通常使用线程池或者 @Async 实现异步处理,而不是直接创建 Thread。使用多线程时需要注意线程安全问题,比如多个线程同时修改共享变量可能导致数据不一致,可以通过 synchronized、Lock 或线程安全集合等方式解决。
19. 线程池的作用是什么?
线程池的作用是管理线程,减少线程创建和销毁的开销,提高系统性能,并控制并发数量。
线程池主要用于管理线程,避免频繁创建和销毁线程带来的性能开销。它可以复用线程,提高系统响应速度,同时通过限制线程数量控制并发,避免大量线程导致系统资源耗尽。在项目中通常使用 ThreadPoolExecutor 或 Spring 的线程池处理异步任务,例如消息发送、文件处理、数据同步等。
20. 线程池有哪些核心参数?
线程池核心参数决定了:创建多少线程、任务怎么排队、线程什么时候回收、任务满了怎么办。
ThreadPoolExecutor 有 7 个核心参数:corePoolSize 表示核心线程数,maximumPoolSize 表示最大线程数,keepAliveTime 表示非核心线程空闲存活时间,TimeUnit 是时间单位,workQueue 是任务等待队列,ThreadFactory 用于创建线程,RejectedExecutionHandler 是任务达到最大容量后的拒绝策略。线程池通过这些参数控制线程创建、任务排队和资源释放。
21. redis为什么这么快?
Redis 快的主要原因:基于内存存储 + 单线程事件模型 + 高效数据结构 + IO多路复用。
Redis 快主要有几个原因:第一,Redis 基于内存存储,避免了磁盘 IO;第二,Redis 使用高效的数据结构,比如 Hash、跳表等;第三,Redis 使用 IO 多路复用,一个线程可以处理大量客户端连接;第四,Redis 的核心命令执行采用单线程模型,避免了线程切换和锁竞争;第五,Redis 操作简单,数据结构针对高性能场景进行了优化。
22. 说一下redis的常用数据类型及使用场景
Redis 提供不同数据结构,是为了适配不同业务场景。
Redis 常用数据类型包括 String、Hash、List、Set、ZSet。String 常用于缓存和计数器,Hash 适合存储对象,List 可以实现消息队列,Set 用于去重和集合运算,ZSet 常用于排行榜。实际项目会根据数据特点选择不同的数据结构。
23. 什么是aop?说一下实现aop的步骤?有哪些通知?项目中哪里用到了aop?
AOP 是把一些与业务无关但很多地方都需要的功能(例如日志、权限、事务)抽取出来,在不修改业务代码的情况下统一增强业务方法。
AOP 是 Spring 提供的面向切面编程思想,用于将日志、权限、事务等公共功能从业务代码中抽离,通过动态代理方式在运行期间对目标方法进行增强。实现 AOP 主要步骤是引入 spring-boot-starter-aop 依赖,创建切面类使用 @Aspect 和 @Component 注解,定义切入点 @Pointcut,然后通过 @Before、@After、@Around 等通知实现增强逻辑。项目中常用于接口日志记录、权限校验、接口耗时统计以及事务控制,其中 Spring 的 @Transactional 底层也是通过 AOP 实现。
24. Spring中的Bean是线程安全的吗?
Spring Bean 默认不是线程安全的,是否线程安全取决于 Bean 的作用域和 Bean 内部是否有共享状态。
Spring Bean 默认是单例的,但是单例 Bean 不代表线程安全。线程安全主要取决于 Bean 是否存在共享状态。如果 Bean 是无状态的,例如 Service 中只处理方法参数,一般是线程安全的;如果 Bean 中定义了成员变量并被多个线程修改,就可能出现线程安全问题。实际开发中通常通过设计无状态 Bean、使用线程安全集合、锁机制或者 ThreadLocal 来保证线程安全。
25. Springboot自动配置原理?
Spring Boot 自动配置就是根据项目中引入的依赖和配置文件,自动判断需要哪些 Bean,并自动创建和配置这些 Bean,减少手动配置。
Spring Boot 自动配置是通过 @EnableAutoConfiguration 实现的。项目启动时,Spring Boot 会加载 META-INF 中定义的自动配置类,然后根据条件注解(例如 @ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty)判断当前环境是否满足条件,如果满足就自动向 Spring 容器中注册对应的 Bean。这样开发者只需要引入对应 starter 依赖和简单配置,就可以使用相关功能。
26. 说下linux常见的命令有哪些?
Linux 常用命令主要包括文件操作、进程管理、网络排查和服务部署相关命令。比如文件操作使用 ls、cd、mkdir、cp、mv、rm;查看日志使用 cat、tail -f、grep;进程管理使用 ps、top、kill;网络排查使用 ping、netstat、curl;磁盘和资源查看使用 df、du、free;部署 Spring Boot 服务时常用 java -jar、nohup、systemctl 等命令。