2026-04-02 星期四
【职场/管理】能量与能力的认知差异(任老师)
在职业发展中,能量往往比能力更具决定性。
- 能量: 指一个人的内在动力、抗压能力、积极心态以及对他人的感染力。
- 能力: 指执行具体任务的技术、知识和熟练度。
💡 感悟: 能力决定了你能走多快,而能量决定了你能走多远。
【设计模式】【面试考点】工厂模式与建造者模式的核心维度区别
| 对比维度 | 工厂模式 | 建造者模式 |
|---|---|---|
| 核心意图 | 创建不同类型但具备相关性的对象,实现多态性 | 构建同一类型但内部结构复杂的对象 |
| 产品复杂度 | 通常创建单一、完整的独立对象 | 通常创建由多个部件/属性组成的复合对象 |
| 构建过程 | 一步到位,单次调用方法直接返回完整对象 | 分步进行,多步配置后由 build() 方法最终组装成品 |
| 核心关注点 | 关注对象整体类型的选择 | 关注对象内部细节的装配过程 |
| 行业经典示例 | LoggerFactory.getLogger() | Java StringBuilder、Lombok @Builder |
【最佳实践】工厂模式的 Java 后端高频使用场景
工厂模式的核心适用场景为:需要根据不同条件动态创建不同具体实现类的场景,Java 后端开发中高频落地场景如下:
- 多类型数据库/中间件客户端创建:根据配置文件的类型,动态创建 MySQL、PostgreSQL 等不同数据库的连接对象,上层业务代码无需感知底层实现差异
- 多环境日志记录器适配:根据运行环境,自动切换 ConsoleLogger(开发环境)、FileLogger/KafkaLogger(生产环境),无需修改业务代码
- 多格式文件解析器创建:根据上传文件的后缀名,自动创建
PdfParser、ExcelParser等对应格式的解析器实现类
【最佳实践】建造者模式的 Java 后端高频使用场景
建造者模式的核心适用场景为:需要创建参数繁多、构造逻辑复杂的对象,或需要创建不可变(Immutable)对象的场景,Java 后端开发中高频落地场景如下:
- 复杂配置类/组件对象构建:例如线程池
ThreadPoolExecutor这类包含7个以上核心参数的对象,通过链式调用分步配置参数,大幅提升代码可读性 - 动态构建类场景:例如 SQL 构建器、HTTP Request 构建器,通过分步调用方法拼接参数,最终生成完整的 SQL 语句或请求对象
- 不可变对象创建:对于创建后不允许修改(无 setter 方法)、但初始化需要大量参数的对象,通过 Builder 模式完成合法参数校验与对象初始化
【后端框架】【Spring IoC 容器】基于 XML 管理 Bean 对象
- 获取 Bean 方式:
- XML 配置属性:id、class、id 和 class (保证唯一性)
- FactoryBean(整合第三方框架的常用机制):实现 FactoryBean<?> 接口来减少配置 XML 操作
- 对象赋值:
- 初始化 Bean 对象(DI 注入):setter(property)、constructor(constructor-arg)
- 常规属性赋值:”ref”、内部 Bean、级联属性 “clazz.clazzname”
- 特殊属性赋值:数组类(<array><value>)、List 类(<list><ref>)、Map 类(<entry><key><value><ref>)
- 引用集合类型赋值:使用 <util> 进行引用赋值
- 通过 p 命名空间赋值:<bean p:property=“xxx”></bean>
- 外部配置文件赋值:利用 <context> 名称空间,将 .property 配置文件注入对象属性 “${a.b}”
- 特殊符号处理:<null/>、“<”、<![CDATA[a < b]]>
- Bean 的作用域(scope):singleton(默认单例、初始化时创建)、prototype(多例、获取 Bean 时创建)
- Bean 的生命周期:(能画个图说明最好)
bean对象创建(调用无参构造器)
给bean对象设置属性
bean的后置处理器(初始化之前)(init-method)
bean对象初始化(需在配置bean时指定初始化方法)
bean的后置处理器(初始化之后)(destroy-method)
bean对象就绪可以使用
bean对象销毁(需在配置bean时指定销毁方法)
IoC容器关闭
【算法经验】快速排序
原理:分治策略、哨兵划分、递归排序
复杂度:$O(nlogn) - O(n)$,最坏时间复杂度 $O(n^2)$
核心思想: 通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
关键步骤:
- 基准数选择: 选择一个数作为基准(Pivot)。
- 哨兵划分: 将大于基准的移到右边,小于基准的移到左边。
- 递归排序: 对左右子区间重复上述过程。
优势:
- 概率论: 出现最差情况的概率极低。
- 缓存友好: 执行划分操作时,子数组连续加载到缓存,访问效率高;跳跃式访问元素的,缺乏该特性
- 常数系数小:快速排序的比较、赋值、交换等操作的总数量最少
C++
int partition(vector<int> &nums, int left, int right) {
int i = left, j = right; // 双指针分治
while (i < j) { // 结束条件:指针相遇,遍历结束,i 左边全小于基数,j 右边全大于基数
while (i<j && nums[j]>=nums[left]) --j; // 从右往左遍历,比基数大的不管,直到下一个比基数小的停止
while (i<j && nums[i]<=nums[left]) ++i; // 从左往右遍历,比基数小的不管,直到下一个比基数大的停止
swap(nums[i], nums[j]); // 交换 i,j 下标的元素,把当前比基数大的,放到 j 路径后面,比基数小的,放到 i 路径后面
} // ⚠⚠⚠ 因为先动的是 j 指针,一定是 j 指针碰到 i 指针,即 j==i ,这个时候 i/j 下标的数是一定是比基数小的数,交换 i 跟 left 下标的数后,当前整个区间基准数就被移到了中间,左边一定是全比基数小的。这是因为默认 left 下标是基数的原因,如果默认 right 下标为基数的话,那么就应该先动 i 指针,让 i 指针与 j 指针相遇,最后交换 i 跟 right 之后,右边的数一定全是大于基数的数
swap(nums[i], nums[left]);
return i;
}
void quickSort(vector<int> &nums, int left, int right) {
if (left >= right) return; // 子数组长度为 1 时终止递归
// 当前这个区间的基准数是 left
int pivot = partition(nums, left, right); // 获取临界点(这个点左边的数都小于 left,右边的数都大于 left)
// 在 partition 中最后将 left 跟 pivot 交换了位置,所以上面一行的 pivot 实际上是快排之后的 left 的下标
// 下面中的 left 仍然是当前区间的 left 边界,**作用域**搞清楚
// [left, a, b, c, pivot, A, B, C, right]
quickSort(nums, left, pivot-1); // 小于 left 的所有数的区间进行快排
quickSort(nums, pivot+1, right); // 大于 left 的所有数的区间进行快排
}TODO:
Spring 跟 Springboot 的具体差异
只有用 IoC 容器管理的实体才能被 Springboot 使用吗?组件跟实体的区别
注解管理 Bean ,各个注解的作用、运用场景