☰
Java 后端面试高频问题详解:从线程到数据库的全面梳理
2026/10/7 8:15:07 网站建设 项目流程

1. 引言

面试是检验技术功底的重要环节,尤其是 Java 后端岗位,面试官往往会从线程、JVM、数据库、框架等多个维度进行考察。本文整理了 45 个高频面试问题,涵盖 Java 基础、并发编程、MySQL 优化、MyBatis、Spring Boot、前端通信、Linux 命令以及项目实践等多个方面,并给出简洁清晰的参考答案,帮助你在面试前快速回顾和巩固。

2. Java 线程与并发

2.1 什么是线程?

线程是程序执行的最小调度单位,一个进程中可以包含多个线程。Java 程序启动后默认会有 main 主线程,我们可以通过 Thread、Runnable、Callable 或线程池创建和管理其他线程。多线程主要用于同时处理多个任务,提高程序的响应速度和资源利用率。不过多个线程共享数据时也可能产生线程安全问题,需要使用 synchronized、Lock 等机制保证线程安全。

2.2 线程池如何实现?

Java 线程池主要通过 ThreadPoolExecutor 实现。任务提交以后,首先判断当前线程数是否小于核心线程数,如果小于就创建核心线程执行;如果核心线程都在工作,则将任务放入阻塞队列;如果阻塞队列满了,并且当前线程数还没有达到最大线程数,则继续创建非核心线程;如果线程数达到最大值并且队列也满了,就执行拒绝策略。线程执行完任务后会继续从阻塞队列获取任务,从而实现线程复用,减少线程频繁创建和销毁的开销。

2.3 线程池的核心构造参数

Java 线程池主要通过 ThreadPoolExecutor 来实现,它有 7 个核心构造参数,分别是核心线程数、最大线程数、线程空闲存活时间、时间单位、任务队列、线程工厂和拒绝策略。

3. JVM 与垃圾回收

3.1 什么是 JVM?

JVM 是 Java Virtual Machine,也就是 Java 虚拟机。Java 源代码经过 javac 编译以后会生成 class 字节码文件,然后由 JVM 加载并执行。JVM 主要负责类加载、内存管理、垃圾回收以及字节码执行。由于不同操作系统都有对应的 JVM,所以 Java 可以实现一次编译、跨平台运行。

3.2 Java 的垃圾回收机制

Java 的垃圾回收机制主要由 JVM 的 GC 自动完成,用来回收堆内存中已经不再被使用的对象。JVM 一般通过可达性分析,从 GC Roots 出发判断对象是否仍然可达,如果对象不可达,就可能被判定为垃圾对象并回收。Java 堆通常采用分代管理,新对象大多进入新生代,经过多次 GC 后仍然存活的对象可能进入老年代。GC 常见有 Minor GC、Major GC 和 Full GC,底层会结合标记清除、复制、标记整理等算法进行垃圾回收。

4. Java 基础语法

4.1 equals 与 == 的区别

== 和 equals() 的区别主要看比较的数据类型。对于基本数据类型,== 比较的是值;对于引用类型,== 比较的是两个引用是否指向同一个对象。equals() 一般用于比较对象内容,但前提是这个类重写了 equals() 方法,比如 String 就重写了 equals,所以比较的是字符串内容。如果没有重写 equals,那么 Object 默认的 equals 实际上也是通过 == 比较引用。

4.2 什么是泛型?

Java 泛型就是把类型参数化,在定义类、接口或者方法的时候先不指定具体类型,而是在使用的时候再确定具体类型。比如 List<String> 中的 String 就是泛型参数。泛型的主要作用是提高代码复用性和类型安全,可以在编译阶段发现类型错误,同时避免频繁进行强制类型转换。常见的泛型符号有 T、E、K、V,分别经常表示 Type、Element、Key 和 Value。

4.3 实体类字段如何设置默认值?

在创建实体类时,可以直接在字段声明处赋默认值。例如:

