当前位置: 首页 > news >正文

Java开发岗面试记录合集

一、Java 核心

1. 基础语法
  • final关键字的作用

    • 修饰类:类不可被继承(如String类),保证类的稳定性和安全性。
    • 修饰方法:方法不可被重写(防止子类篡改父类核心逻辑,如工具类方法)。
    • 修饰变量:
      • 基本类型:值不可修改(如final int a = 10a的值终身不变)。
      • 引用类型:引用地址不可变(对象内容可修改,如final List<String> list = new ArrayList<>()list可添加元素但不能指向新集合)。
  • 深拷贝 vs 浅拷贝

    类型特点实现方式适用场景
    浅拷贝仅复制对象本身及基本类型字段,引用类型字段与原对象共享同一份引用(新旧对象关联)Object.clone()默认实现(需实现Cloneable接口)、BeanUtils.copyProperties()简单对象复制,无需完全隔离引用关系
    深拷贝递归复制所有字段(包括引用类型),新旧对象完全独立(无任何关联)手动递归复制、序列化(Serializable)、JSON 工具(如 Jackson)复杂对象复制,需彻底隔离引用关系
2. 集合框架
  • HashMap底层结构(Java 8+)
    采用数组 + 链表 + 红黑树混合结构,平衡查询效率与内存开销:

    • 数组(桶):初始容量为 16(DEFAULT_INITIAL_CAPACITY),扩容时按 2 倍增长(newCap = oldCap << 1),存储链表头节点或红黑树根节点。
    • 链表:哈希冲突时(不同 key 计算出相同索引),用链表存储元素,查询时间复杂度为 O (n)。
    • 红黑树:当链表长度超过 8(TREEIFY_THRESHOLD)且数组长度≥64(MIN_TREEIFY_CAPACITY)时,链表转为红黑树(查询时间复杂度优化为 O (log n));当长度≤6(UNTREEIFY_THRESHOLD)时,红黑树转回链表(降低树结构维护成本)。

    优化点

    • 哈希计算:(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16),通过高位与低位异或,减少哈希冲突(将高位信息融入低位)。
    • 扩容机制:使用高位掩码计算新索引(newIndex = oldCap + (index & (oldCap - 1))),避免节点重新哈希,直接定位新位置。
  • HashMap线程安全性
    HashMap非线程安全,多线程并发操作可能导致:

    • 数据覆盖:多线程同时put时,可能覆盖彼此的结果。
    • Java 7 死循环:链表采用头插法,扩容时可能导致链表成环(Java 8 改为尾插法解决,但仍非线程安全)。

    线程安全的 Map 实现

    • ConcurrentHashMap(推荐):
      • Java 7:基于分段锁(Segment,每个 Segment 是独立的哈希表),并发度为 Segment 数量(默认 16)。
      • Java 8:移除 Segment,改用 CAS + synchronized(只锁单个桶),并发效率更高,支持computeIfAbsent等原子操作。
    • Hashtable:全表锁(所有方法用synchronized修饰),并发性能差,已淘汰。
    • Collections.synchronizedMap(Map):包装普通 Map,通过同步代码块(synchronized (mutex))保证安全,性能一般(锁整个 Map)。
  • ArrayList vs LinkedList

    特性ArrayListLinkedList
    底层结构动态数组(连续内存空间)双向链表(非连续内存)
    随机访问O (1)(通过索引直接访问)O (n)(需遍历链表)
    插入 / 删除中间操作 O (n)(需移动元素),尾部操作 O (1)中间操作 O (1)(修改指针),但定位节点需 O (n)
    内存占用较少(连续空间,有扩容冗余)较多(每个节点含前后指针)
    扩容机制容量不足时扩容为 1.5 倍(oldCap + (oldCap >> 1)无扩容(动态添加节点)
    适用场景频繁查询、尾部增删频繁中间插入 / 删除
3. JVM
  • 内存区域划分

    区域类型包含区域特点可能的异常
    线程私有虚拟机栈存储栈帧(局部变量表、操作数栈、返回地址等),方法调用时创建,结束时销毁。栈深度超过限制→StackOverflowError;内存不足→OutOfMemoryError
    本地方法栈native方法(如Object.hashCode())提供内存空间。同虚拟机栈。
    程序计数器记录当前线程执行的字节码行号(如分支、循环、跳转)。唯一不会抛出 OOM 的区域。
    线程共享存储对象实例、数组,GC 主要工作区域(分新生代、老年代)。内存不足→OutOfMemoryError
    方法区(元空间)存储类信息(结构、方法数据)、常量池、静态变量、JIT 编译后的代码。JDK 8 前(永久代)可能 OOM;JDK 8 后元空间使用本地内存,默认无上限(可通过-XX:MaxMetaspaceSize限制)。
  • 垃圾回收(GC)判定机制
    采用可达性分析算法

    • GC Roots(根对象):
      • 虚拟机栈中局部变量表引用的对象(如方法内的User user = new User())。
      • 方法区中类静态变量引用的对象(如static User user = new User())。
      • 方法区中常量引用的对象(如String s = "abc"中的"abc")。
      • 本地方法栈中native方法引用的对象。
    • 判定流程:从 GC Roots 出发,遍历所有可达对象并标记;未被标记的对象为不可达对象,可被回收(但并非立即回收,需经历多次标记)。

    引用类型对回收的影响

    • 强引用(User u = new User()):永不回收,OOM 也不释放。
    • 软引用(SoftReference<User>):内存不足时回收,用于缓存(如图片缓存)。
    • 弱引用(WeakReference<User>):GC 时立即回收,用于临时关联(如ThreadLocal的 key)。
    • 虚引用(PhantomReference<User>):仅用于跟踪对象回收,必须配合引用队列,无实际引用意义(如堆外内存回收)。
  • 类加载机制
    遵循双亲委派模型(防止类重复加载),流程:

    1. 加载(Loading):通过类全限定名(如com.xxx.User)读取.class文件到内存,生成Class对象(存方法区)。
    2. 验证(Verification):检查字节码合法性(格式、语义、字节码指令、符号引用),防止恶意 class 文件。
    3. 准备(Preparation):为静态变量(static)分配内存并赋默认值(如static int a = 10,此处赋 0)。
    4. 解析(Resolution):将符号引用(如类名、方法名)转为直接引用(内存地址)。
    5. 初始化(Initialization):执行<clinit>()方法(合并静态变量赋值与静态代码块,按顺序执行)。
    6. 使用(Using):实例化对象、调用方法。
    7. 卸载(Unloading):类不再被引用且类加载器被回收时,Class对象被卸载(极少发生)。
  • 垃圾回收机制

    • 分代收集理论

      • 新生代(Young Generation):对象存活时间短(如方法内临时变量),约 90% 对象会被回收,频繁触发Minor GC(年轻代 GC)。
        • 结构:Eden 区(80%) + From Survivor 区(10%) + To Survivor 区(10%)。
        • 算法:复制算法(将 Eden 和 From Survivor 存活对象复制到 To Survivor,清空原区域,无碎片)。
      • 老年代(Old Generation):对象存活时间长(如缓存对象),触发Full GC(全局 GC)频率低。
        • 算法:标记 - 清除(标记存活对象,清除未标记对象,可能产生碎片)或标记 - 整理(标记后将存活对象压缩到一端,消除碎片)。
    • GC 算法实现

      • Serial GC:单线程回收,STW(Stop The World)时间长,适合客户端应用(如桌面程序)。
      • Parallel GC:多线程回收,以吞吐量(用户线程运行时间 / 总时间)为目标,JDK 8 默认年轻代收集器。
      • CMS GC(Concurrent Mark Sweep):低延迟优先,步骤:初始标记(STW)→并发标记→重新标记(STW)→并发清除(仅初始和重新标记 STW),缺点:内存碎片多、CPU 消耗高(JDK 9 后废弃)。
      • G1 GC(Garbage-First):区域化分代式收集器(取代 CMS),将堆分为多个 Region,兼顾吞吐量和延迟,JDK 9 + 默认,适合大堆场景(如 8G 以上)。
  • JVM 调优经验

    • 核心参数

      bash

      -Xms2g -Xmx2g  # 堆初始和最大大小(设为相同,避免动态扩容)
      -XX:NewRatio=2  # 老年代:新生代=2:1(默认)
      -XX:SurvivorRatio=8  # Eden:From:To=8:1:1
      -XX:MaxMetaspaceSize=256m  # 元空间上限(默认无上限)
      -XX:+UseG1GC  # 使用G1垃圾回收器
      -XX:MaxGCPauseMillis=200  # G1目标最大STW时间(毫秒)
      -XX:+HeapDumpOnOutOfMemoryError  # OOM时生成堆转储文件
      -XX:HeapDumpPath=/path/to/dump  # 堆转储文件路径
      
    • 调优场景
      • Full GC频繁:检查是否有大对象频繁进入老年代(如大集合未释放),增大老年代内存(-Xmx)或调整晋升阈值(-XX:MaxTenuringThreshold,默认 15)。
      • OOM(Java heap space):通过jmap -heap <pid>分析堆使用,结合堆转储文件(jhat或 MAT 工具)定位内存泄漏(如静态集合未清理)。
      • 延迟过高:切换至 G1 或 ZGC(超低延迟),调小-XX:MaxGCPauseMillis
4. 多线程
  • 线程安全集合

    • ConcurrentHashMap:线程安全的哈希表(见上文)。
    • CopyOnWriteArrayList:读写分离,写时复制新数组(add/set时),读无需锁,适合读多写少场景(如配置列表)。
    • ConcurrentLinkedQueue:无锁并发队列,基于 CAS 实现,高效(生产者 - 消费者模型)。
    • BlockingQueue:阻塞队列(ArrayBlockingQueueLinkedBlockingQueue),支持put(满时阻塞)和take(空时阻塞),适合异步通信。
  • 线程池

    • 核心参数(ThreadPoolExecutor

      java

      new ThreadPoolExecutor(corePoolSize,    // 核心线程数(长期存活,即使空闲)maximumPoolSize, // 最大线程数(核心 + 临时线程)keepAliveTime,   // 临时线程空闲存活时间unit,            // 时间单位(如TimeUnit.SECONDS)workQueue,       // 工作队列(存放等待执行的任务)threadFactory,   // 线程工厂(自定义线程名、优先级等)handler          // 拒绝策略(队列满且线程达最大时触发)
      )
      
    • 参数设置策略

      • 核心线程数(corePoolSize)
        • CPU 密集型任务(如计算):CPU核心数 + 1(减少线程切换,充分利用 CPU)。
        • IO 密集型任务(如网络请求、文件读写):CPU核心数 * 2(线程多处于等待状态,需更多线程提高吞吐量)。
      • 最大线程数(maximumPoolSize):通常为核心线程数的 2 倍,需结合队列容量(队列满时才创建临时线程)。
      • 工作队列
        • LinkedBlockingQueue:无界队列(默认容量 Integer.MAX_VALUE),适合高吞吐但需控制内存(避免 OOM)。
        • ArrayBlockingQueue:有界队列,适合资源有限场景(需合理设置容量,避免队列过大导致内存溢出)。
        • SynchronousQueue:无缓冲队列(直接移交任务给线程),适合快速响应场景(配合maximumPoolSize为 Integer.MAX_VALUE)。
      • 拒绝策略
        • AbortPolicy:抛RejectedExecutionException(默认)。
        • CallerRunsPolicy:由提交任务的线程执行(减缓提交速度,自带限流)。
        • DiscardPolicy:默默丢弃新任务。
        • DiscardOldestPolicy:丢弃队列中最旧的任务,尝试提交新任务。
    • 使用示例

      java

      // 构建线程池
      ExecutorService executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS,new LinkedBlockingQueue<>(100),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy()
      );
      // 提交任务
      executor.submit(() -> System.out.println("Task running")); // 返回Future
      // 关闭线程池
      executor.shutdown(); // 执行完现有任务后关闭,不再接受新任务
      
  • synchronized锁升级
    synchronized通过锁升级机制优化性能(从低效到高效):

    1. 无锁状态:对象刚创建,无线程竞争,Mark Word 存储对象哈希码。
    2. 偏向锁:同一线程多次获取锁,Mark Word 记录线程 ID,避免 CAS 操作(适用于单线程场景)。
    3. 轻量级锁:多线程交替获取锁,通过 CAS 竞争锁(自旋尝试),避免重量级锁开销(适用于短时间竞争)。
    4. 重量级锁:多线程并发竞争激烈,自旋失败后膨胀为重量级锁(依赖操作系统互斥量),线程阻塞等待(适用于长时间竞争)。

二、MySQL 数据库

1. 索引
  • 底层结构(InnoDB)
    采用B + 树,原因:

    • 磁盘友好:非叶子节点仅存索引键,叶子节点存储数据(聚集索引)或主键(二级索引),单节点存储更多键,减少 I/O 次数。
    • 范围查询高效:叶子节点通过双向链表连接,支持快速范围扫描(如WHERE id > 100 AND id < 200)。
    • 平衡性好:B + 树为平衡树,查询时间稳定在 O (log n)。

    对比 B 树:B 树叶子节点和非叶子节点都存数据,单节点存储键数少,树高更高,I/O 效率低。

  • 索引类型

    • 聚集索引(主键索引):按主键排序,叶子节点存储完整数据行,InnoDB 表必存在(无主键则选唯一索引,否则隐式生成 6 字节 ROWID)。
    • 二级索引(非主键索引):按索引列排序,叶子节点存储主键值,查询时需通过主键回表(到聚集索引查完整数据)。
    • 联合索引:多列组合索引(如(a,b,c)),遵循最左前缀原则(查询条件含aa+ba+b+c时生效)。
  • 索引失效场景

    • 未遵循最左前缀原则(如联合索引(a,b,c),查询WHERE b=1则失效)。
    • 对索引列进行计算 / 函数操作(如WHERE YEAR(create_time) = 2023create_time索引失效)。
    • 使用!=<>NOT IN(优化器可能放弃索引,选择全表扫描)。
    • 隐式类型转换(如varchar列用数字查询:WHERE name = 123,索引失效)。
    • OR条件中存在非索引列(如WHERE a=1 OR b=2,仅a有索引则失效)。
    • LIKE以通配符开头(如WHERE name LIKE '%张',无法使用索引)。
    • 数据量极小(优化器认为全表扫描比走索引更快)。
    • IS NULL/IS NOT NULL(取决于字段是否允许为 NULL 及数据分布)。
  • 索引设计原则

    • 适合建索引:高频查询条件(WHERE)、排序 / 分组字段(ORDER BY/GROUP BY)、联合索引需满足最左前缀。
    • 不适合建索引:低区分度字段(如性别、状态,基数低)、频繁更新字段(索引维护成本高)、表数据量极少(全表扫描更快)。
2. 事务
  • ACID 特性

    • 原子性(Atomicity):事务要么全执行,要么全回滚。由Undo Log实现(记录数据修改前状态,回滚时恢复)。
    • 一致性(Consistency):事务执行前后数据状态合法(如转账后总金额不变)。由原子性、隔离性、持久性共同保证。
    • 隔离性(Isolation):多事务并发时,操作互不干扰。由锁(行锁、间隙锁)和 MVCC(多版本并发控制)实现。
    • 持久性(Durability):事务提交后数据永久保存。由Redo Log实现(先写日志再刷盘,崩溃后重放日志恢复)。
  • 隔离级别

    隔离级别脏读(读未提交数据)不可重复读(重读数据变)幻读(新增数据被读到)实现方式
    读未提交(RU)存在存在存在无锁,直接读取最新数据
    读已提交(RC)不存在存在存在MVCC(快照读,每次读新快照)
    可重复读(RR)不存在不存在不存在(InnoDB)MVCC + 间隙锁(防止新增数据)
    串行化(Serial)不存在不存在不存在全表锁(事务按顺序执行)

    幻读解决:InnoDB 在 RR 级别通过间隙锁(锁定索引范围,如WHERE id > 10会锁定 10 以上的间隙)防止其他事务插入新行,避免幻读。显式加锁SELECT ... FOR UPDATE也可解决。

3. 日志
日志类型作用特点
Binlog(二进制日志)记录所有写操作(DDL、DML),用于主从复制(从库同步主库日志)和数据恢复(mysqlbinlog回放)。逻辑日志(记录 SQL 语句或行变更),追加写入,可通过expire_logs_days自动清理。
Redo Log(重做日志)InnoDB 引擎日志,记录数据页修改,保证事务持久性(崩溃后重放日志恢复数据)。物理日志(记录页地址和修改内容),循环写入(固定大小),有innodb_log_file_size控制大小。
Undo Log(回滚日志)InnoDB 引擎日志,记录数据修改前状态,用于事务回滚和 MVCC(提供历史版本)。事务提交后可删除,存储在 undo 表空间。
Slow Query Log(慢查询日志)记录执行时间超过long_query_time的 SQL,用于优化慢查询。默认关闭,需手动开启(slow_query_log = ON)。
Error Log(错误日志)记录 MySQL 启动、运行、关闭过程中的错误信息,用于排查故障。必开启,路径由log_error指定。
4. 慢查询优化
  • 定位慢查询

    1. 开启慢查询日志:

      sql

      SET GLOBAL slow_query_log = ON;
      SET GLOBAL long_query_time = 2; -- 阈值(秒)
      SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
      
    2. 使用EXPLAIN分析执行计划,关注:
      • type:访问类型(ALL全表扫描,ref/range走索引)。
      • key:实际使用的索引(NULL表示未走索引)。
      • rows:预估扫描行数(值越小越好)。
      • ExtraUsing filesort(文件排序,需优化)、Using index(覆盖索引,优)。
  • 优化措施

    • 索引优化:添加缺失索引,修复失效索引(如避免函数操作索引列)。
    • SQL 改写:拆分复杂查询、避免SELECT *(用覆盖索引)、用JOIN代替子查询。
    • 数据库调优:调整innodb_buffer_pool_size(建议设为内存 50%-70%)、分库分表(数据量超千万时)。

三、Redis 缓存

1. 数据结构
数据类型底层结构(核心)特点应用场景
String简单动态字符串(SDS)二进制安全(可存文本、图片),支持INCR/DECR自增自减缓存(用户信息)、计数器(阅读量)、分布式 ID
Hash哈希表(数组 + 链表)键值对集合(field-value),适合存储对象存储用户信息(user:1 {name: "a", age: 20}
List双向链表 + 压缩列表(元素少且小)有序,可重复,支持LPUSH/RPOP两端操作消息队列、最新列表(热搜前 10)
Set哈希表 + 整数集合(元素为整数且少)无序,唯一元素,支持SINTER(交集)/SUNION(并集)标签(文章标签去重)、共同好友
ZSet跳跃表 + 哈希表score排序,元素唯一,支持ZRANK(排名)排行榜(游戏积分)、延迟队列(按时间 score 排序)
2. 持久化
持久化方式原理优点缺点
RDB定时生成内存快照(.rdb 文件),通过SAVE(阻塞)或BGSAVE(后台线程)触发。恢复速度快(直接加载二进制文件)、文件小可能丢失最后一次快照后的修改(如突发宕机)。
AOF记录所有写命令(追加到.aof 文件),通过appendfsync控制同步策略(always/everysec/no)。数据安全性高(everysec最多丢 1 秒数据)文件大(命令冗余)、恢复速度慢(需重放命令)。

混合持久化(Redis 4.0+):AOF 文件开头为 RDB 快照,后续为增量命令,兼顾两者优点(aof-use-rdb-preamble yes开启)。

3. 主从复制
  • 流程

    1. 从节点执行SLAVEOF <master-ip> <port>,建立连接。
    2. 主节点执行BGSAVE生成 RDB,发送给从节点(全量复制)。
    3. 从节点加载 RDB 后,主节点通过repl_backlog_buffer同步增量写命令(增量复制)。
  • 问题及解决

    • 数据延迟:主从异步复制导致从库数据滞后,适合读多写少场景(写主读从)。
    • 脑裂:网络分区时,从库被误认为主库,导致数据不一致。解决:min-slaves-to-write = 1(主库至少 1 个从库连接才允许写)。
4. 缓存问题(缓存三兄弟)
问题原因解决方案
缓存穿透查询不存在的数据(如id=-1),缓存和 DB 都无数据,请求直达 DB。1. 布隆过滤器(提前过滤不存在的 key);2. 缓存空值(短期有效,如 5 分钟)。
缓存击穿热点 key 过期瞬间,大量并发请求直达 DB。1. 互斥锁(Redis SETNX,只让一个线程更新缓存);2. 热点 key 永不过期(后台异步更新)。
缓存雪崩大量 key 同时过期或缓存集群宕机,请求全压到 DB。1. 随机过期时间(避免集中过期);2. 缓存集群(主从 + 哨兵);3. 服务降级 / 限流。
5. 常见用途
  • 缓存:存储高频访问数据(如商品详情),减轻数据库压力(设置合理 TTL 避免雪崩)。
  • 分布式锁SET key value NX PX 30000(NX:不存在才设置,PX:30 秒过期),释放锁时验证 value(避免误删)。
  • 排行榜:ZSET 的ZADD(更新分数)、ZREVRANGE(获取 TopN)实现实时排名。
  • 计数器INCR/DECR原子操作,适合阅读量、点赞数等。
  • 消息队列:List 的LPUSH(生产者)和BRPOP(消费者,阻塞弹出)实现简单队列。
6. 引入组件的考虑因素
  • 必要性:是否解决核心问题(如缓存是否真能提升性能,避免过度设计)。
  • 性能:吞吐量(QPS)、延迟(响应时间)是否满足业务需求(Redis 单机 QPS 可达 10 万 +)。
  • 运维成本:部署、监控(INFO命令、Prometheus)、扩缩容难度(集群是否支持动态添加节点)。
  • 社区支持:文档完善度、版本更新频率(Redis 社区活跃,问题解决快)。
  • 数据一致性:与数据库的同步策略(Cache-Aside、Write-Through)是否可控。
7. 高可用方案
模式原理特点适用场景
主从复制主节点写入,从节点同步数据并提供读服务(一主多从)。部署简单,提升读吞吐量;主节点故障需手动切换。读多写少,可用性要求不高。
哨兵(Sentinel)哨兵集群监控主从节点,主节点故障时自动选举新主节点(故障转移)。自动故障转移,高可用;需至少 3 个哨兵节点(避免脑裂)。生产环境基础高可用方案。
Cluster 集群数据分片到 16384 个槽(Slot),每个节点负责部分槽,多主多从(每个主节点有从节点)。水平扩展(动态添加节点)、高可用;支持跨节点操作(MGET需指定槽)。数据量大(超 10GB)、高并发场景。
8. Redis Cluster 的插槽(Slot)
  • 作用
    • 数据分片CRC16(key) % 16384计算 key 所属槽,每个节点负责部分槽(如 3 个主节点各负责~5461 个槽),实现数据分布式存储。
    • 路由转发:客户端连接任一节点,若 key 不在当前节点负责的槽,节点返回MOVED指令指引至目标节点。
    • 动态扩容:槽可在节点间迁移(CLUSTER MIGRATE),无需停机(迁移过程中双写保证数据一致性)。

四、Spring 框架

1. IOC(控制反转)
  • 核心思想:将对象创建、依赖管理交给 Spring 容器,而非手动new对象,降低耦合。
  • 依赖注入方式
    • @Autowired:按类型注入(默认),配合@Qualifier按名称注入。
    • @Resource:按名称注入(默认),无名称时按类型注入(JDK 注解,非 Spring)。
    • 构造器注入:通过构造方法传递依赖(推荐,避免循环依赖问题)。
  • Bean 初始化流程
    1. 读取配置(XML / 注解)→ 2. 解析为BeanDefinition → 3. 实例化(new对象)→ 4. 依赖注入(填充属性)→ 5. 初始化(@PostConstructInitializingBean等)→ 6. 放入容器。
2. AOP(面向切面编程)
  • 原理:基于动态代理,在不修改源码的情况下为方法添加额外功能(如日志、事务)。

    • JDK 代理:目标类实现接口时,Proxy.newProxyInstance()生成代理类(实现同一接口)。
    • CGLIB 代理:目标类无接口时,Enhancer生成子类代理(重写目标方法),无法代理final类 / 方法。
  • 核心概念

    • 切面(@Aspect):封装横切逻辑的类(如日志切面)。
    • 切点(@Pointcut):定义拦截哪些方法(如execution(* com.xxx.service.*(..)))。
    • 通知(Advice):切点执行时的动作(@Before前置、@After后置、@Around环绕等)。
  • 应用场景:日志记录、事务管理(@Transactional)、权限校验、性能监控。

3. Bean 生命周期
  1. 实例化:容器通过构造方法创建 Bean 对象(new)。
  2. 属性赋值:注入依赖(@Autowired/setter 方法)。
  3. 初始化
    • 执行Aware接口回调(BeanNameAwareApplicationContextAware等)。
    • 执行@PostConstruct注解方法(JSR-250 规范,初始化前)。
    • 执行InitializingBean.afterPropertiesSet()方法(Spring 接口)。
    • 执行自定义init-method(XML 配置或@Bean(initMethod))。
  4. 使用:Bean 放入容器,供其他组件调用。
  5. 销毁
    • 执行@PreDestroy注解方法(JSR-250 规范,销毁前)。
    • 执行DisposableBean.destroy()方法(Spring 接口)。
    • 执行自定义destroy-method(XML 配置或@Bean(destroyMethod))。
4. 循环依赖解决

场景:A 依赖 B,B 依赖 A。

Spring 通过三级缓存解决单例 Bean 循环依赖:

  • 一级缓存(singletonObjects):存放完全初始化的 Bean。
  • 二级缓存(earlySingletonObjects):存放半成品 Bean(已实例化未赋值)。
  • 三级缓存(singletonFactories):存放 Bean 工厂(用于生成代理对象)。

流程

  1. A 实例化后,放入三级缓存 → 2. A 需要注入 B,容器初始化 B → 3. B 需要注入 A,从三级缓存获取 A 的早期引用(若 A 需代理,通过工厂生成代理),放入二级缓存 → 4. B 初始化完成,注入 A,放入一级缓存 → 5. A 注入 B,完成初始化,放入一级缓存(移除二、三级缓存)。

五、消息队列(Kafka)

1. 消息不丢失保证
环节措施
生产者acks=all:消息需被所有 ISR(同步副本)确认后才算发送成功。
- 开启重试(retries=N):发送失败自动重试。
- 用linger.ms批量发送,减少网络开销。
Brokerreplication.factor≥3:每个分区至少 3 个副本。
min.insync.replicas≥2:ISR 中至少 2 个副本存活才允许写入。
- 禁用unclean.leader.election.enable(不允许非 ISR 副本成为 leader)。
消费者- 关闭自动提交(enable.auto.commit=false),手动提交偏移量(commitSync())。
- 处理完消息后再提交,避免消费失败但已提交偏移量。
- 幂等处理:通过消息 ID 去重,防止重复消费。

六、设计模式

1. 单例模式(Singleton Pattern)
  • 概念:保证一个类在整个应用中只有一个实例,并提供一个全局访问点。
  • 核心思想:通过私有化构造方法,防止外部直接创建实例;在类内部自行创建唯一实例,并提供静态方法供外部获取。
  • 适用场景
    • 全局配置类(如读取配置文件的工具类),确保配置信息一致。
    • 数据库连接池,避免频繁创建和关闭连接带来的性能损耗。
    • 日志工具类,保证日志输出的一致性和顺序性。
  • 注意点:需考虑线程安全问题(如懒汉式需加锁),以及反射、序列化对单例的破坏。
2. 工厂模式(Factory Pattern)

包括简单工厂、工厂方法、抽象工厂三种形式:

  • 简单工厂模式
    • 概念:由一个工厂类根据传入的参数,动态决定创建哪一种产品类的实例。
    • 适用场景:产品种类较少且固定,如不同类型的日志记录器(文件日志、控制台日志)的创建。
  • 工厂方法模式
    • 概念:定义一个创建产品的接口,由子类决定具体创建哪种产品,将产品创建延迟到子类。
    • 适用场景:产品种类可能扩展,如不同数据库(MySQL、Oracle)的连接工厂,新增数据库时只需新增对应的工厂子类。
  • 抽象工厂模式
    • 概念:提供一个接口,用于创建一系列相关或相互依赖的产品族,而无需指定具体类。
    • 适用场景:需要创建多个产品族,且产品族内的产品相互关联,如不同操作系统(Windows、Mac)下的按钮、文本框等组件族的创建。
3. 观察者模式(Observer Pattern)
  • 概念:定义对象间的一种一对多依赖关系,当一个对象(被观察者)状态发生改变时,所有依赖它的对象(观察者)会自动收到通知并更新。
  • 核心思想:解耦被观察者和观察者,两者通过抽象接口交互,互不依赖具体实现。
  • 适用场景
    • 事件监听机制(如 GUI 中的按钮点击事件,多个组件监听同一按钮)。
    • 消息通知系统(如公众号推送,订阅者接收消息)。
    • 状态变化同步(如股票价格更新,多个指标面板实时刷新)。
4. 装饰器模式(Decorator Pattern)
  • 概念:动态地给一个对象添加额外的职责,同时不改变其原有的结构。相比继承,更灵活地扩展功能。
  • 核心思想:通过创建装饰器类包裹原对象,装饰器实现与原对象相同的接口,并在调用原对象方法前后添加新功能。
  • 适用场景
    • 功能需要动态扩展且可组合的场景,如 IO 流(BufferedInputStream 装饰 FileInputStream,增加缓冲功能)。
    • 避免使用多层继承导致的类爆炸,如给咖啡添加不同配料(牛奶、糖),每种配料作为一个装饰器。
5. 适配器模式(Adapter Pattern)
  • 概念:将一个类的接口转换成客户端期望的另一个接口,使原本因接口不兼容而无法一起工作的类能够协同工作。
  • 核心思想:创建适配器类,实现目标接口,并持有被适配类的实例,在适配器方法中调用被适配类的对应方法。
  • 适用场景
    • 集成第三方组件,其接口与本地系统接口不匹配时(如第三方支付接口适配本地支付服务)。
    • 旧系统改造,需复用旧类但接口不符合新需求时。
  • 分类
    • 类适配器:通过继承被适配类实现适配(单继承限制,较少使用)。
    • 对象适配器:通过持有被适配类实例实现适配(更灵活,常用)。
6. 代理模式(Proxy Pattern)
  • 概念:为其他对象提供一种代理,以控制对原对象的访问。代理对象在原对象和客户端之间起到中介作用。
  • 核心思想:代理类与原对象实现同一接口,客户端通过代理类访问原对象,代理类可在调用原对象方法前后添加额外逻辑。
  • 适用场景
    • 远程代理:为远程对象(如分布式服务)提供本地代理,屏蔽网络通信细节。
    • 安全代理:控制对敏感对象的访问(如权限校验)。
    • 延迟加载:在真正需要时才创建 heavy 资源(如大图片加载前的代理)。
    • AOP(面向切面编程):如 Spring 的事务管理,通过代理在方法执行前后添加事务控制逻辑。
7. 模板方法模式(Template Method Pattern)
  • 概念:定义一个操作中的算法骨架,将某些步骤延迟到子类中实现。子类可重写具体步骤,但不改变算法的整体结构。
  • 核心思想:父类中定义模板方法(包含算法骨架),并将可变步骤声明为抽象方法,由子类实现。
  • 适用场景
    • 多个子类有共同的操作流程,但部分步骤实现不同,如数据库访问(连接、执行 SQL、关闭连接的流程固定,SQL 语句和结果处理可变)。
    • 框架设计中,定义核心流程,允许用户自定义部分逻辑(如 Servlet 的 doGet/doPost 方法,由框架控制调用时机)。
8. 策略模式(Strategy Pattern)
  • 概念:定义一系列算法,将每个算法封装起来并使它们可相互替换,让算法的变化独立于使用算法的客户端。
  • 核心思想:创建策略接口,不同算法实现该接口;客户端持有策略接口的引用,可动态切换不同的策略实现。
  • 适用场景
    • 存在多种类似的算法,且需要根据场景动态选择时(如排序算法,可根据数据量选择冒泡、快排、归并等)。
    • 避免使用多重条件判断(if-else/switch),如电商平台的折扣策略(新用户折扣、会员折扣、节日折扣等)。
9. 迭代器模式(Iterator Pattern)
  • 概念:提供一种方法顺序访问聚合对象中的元素,而无需暴露聚合对象的内部结构。
  • 核心思想:定义迭代器接口(包含 hasNext、next 等方法),聚合对象提供获取迭代器的方法,客户端通过迭代器遍历元素。
  • 适用场景
    • 需统一遍历不同聚合结构(如数组、链表、集合)的场景,如 Java 中的 Iterator 接口,可遍历 List、Set 等不同集合。
    • 避免聚合对象暴露内部实现,如自定义容器类,通过迭代器提供安全的遍历方式。
10. 命令模式(Command Pattern)
  • 概念:将一个请求封装为一个对象,使请求的发送者和接收者解耦。发送者只需发送命令对象,无需知道接收者如何处理。
  • 核心思想:创建命令接口(包含执行方法),具体命令类实现接口并持有接收者引用;发送者持有命令对象,通过调用命令的执行方法完成请求。
  • 适用场景
    • 需记录、撤销、重做请求的场景(如文本编辑器的撤销操作,每个编辑命令可被保存和反向执行)。
    • 事件驱动系统(如 GUI 中的按钮点击,按钮持有命令对象,点击时执行命令)。
    • 批量任务处理(如任务队列,每个任务封装为命令,依次执行)。
http://www.lryc.cn/news/597202.html

相关文章:

  • LLM 中的 温度怎么控制随机性的?
  • AI驱动攻防升级,API安全走到关键档口
  • CentOS 7 Linux 用 yum 安装 Docker,含 Docker 镜像无法拉取问题(即 docker pull 失败)的解决方案
  • 路由器与交换机的区别
  • 数据结构之队列(C语言)
  • 【优选算法-多源 BFS】多源 BFS:解决多个起点的广度优先搜索
  • 【大模型文生图、文生音频实战Demo】基于Spring AI Alibaba和阿里百炼大模型实现文生图、文生视频
  • Android MediaCodec 的使用和源码实现分析
  • 2.1 为什么定义tensor数据结构?
  • 【有趣的跳跃一维数组问题】2022-7-27
  • 彻底掌握双列集合——Map接口以及实现类和常用API及其底层原理
  • 题解:P9468 [EGOI 2023] Candy / 糖果
  • 亚马逊云科技 上海AI研究院 突然解散 | AI早报
  • Taint Bug (污点漏洞):
  • GitHub 趋势日报 (2025年07月22日)
  • Docker 基础概念
  • 解决Node 17+版本与Metro、Webpack等兼容性问题(500)
  • 数据结构自学Day13 -- 快速排序--“分而治之”
  • ITIL 4:云计算与微服务对组织架构的影响
  • kotlin基础【1】
  • android studio(NewsApiDemo)100%kotlin
  • 【前端】当前主流的 CSS 预处理器语言Sass / SCSS、Less、Stylus
  • kotlin基础【2】
  • BaaS平台(Supabase)
  • 数据结构自学Day13 -- 快速排序--“前后指针法”
  • 《计算机网络》实验报告六 电子邮件
  • 数据结构(2)顺序表算法题
  • 【数据结构】二叉树的链式结构--用C语言实现
  • 数据结构系列之AVL树
  • Java冒泡排序的不同实现