资讯动态

Java八股面试(5)

发布时间:2026/10/15 16:21:33 来源:尧图企业网站定制
1.#和$的区别是什么什么情况必须用$**#**** 是预编译处理相当于 JDBC 的**?**占位符专门用来防注入**$**是字符串替换相当于直接拼接 SQL有注入风险。**一、 核心区别深度解析1.#{}(预编译)原理MyBatis 在处理#{}时会把它替换成一个**?**** (问号)**。执行然后调用 JDBC 的PreparedStatement通过setString()等方法把参数填进去。效果假如你传的参数是Tom它会自动加上单引号变成Tom。安全因为是预编译数据库知道这个?只是一个参数不会把它当成 SQL 命令去执行。所以能防止 SQL 注入。2.${}(字符串替换)原理MyBatis 在处理${}时就是简单的字符串拼接。执行假如你传的参数是Tom它就直接把Tom拼在 SQL 里不加引号。风险如果恶意用户传了1 OR 11这句 SQL 就变成了逻辑真所有数据都会被查出来。这就是SQL 注入。二、 面试标准回答它们的核心区别在于“处理机制”和“安全性”。首先关于它们的区别处理机制不同**#{}**** 是预编译处理** MyBatis 在处理它时会将 SQL 中的#{}替换为 JDBC 的占位符**?**。接着调用PreparedStatement的set方法来赋值。**${}**** 是字符串替换** MyBatis 在处理它时就是做纯粹的字符串拼接。它会把参数的值直接拼接到 SQL 语句中不会自动添加引号。安全性不同最关键点**#{}**因为使用了预编译能够有效防止 SQL 注入所以我们在传递查询参数如where id #{id}时绝大多数情况都必须用这个。**${}**因为是直接拼接如果接收前端传入的不可信数据极易引发 SQL 注入风险。2.介绍一下ES是什么一、 知识点讲解ES (Elasticsearch) 说白了就是一个基于 Lucene 的分布式搜索和分析引擎。你可以把它理解为是一个对 Lucene 做了封装的“超级壳子”把原本复杂难用的 Lucene 变成了支持分布式、高可用、提供 RESTful API 接口的开箱即用的服务。它主要解决的是传统关系型数据库在海量数据全文检索和复杂聚合分析上性能极其低下的问题。倒排索引这是 ES 的灵魂。MySQL 用的是 B 树适合精确查找而 ES 用的是倒排索引。一般的数据库是“通过 ID 找内容”。ES 是反过来的它是“通过内容找 ID”。当你存数据时ES 会先把内容分词比如把“华为手机”拆成“华为”、“手机”然后建立一个字典记录每个词出现在哪些文档 ID 里。当你搜“华为”时它直接查字典就能告诉你哪些文档里有不用去遍历每一行数据。这就是为什么它能在亿级数据中毫秒级响应。分片与副本ES 天生就是为了分布式设计的。分片数据量太大一台机器存不下怎么办切开了存。一个索引切成几个分片分散在不同的机器节点上并行查询提高吞吐量。副本机器坏了数据丢了怎么办备份。每个分片都有副本主分片挂了副本立马顶上保证高可用。二、 面试标准回答实战篇Elasticsearch 是一个基于 Lucene 开发的、分布式的、实时的搜索和分析引擎。它对外提供了 RESTful 风格的接口非常易于使用。在目前的架构中我们主要用它来解决海量数据的全文检索和实时数据分析的需求。它之所以被广泛使用主要有两点核心优势第一是查询性能极强。这主要归功于它底层的倒排索引机制。和传统数据库不同ES 在写入数据时就会对内容进行分词建立索引这让它在处理模糊搜索、多条件组合过滤时比 MySQL 的 Like 查询快几个数量级基本能做到亿级数据毫秒级返回。第二是天生的分布式架构。它支持水平扩展通过分片机制我们可以把庞大的数据切分存储在多台节点上提升系统的吞吐量同时通过副本机制保证了即使某台节点宕机数据也不会丢失服务也不会中断天然具备高可用性。3.如何理解BASE理论一、知识点讲解BASE 理论是对 CAP 中一致性C和可用性A权衡的结果。在分布式系统中因为网络分区P是必然存在的我们往往无法同时做到强一致性和高可用性。BASE 理论的核心思想是我们不追求强一致性而是通过牺牲强一致性来获得基本可用性并允许数据在一段时间内是不一致的但最终会达到一致状态。它对应的是这三个短语的缩写Basically Available基本可用系统出现故障时允许损失部分可用性但保证核心功能可用。比如响应时间变长原本 0.5秒 返回现在 2秒或者功能降级淘宝双11流量洪峰时你可能无法看到具体的物流详情只能看到简单的“已发货”状态或者无法立刻修改收货地址但你依然可以下单买东西。Soft State软状态允许系统存在中间状态而该中间状态不会影响系统的整体可用性。实质允许不同节点间的数据同步存在延时。比如你刚付完款状态可能显示“支付中”而不是立刻变为“支付成功”这就是一种软状态。Eventually Consistent最终一致性这是 BASE 的最终目标。系统不需要实时保持各节点数据一致但在经过一段时间的同步后所有节点的数据最终必须达成一致。关键这个“一段时间”取决于网络延迟、系统负载等因素。二、面试标准回答BASE 理论是分布式系统设计中为了解决大规模互联网系统的高可用问题对 CAP 理论中一致性和可用性权衡后的结果。它主要针对的是对数据强一致性要求没那么高但对并发和可用性要求极高的场景。它和传统数据库的 ACID 特性是相反的ACID 追求的是“强一致性”而 BASE 追求的是“最终一致性”。第一是基本可用。这意味着在系统出现不可预知的故障时我们允许损失部分可用性来保全大局。比如响应时间上的损失或者功能上的降级保证核心业务如电商的下单不受影响。第二是软状态。这是相对于原子性中的“硬状态”而言的。我们允许系统存在中间状态也就是说允许系统在不同节点的数据副本之间进行同步时存在延时这个延时不会阻塞业务的进行。第三是最终一致性。这是 BASE 的核心目标。虽然我们允许中间状态和短暂的不一致但系统必须保证在经过一段时间的同步后所有节点的数据最终能够达到一致。这个时间窗口取决于网络状况和系统负载。4.如何理解IOCIOC控制反转是一种设计思想它的核心是将对象的创建权和管理权从代码中剥离出来交给 Spring 容器去统一管理。这样做最大的好处就是实现了代码的解耦。一、 核心原理深度解析我们必须搞清楚两个问题谁控制了谁反转了什么正向控制场景假设对象 A 依赖于对象 B。做法在 A 的代码内部我们必须显式地new B()。弊端耦合度高如果 B 的构造函数变了A 的代码也得改。难以替换如果想把 B 换成子类 C必须修改 A 的源代码。难以测试想单独测 A还得先把 B 搞定没法 Mock。反转控制做法对象 A 不再去new B()了它只声明“我需要一个 B”。谁来控制**Spring 容器。容器在启动时会自动创建好 B然后通过反射把 B注入**到 A 里面。反转了什么**对象的创建权和依赖关系的维护权**从程序员的手里代码内部反转到了容器的手里。IOC 与 DI 的关系这是面试常考的辨析。IOC 是思想是目标我要让控制权反转。DI是手段是实现通过依赖注入来实现控制反转。结论IOC 是理论DI 是实践。二、 面试标准回答IOC 是 Spring 框架最核心的设计思想。可以总结为一句话IOC 是为了解耦将对象的创建权移交给容器。具体来说可以从以下三个层面来理解第一“谁控制谁控制什么”在传统的 Java 开发中如果 A 类依赖 B 类我们需要在 A 类里手动通过 new 关键字来创建 B 的实例。这时候依赖对象的创建和生命周期是由程序员在代码里主动控制的。而在 Spring 中我们不再手动 new 对象而是把对象交给 Spring 容器去管理。这就是“控制”。第二什么是“反转”所谓的反转就是对象的创建权和依赖关系的维护权发生了转移。以前是我们主动去“找”资源现在变成了被动等待容器把资源“送”过来。第三为什么要这么做这么做的最大好处就是解耦。5.如何在分布式环境下实现精确限流在分布式场景下要实现精确限流最通用的方案是“Redis Lua 脚本”组合。针对“精确”二字如果要求极致的时间窗口平滑度使用“ZSet 滑动窗口”如果追求性能与精度的平衡使用基于 Lua 实现的“令牌桶算法”。一、 核心难点与架构选型在分布式环境下流量打在不同的服务器上单机的AtomicInteger或RateLimiter只能管住自己这台机器管不住全局。比如你要限制全局 QPS 为 1000你有 10 台机器简单的每台限流 100 是不准确的因为负载均衡不一定绝对均匀。要做到精确必须把所有机器的计数收敛到一个地方。存储介质**Redis** 是不二之选。因为它是单线程处理命令天然防并发且读写极快。一致性保障**Lua 脚本**。因为“读取当前令牌数”和“扣减令牌数”是两个网络请求在高并发下会有竞争必须把逻辑打包在 Lua 脚本里发给 Redis保证原子执行。二、 两种主流的“精确”实现方案针对“精确”定义的程度不同有两种做法极致精确Redis ZSet 实现滑动窗口如果你说的“精确”是指要解决“固定窗口”算法在临界点流量翻倍的问题比如 0.9秒来了 100 个请求1.1秒又来了 100 个请求虽然每秒都没超但 0.2秒内压垮了系统那么ZSet 滑动窗口是唯一的解法。原理我们将 Key 设为限流的资源名如 API 接口。Value 是一个 Redis 的ZSet有序集合。每当一个请求进来记录将当前请求的时间戳作为 member 和 score 写入 ZSet。清理移除当前时间窗口之外的旧记录。计数统计 ZSet 中剩下的元素个数。判断如果个数 阈值放行否则拒绝。优缺点优点非常精确。它可以任意滑动彻底消除了临界突刺问题。缺点内存消耗大。你需要存储过去 1秒内所有请求的时间戳。如果限流阈值是 10万 QPS那 ZSet 里就要存 10万条数据这对 Redis 内存和 CPU 都是巨大压力。适用场景强一致性要求高但 QPS 阈值不高的场景如短信发送频率限制。生产通用Redis Lua 实现令牌桶如果你说的“精确”是指严格控制速率且能抗住高并发那么令牌桶算法是工业界标准。它比固定窗口精确比滑动窗口省资源。原理在 Redis 中存储两个键tokens当前剩余令牌数和timestamp上次刷新时间。每次请求进来执行一段 Lua 脚本计算生成now - timestamp计算出这段时间理论上应该生成多少个新令牌。更新桶Math.min(capacity, tokens generated)填满为止。判断扣减如果tokens 1则tokens - 1并返回成功否则返回失败。更新时间将timestamp更新为当前时间。为什么精确它不是简单地按秒清零而是基于时间流逝线性地补充令牌。Redis Lua 保证了这整个计算过程是原子的不会出现“多扣”或“少扣”。优缺点优点性能极高内存占用极小只存几个数字且支持预热和突发流量。缺点实现逻辑比计数器复杂需要维护 Lua 脚本。三、 补充点时间同步问题在分布式环境下应用服务器的时间可能不准。在使用 ZSet 滑动窗口时尽量使用Redis 服务器的时间Lua 脚本中获取redis.call(TIME)而不是应用服务器的System.currentTimeMillis()以保证基准一致。Redis 挂了怎么办降级策略这是分布式限流最大的软肋。如果 Redis 崩了或者网络抖动限流器会抛异常。策略必须要有本地降级。可以在本地内存如 Guava预设一个更大的兜底阈值。一旦 Redis 链接超时自动切换到单机限流模式虽然不准但能保命别让限流组件把业务搞挂了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