public class User { private Long id; private String name; // 默认值 private Integer status = 1; }

5. MySQL 优化与索引

5.1 MySQL 的优化查找方法有哪些?

MySQL 优化我一般先从慢 SQL 定位开始。首先可以开启慢查询日志 slow query log,找出执行时间比较长的 SQL;然后使用 EXPLAIN 查看 SQL 的执行计划,重点关注是否出现全表扫描、使用了什么索引、扫描行数、JOIN 方式等。

如果是线上实时问题,还可以通过 SHOW PROCESSLIST 查看当前正在执行的 SQL,判断有没有长时间执行或者锁等待。除此之外,也可以通过 performance_schema、监控工具等查看数据库的性能情况。

定位到问题 SQL 后,再从索引、SQL 写法、表结构、分页、JOIN、事务和锁等方面进行优化。

5.2 MySQL 的分页关键字

MySQL 的分页关键字是 LIMIT。

5.3 MySQL 的索引方式有哪些?

MySQL 常见索引方式有 B+Tree、Hash、FullText 和 Spatial,其中 InnoDB 最常用的是 B+Tree。InnoDB 的主键索引是聚簇索引,叶子节点保存完整数据;普通索引属于二级索引,叶子节点保存索引字段和主键,如果查询的字段不在索引中,可能需要通过主键再次查询,这个过程叫回表。

按照功能划分,还可以分成主键索引、唯一索引、普通索引、联合索引和全文索引。这个分类和上面的 B+Tree、Hash 不是一个维度,面试时最好区分开。

5.4 MySQL 中的增删改查

MySQL 的增删改查分别是 INSERT、DELETE、UPDATE 和 SELECT。INSERT 用于新增数据,DELETE 用于删除数据,UPDATE 用于修改数据,SELECT 用于查询数据。

6. MyBatis 框架

6.1 MyBatis 是什么?

MyBatis 是一个 Java 的持久层框架,主要用于操作数据库。它底层其实还是基于 JDBC,只不过把 JDBC 里面很多重复的代码,比如获取连接、创建 PreparedStatement、参数绑定、结果集处理和资源释放这些操作进行了封装。我们在使用 MyBatis 的时候,通常只需要定义 Mapper 接口,然后通过 XML 或者注解来编写 SQL,MyBatis 会负责执行 SQL,并把查询结果自动映射成 Java 对象。相比直接使用 JDBC,MyBatis 可以减少很多重复代码,同时又保留了开发人员自己编写和优化 SQL 的能力,所以在实际 Java 项目中使用得比较多。

6.2 MyBatis 和 JDBC 有什么区别?

JDBC 是 Java 操作数据库最基础的一套 API,使用 JDBC 的时候,需要自己获取数据库连接、创建 Statement 或 PreparedStatement、设置参数、执行 SQL、处理 ResultSet,最后还要关闭连接和其他资源,所以代码会比较繁琐。MyBatis 是在 JDBC 基础上进行了封装,我们主要负责写 SQL 和 Mapper 接口,参数绑定、SQL 执行以及结果集到 Java 对象的映射都可以交给 MyBatis 来处理。所以 MyBatis 可以减少很多重复代码,提高开发效率,不过它底层最终还是通过 JDBC 去访问数据库的。

6.3 Mapper 接口为什么不需要写实现类?

MyBatis 的 Mapper 接口不需要我们自己写实现类,是因为 MyBatis 在运行的时候会通过动态代理为 Mapper 接口生成代理对象。当我们调用 Mapper 中的方法时,实际上调用的是这个代理对象,代理对象会根据 Mapper 接口的方法找到对应的 XML 或注解 SQL,然后完成参数处理、SQL 执行以及结果映射,最后把结果返回给我们。所以虽然代码里面看不到 Mapper 的实现类,但实际上运行时 MyBatis 已经动态帮我们生成了实现。

6.4 #{} 和 ${} 有什么区别?

#{} 和 ${} 都可以在 MyBatis 的 SQL 中接收参数,但是处理方式不一样。#{} 使用的是预编译方式,底层相当于 PreparedStatement 中的问号占位符,参数和 SQL 是分开的,所以普通查询参数使用 #{} 可以有效避免 SQL 注入。而 ${} 是直接进行字符串替换,会把传入的内容直接拼接到 SQL 中,因此存在 SQL 注入风险。实际开发中普通参数一般都使用 #{},只有像动态表名、动态字段名或者 order by 字段这种不能使用占位符的场景,才可能使用 ${},并且使用的时候要做好白名单校验。

6.5 什么是 MyBatis 动态 SQL?

MyBatis 的动态 SQL 就是可以根据传入的参数动态决定最终执行哪一部分 SQL。比如一个用户查询页面,用户名、年龄、手机号这些查询条件不一定每次都有,如果用户传了用户名,就拼接用户名条件,如果没有传,就不拼接。MyBatis 提供了 if、where、choose、foreach、set 等标签来实现动态 SQL。这样我们就不需要在 Java 代码里面手动拼接大量 SQL,代码会更加清晰,也更容易维护。

6.6 resultType 和 resultMap 有什么区别?

resultType 和 resultMap 都是用来处理 MyBatis 查询结果映射的。resultType 比较简单,适合数据库字段和 Java 对象属性能够直接对应的情况,MyBatis 可以自动完成映射。resultMap 的功能更灵活,如果数据库字段名和 Java 属性名不一致,或者涉及一对一、一对多这种复杂对象关系,就可以通过 resultMap 手动指定字段和属性之间的对应关系。所以简单查询通常使用 resultType,复杂映射一般使用 resultMap。

6.7 MyBatis 一级缓存和二级缓存是什么?

MyBatis 的一级缓存是 SqlSession 级别的缓存,而且默认开启。在同一个 SqlSession 中,如果连续执行相同的查询,并且中间没有发生导致缓存失效的操作,那么第二次查询有可能直接从一级缓存中获取结果,而不再访问数据库。二级缓存的范围比一级缓存更大,它是 Mapper namespace 级别的,可以在多个 SqlSession 之间共享。不过实际项目中如果有比较复杂的缓存需求,一般还会使用 Redis 这类专门的缓存中间件,而不是过度依赖 MyBatis 自带的二级缓存。

6.8 MyBatis 底层原理是什么?

MyBatis 底层最终还是基于 JDBC。项目启动后,MyBatis 会读取配置文件和 Mapper 映射信息。当我们调用 Mapper 方法时,MyBatis 会通过动态代理找到对应的 SQL,然后根据参数生成最终 SQL,再通过 JDBC 的 PreparedStatement 去执行。数据库返回 ResultSet 之后,MyBatis 再根据 resultType 或 resultMap 把查询结果映射成 Java 对象,最后返回给业务代码。所以简单来说,MyBatis 做的事情就是在 JDBC 之上帮我们完成 SQL 映射、参数绑定、结果映射以及一些资源管理工作。

6.9 MyBatis 查询很慢,你怎么排查?

如果 MyBatis 查询比较慢,我首先会把 MyBatis 最终执行的 SQL 和实际参数找出来,然后把这条 SQL 单独放到数据库中执行,确认到底是数据库 SQL 本身慢,还是 Java 代码其他地方的问题。如果确定 SQL 慢,我会通过 MySQL 的 EXPLAIN 查看执行计划,重点检查有没有全表扫描、索引是否生效、扫描的数据量是不是太大,以及 JOIN、排序、分页有没有问题。如果发现索引没有使用,还会继续检查是不是存在函数操作、隐式类型转换或者前置模糊查询等导致索引失效的情况,然后再根据实际情况优化 SQL 或增加合适的索引。

7. Spring Boot 与注解

7.1 Spring Boot 中常用的注解

@SpringBootApplication 是启动注解,@RestController 用来写 REST 接口,@Autowired 用于依赖注入,@RequestMapping 定义请求路径,@RequestBody 接收 JSON,@RequestParam 接收查询参数,@PathVariable 接收路径参数,@Transactional 用于事务管理。

7.2 数据库信息在 Spring Boot 哪里配置?

数据库信息一般在 application.yml 或 application.properties 中配置。

7.3 @GetMapping 注解是什么意思?

@GetMapping 是 Spring MVC 中用于处理 GET 请求的注解,一般用来做查询操作,比如查询用户信息。它相当于 @RequestMapping(method = RequestMethod.GET) 的简写。

7.4 @Configuration 注解是什么意思?

@Configuration 是 Spring 中声明配置类的注解,作用类似以前的 XML 配置文件。通常会配合 @Bean 使用,把对象注册到 Spring 容器中。

7.5 @Autowired 注解是什么意思?

@Autowired 用来实现依赖注入,Spring 会自动从容器中找到对应类型的 Bean 并注入进来,这样我们就不用手动创建对象了。

7.6 注解的作用

通过注解可以简化传统的 Spring 的 XML 配置和手动 Bean 注册。

8. 前端通信与 Ajax

8.1 什么是 Ajax?

Ajax 是 Asynchronous JavaScript and XML 的缩写,是一种前端异步通信技术。它可以通过 JavaScript 向服务器发送 HTTP 请求,在不刷新整个页面的情况下获取后端数据,然后局部更新页面。虽然名字中有 XML,但现在一般使用 JSON 作为前后端的数据交换格式,比如 Vue 项目中常通过 Axios 或 Fetch 实现 Ajax 请求。

8.2 Ajax 的核心特点是什么?

Ajax 最大的特点是异步请求和局部刷新。用户操作页面时,不需要重新加载整个网页,就可以和服务器进行数据交互。

8.3 Ajax 的工作流程是什么?

首先由 JavaScript 创建请求,然后通过 HTTP 向服务器发送请求,服务器处理业务逻辑并返回数据,前端收到数据后再通过 JavaScript 或 DOM 更新页面内容。

8.4 异步是什么意思?

异步指的是发送请求以后,不需要等待服务器返回结果才能继续执行后面的代码,服务器返回数据以后,再通过回调、Promise 等方式处理结果。

8.5 同步和异步有什么区别?

同步请求需要等待当前任务执行完成后才能继续执行后面的任务,可能阻塞页面;异步请求发送以后可以继续执行其他任务,等服务器返回结果后再处理,因此用户体验更好。

8.6 Ajax 跨域怎么解决?

常见方式有 CORS、反向代理等。实际前后端分离项目中经常由后端配置 CORS,允许指定前端域名访问。

8.7 Ajax 请求失败了,怎么排查?

首先通过浏览器开发者工具的 Network 查看请求有没有发送出去,然后检查请求地址、请求方式、参数和请求头,再看 HTTP 状态码。如果是 4xx,一般检查请求参数、权限或者接口地址;如果是 500,则查看后端日志和异常堆栈。如果请求被浏览器拦截,还需要检查是否存在跨域问题。

8.8 Ajax 如何实现局部刷新?

Ajax 请求后端获取数据后,通过 JavaScript 操作 DOM,只修改页面中需要变化的部分,而不是重新加载整个 HTML 页面。

9. 网络协议基础

9.1 HTTP 和 HTTPS 的区别

HTTP 和 HTTPS 都是应用层协议,主要区别在安全性。HTTP 默认是明文传输,端口是 80;HTTPS 是在 HTTP 的基础上加入 TLS/SSL 加密,默认端口是 443。HTTPS 可以提供数据加密、身份认证和完整性校验,能够降低数据被窃听或者篡改的风险。另外 HTTPS 一般需要配置数字证书,所以现在正式的网站和接口基本都会使用 HTTPS。

9.2 三次握手和四次挥手

TCP 三次握手的过程是,第一次客户端向服务器发送 SYN 请求建立连接;第二次服务器收到后回复 SYN 和 ACK,表示同意建立连接并确认客户端的请求;第三次客户端再发送 ACK 给服务器,双方确认收发能力正常,TCP 连接正式建立。之所以需要三次,是为了确认客户端和服务器双方的发送和接收能力都正常,同时避免历史失效连接请求造成错误连接。

TCP 四次挥手是用来关闭连接的。第一次客户端发送 FIN,请求关闭连接;第二次服务器回复 ACK,表示收到关闭请求,但服务器可能还有数据没有发送完,所以不能马上关闭;第三次服务器数据发送完成后,再发送 FIN;第四次客户端回复 ACK,双方连接关闭。之所以需要四次,是因为 TCP 是全双工通信,两个方向需要分别关闭,所以服务器的 ACK 和 FIN 通常不能像握手阶段那样合并发送。

10. Python 基础

10.1 Python 中常用的容器

Python 中常用的容器主要有 list、tuple、set 和 dict。

10.2 元组和列表的区别

列表可以修改,元组不能修改。

10.3 Python 的内存管理方法

Python 的内存管理主要由解释器自动完成,核心包括引用计数、垃圾回收和内存池机制。Python 主要通过引用计数判断对象是否可以释放,当对象引用计数变成 0 时可以回收内存。但是引用计数无法很好处理循环引用,所以 Python 还提供了 GC 垃圾回收机制,通过分代回收处理循环引用。另外 Python 对小对象会使用内存池进行管理,减少频繁申请和释放内存的开销。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询