每天一套面试题Day1-九识智能
1. 静态字段、静态方法、静态代码块、成员属性、成员方法、构造函数执行顺序
核心原则:先静态,后成员;先父类,后子类;先成员域,后构造。
一个类的完整初始化顺序如下: 先是类加载中的初始化:
- 父类静态域(静态字段和静态代码块):按代码书写顺序执行。
- 子类静态域(静态字段和静态代码块):按代码书写顺序执行。
再是对象的创建
- 父类成员域(成员属性和构造代码块):按代码书写顺序执行。 构造代码块就是用{}包住的语句。
- 父类构造函数。
确保了当子类代码开始执行时,父类已经处于完全初始化的稳定状态。
- 子类成员域(成员属性和构造代码块):按代码书写顺序执行。
- 子类构造函数。
回顾语法,Java规定:子类构造函数的第一行必须是super(...)调用父类构造函数,或者是this(...)调用本类其他构造函数。如果没有显式写出,编译器会自动插入无参的super()。 如果父类只有有参构造函数,子类必须显式地在自己的构造函数中调用父类的有参构造函数,否则会编译错误。
2. MyBatis中 #{} 与 ${} 的区别
这是一个关于 SQL注入 和 预编译 的核心问题。
| 特性 | #{} (井号占位符) |
${} (美元符拼接) |
|---|---|---|
| 本质 | 参数占位符,会生成 ? |
字符串拼接,直接将值插入SQL |
| 处理方式 | MyBatis会使用 PreparedStatement,进行预编译,预编译语句中的 ? 占位符在最终执行时会被当作值来处理 |
MyBatis会使用 Statement,进行字符串拼接 |
| 安全性 | 高,能有效防止SQL注入攻击 | 低,存在SQL注入风险 |
| 适用场景 | 绝大部分场景,尤其是传入参数值时 | 动态传入表名、列名等非参数值场景 |
| 自动类型处理 | 是,会自动根据属性类型处理 | 否,原样插入 |
总结: 在传递 值(Value) 时,永远优先使用 #{}。只有在动态SQL中需要传递 数据库对象名(如表名、列名) 时,才不得已使用 ${}。
预编译语句中的 ? 占位符在最终执行时会被当作值来处理。数据库不允许表名、列名等结构标识符作为参数值传递。这会导致语法错误,因为数据库期望的是 SELECT * FROM users,而不是 SELECT * FROM 'users'
3. MyBatis Plus用过吗
ai给的答案。我没用过。待补充
标准回答: “是的,我在项目中使用过MyBatis Plus。它是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,简化了开发,提高了效率。”
可以补充的亮点(说明你不仅用过,还了解其核心功能):
- 强大的CRUD操作:内置了通用Mapper (
BaseMapper<T>) 和通用Service,无需编写简单的XML,通过继承即可获得大部分单表CRUD方法。 - 条件构造器 (
QueryWrapper,LambdaQueryWrapper):支持链式调用,以Java Lambda的方式优雅地构建复杂的查询条件,避免了SQL拼接的麻烦和错误。 - 分页插件:配置简单,能轻松实现物理分页。
- 代码生成器:可以快速生成Entity、Mapper、Service、Controller等代码,极大提升了开发效率。
- 乐观锁插件:方便地实现乐观锁,解决并发更新问题。
4. 项目中的Redis服务是怎么部署的,集群,主从还是哨兵?
在我目前的个人项目中,为了快速开发和验证想法,我是在本地直接运行了一个单实例的Redis服务。但是,我非常清楚这种单机模式在生产环境下是有单点故障风险的。
如果这个项目要上线,我会根据业务体量和可用性要求来选择部署方案:
中小型项目中,我会采用哨兵模式,部署一主两从三哨兵,这是性价比很高的高可用方案。
如果预期会有海量数据和高并发,我会直接采用 Redis Cluster集群模式,通过分片来实现横向扩展。
我理解它们各自的原理和适用场景,只是目前个人项目阶段还没有实际部署集群的必要。
常见的部署模式:
- 单机模式:开发环境常用,生产环境不推荐,有单点故障风险。
- 主从复制 (Master-Slave):一主多从,主负责写,从负责读。实现了读写分离和数据备份,但主节点宕机需要手动切换。
- 哨兵模式 (Sentinel):在主从基础上,增加了哨兵进程来监控主从节点。当主节点宕机时,哨兵可以自动完成故障转移和主从切换,实现高可用。这是中小型项目中非常经典的架构。
- 集群模式 (Cluster):采用无中心结构,通过分片(Sharding)来进行数据存储,可实现海量数据存储和高并发。具备高可用和可扩展性,是大型项目的选择。
5. 项目中使用到了哪些Redis数据结构?
- String (字符串):
- 场景: 文章阅读量。
- Hash (哈希):
- 场景: 存储对象信息,如用户信息 (
key: user:1,field: name, age, email)、商品信息。比String更适合存储对象。
- 场景: 存储对象信息,如用户信息 (
待完善
6. 接口使用JMeter压测的QPS是多少?
ai给的答案。待补充
这个问题没有标准答案,完全取决于你的接口业务逻辑、服务器配置、数据库性能、缓存使用情况等。
回答策略:
- 不要捏造一个不切实际的数字。
- 给出一个范围,并说明影响因素。
- 展示你的分析思路。
标准回答: “这个数值因接口而异。在我之前的项目中,我们对一个核心查询接口进行过压测。该接口业务逻辑不复杂,并且有Redis缓存。在4核8G的测试服务器上,这个接口的QPS大概在 1200 到 1500 左右。 对于一些涉及复杂联表查询或者没有命中缓存的接口,QPS会下降到 200 到 300。 我们当时发现瓶颈主要在数据库上,后续通过优化SQL语句和增加缓存命中率,性能得到了提升。”
7. 跨域问题怎么解决?
跨域是由浏览器的 同源策略 引起的一种安全限制。 由浏览器实施的一套规则。它规定了:一个源(Origin)(当协议,域名,端口完全相同的时候才是同源)的文档(网页)或脚本,在没有明确授权的情况下,不能与另一个源的资源进行交互。假设原始页面来自 https://www.example.com,不能用 JavaScript 直接向 https://api.another-domain.com 发起 XMLHttpRequest 或 Fetch 请求并读取响应。 阻碍的是 一个源的文档或脚本,去读取另一个源加载来的资源的 内容浏览器拦截并拒绝JavaScript 代码读取该请求的响应内容。会得到一个网络错误,但在开发者工具的 Network 面板中,能看到这个请求已经发出并且服务器也返回了响应,只是代码拿不到。 解决方案:
-
后端解决方案(最常用): CORS(跨域资源共享)全称为 Cross-Origin Resource Sharing。允许服务器明确地指示浏览器,哪些外部源(域、协议、端口)有权访问该服务器的资源。
- CORS (跨域资源共享):这是最标准、最推荐的解决方案。在后端服务器的响应头中添加特定的字段。
- 在Spring项目中,可以使用
@CrossOrigin注解在Controller类或方法上。 - 也可以配置一个全局的
WebMvcConfigurerBean,来统一处理。
@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8080") // 允许的源 .allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的方法 .allowCredentials(true); // 允许携带Cookie } } - 在Spring项目中,可以使用
- CORS (跨域资源共享):这是最标准、最推荐的解决方案。在后端服务器的响应头中添加特定的字段。
-
网关/代理层解决方案:
- 在Nginx等网关或代理服务器中配置反向代理,将前端和后端的请求代理到同一个域下,从而避免跨域。
回答: “我们项目中使用的是最标准的 CORS(跨域资源共享) 方案。在基于Spring Boot的后端项目中,我们通过一个全局配置类,统一设置了允许跨域的源、方法。这种方式对代码侵入性小,且能很好地解决问题。”
8. SQL题目:成绩互换
表结构: result (id, stu_id, math, english)
目标: 将 math 成绩与 english 成绩互换。
正确解法: 需要借助一个临时值来完成交换。
方法一:
UPDATE result
SET
math = english,
english = math;
-- !!!这个写法在MySQL等数据库中实际上是正确的,因为它会使用行级更新,先读取整行旧值再赋值。
-- 但为了概念清晰和在所有数据库中的通用性,显式使用临时变量是更稳妥的做法。
说明:我知道这个写法可能看起来有问题,但在MySQL等数据库中,它利用的是更新前的旧值,所以可以正确完成交换。这是一种特性和优化
方法二:使用一个明确的临时值
-- 创建明确的临时备份表
CREATE TEMPORARY TABLE result_temp AS
SELECT id, math AS original_math, english AS original_english
FROM result;
-- 执行明确的数值交换
UPDATE result r
SET r.math = rt.original_english, -- 明确的:英语原始值赋给数学
r.english = rt.original_math -- 明确的:数学原始值赋给英语
FROM result_temp rt
WHERE r.id = rt.id;
-- 清理临时表
DROP TABLE result_temp;```
#面试真题#