每天一套面试题Day1-九识智能

来自牛友

1. 静态字段、静态方法、静态代码块、成员属性、成员方法、构造函数执行顺序

核心原则:先静态,后成员;先父类,后子类;先成员域,后构造

一个类的完整初始化顺序如下: 先是类加载中的初始化:

  1. 父类静态域(静态字段和静态代码块):按代码书写顺序执行。
  2. 子类静态域(静态字段和静态代码块):按代码书写顺序执行。

再是对象的创建

  1. 父类成员域(成员属性和构造代码块):按代码书写顺序执行。 构造代码块就是用{}包住的语句。
  2. 父类构造函数

确保了当子类代码开始执行时,父类已经处于完全初始化的稳定状态。

  1. 子类成员域(成员属性和构造代码块):按代码书写顺序执行。
  2. 子类构造函数

回顾语法,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集群模式,通过分片来实现横向扩展。

我理解它们各自的原理和适用场景,只是目前个人项目阶段还没有实际部署集群的必要。

常见的部署模式:

  1. 单机模式:开发环境常用,生产环境不推荐,有单点故障风险。
  2. 主从复制 (Master-Slave):一主多从,主负责写,从负责读。实现了读写分离和数据备份,但主节点宕机需要手动切换。
  3. 哨兵模式 (Sentinel):在主从基础上,增加了哨兵进程来监控主从节点。当主节点宕机时,哨兵可以自动完成故障转移和主从切换,实现高可用。这是中小型项目中非常经典的架构。
  4. 集群模式 (Cluster):采用无中心结构,通过分片(Sharding)来进行数据存储,可实现海量数据存储和高并发。具备高可用和可扩展性,是大型项目的选择。

5. 项目中使用到了哪些Redis数据结构?

  • String (字符串)
    • 场景: 文章阅读量。
  • Hash (哈希)
    • 场景: 存储对象信息,如用户信息 (key: user:1, field: name, age, email)、商品信息。比String更适合存储对象。

待完善

6. 接口使用JMeter压测的QPS是多少?

ai给的答案。待补充

这个问题没有标准答案,完全取决于你的接口业务逻辑、服务器配置、数据库性能、缓存使用情况等。

回答策略:

  1. 不要捏造一个不切实际的数字。
  2. 给出一个范围,并说明影响因素。
  3. 展示你的分析思路。

标准回答: “这个数值因接口而异。在我之前的项目中,我们对一个核心查询接口进行过压测。该接口业务逻辑不复杂,并且有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 面板中,能看到这个请求已经发出并且服务器也返回了响应,只是代码拿不到。 解决方案:

  1. 后端解决方案(最常用): CORS(跨域资源共享)全称为 Cross-Origin Resource Sharing。允许服务器明确地指示浏览器,哪些外部源(域、协议、端口)有权访问该服务器的资源。

    • CORS (跨域资源共享):这是最标准、最推荐的解决方案。在后端服务器的响应头中添加特定的字段。
      • 在Spring项目中,可以使用 @CrossOrigin 注解在Controller类或方法上。
      • 也可以配置一个全局的 WebMvcConfigurer Bean,来统一处理。
      @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
          }
      }
      
  2. 网关/代理层解决方案

    • 在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;```

#面试真题#
全部评论

相关推荐

迷茫的大四🐶:这就是他们口中的ai时代的一人公司
点赞 评论 收藏
分享
算法冲刺中:kpi面加一,面完完全没动静,感谢信都没有
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务