mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
5996 字
12 分钟
博客学习日志
2026-03-05
2026-03-06

博客日志:后端研发深度复盘与面试集锦

一、 Java 进阶:从语法到实战

基础概念篇

1. 你在简历中提到熟悉异常处理,请解释 Error 和 Exception 的区别。

解答:

  • Error(错误): 通常是程序无法处理的严重问题,如 OutOfMemoryError。它们通常与 JVM 运行状态有关,不建议在代码中通过 try-catch 捕获。
  • Exception(异常): 程序可以处理的情况。分为:
    • 编译时异常 (Checked): 必须手动处理,否则无法编译。
    • 运行时异常 (Unchecked):NullPointerException,通常可以通过良好的编程习惯避免。

2. 什么是 Java 的 Stream API?它在处理项目数据(如人员得分)时有什么优势?

解答:

  • 定义: Stream 是 Java 8 引入的处理集合数据的函数式编程方式。
  • 优势: 在“科研人员画像系统”中计算人员综合得分时,通过 Stream API 可以极其简洁地完成数据的过滤 (filter)映射 (map)聚合 (reduce),大大减少了冗余的 for 循环代码,提高了代码的可读性。

3. 谈谈你对 Java 泛型中类型擦除的理解。

解答:

  • 定义: 泛型是 Java 5 引入的特性,但在编译阶段有效。编译后的字节码中,泛型类型会被擦除,替换为原始类型(如 List<T> 变成 List),并自动插入强制类型转换。
  • 意义: 保证与旧版本 JVM 的向后兼容性。
  • 局限: 运行时无法获取泛型的具体类型,例如 new T() 是不允许的,也不能用 instanceof 检查泛型类型。

4. Java 中的 ==equals() 有什么区别?

解答:

  • == 运算符: 比较基本数据类型时,比较的是值;比较引用类型时,比较的是内存地址。
  • equals() 方法:Object 类的方法,默认行为也是比较内存地址。但通常被重写(如 StringInteger 类)来比较对象的内容是否相等。
  • 使用原则: 比较对象内容时,应重写 equals() 并同时重写 hashCode() 方法。

5. 什么是 Java 的反射机制?你在项目中如何使用它?

解答:

  • 定义: 反射是指在运行状态中,对于任意一个类,都能够获取其所有属性和方法;对于任意一个对象,都能调用其任意方法和属性。
  • 项目应用:
    • 动态代理: 在 Spring Security 中,利用反射实现 AOP,动态增强权限校验逻辑。
    • 框架配置: 在项目初始化时,通过反射读取注解信息,动态注册 Bean 或生成 SQL 语句。

集合框架篇

6. 请对比 HashMapHashtable 的区别。

解答:

  • 线程安全: Hashtable 是线程安全的(方法用 synchronized 修饰),HashMap 非线程安全。
  • 性能: HashMap 性能更高,单线程环境下优先使用。
  • Null 值: HashMap 允许一个 null 键和多个 null 值;Hashtable 不允许 null 键或 null 值。
  • 扩容机制: HashMap 初始容量 16,扩容为 2 倍;Hashtable 初始容量 11,扩容为 2 倍+1。

7. ConcurrentHashMap 是如何实现线程安全的?

解答:

  • JDK 1.7: 采用分段锁(Segment),将数据分成多个段,每个段独立加锁,提高并发度。
  • JDK 1.8+: 改用 synchronized + CAS(乐观锁)锁住链表或红黑树的头节点,粒度更细,并发性能更好。
  • 核心特性: 读操作基本无锁,写操作只锁住当前操作的桶,支持高并发场景。

8. ArrayListLinkedList 分别适用于什么场景?

解答:

  • ArrayList 基于动态数组实现。适合随机访问getset)频繁的场景,时间复杂度 O(1);但在中间插入/删除元素效率低,因为需要移动元素。
  • LinkedList 基于双向链表实现。适合频繁插入/删除(尤其是头部和尾部)的场景,时间复杂度 O(1);但随机访问效率低,需要遍历。

9. 如何保证遍历集合时不被其他线程修改?

解答:

  • Fail-Fast 机制: 使用 java.util 包下的集合(如 ArrayListHashMap),当多个线程并发修改时,会抛出 ConcurrentModificationException
  • Fail-Safe 机制: 使用 java.util.concurrent 包下的集合(如 CopyOnWriteArrayListConcurrentHashMap),它们通过复制原数据或分段锁来保证遍历时不被修改,不会抛出异常。

并发编程篇

10. synchronizedReentrantLock 有什么区别?

解答:

  • 使用方式: synchronized 是关键字,自动加解锁;ReentrantLock 是 API 类,需要手动 lock()unlock()
  • 功能特性: ReentrantLock 提供了更多高级功能,如公平锁可中断锁超时获取锁多个 Condition
  • 性能: 早期 synchronized 性能较差,但现在 JVM 对两者做了优化,性能差异不大。建议简单场景用 synchronized,复杂并发场景用 ReentrantLock

11. 什么是 volatile 关键字?它能保证原子性吗?

解答:

  • 作用:
    1. 可见性: 保证一个线程修改变量后,其他线程能立即看到最新值(禁止线程本地缓存)。
    2. 有序性: 禁止指令重排序优化(如 DCL 单例模式中必须用 volatile)。
  • 原子性: volatile 不能保证原子性。例如 count++ 操作,即使变量被 volatile 修饰,多线程下仍会数据错乱,需用 synchronizedAtomicInteger 保证原子性。

12. 说说线程池的核心参数(ThreadPoolExecutor)。

解答:

  • corePoolSize: 核心线程数,即使空闲也会保留。
  • maximumPoolSize: 最大线程数。
  • keepAliveTime: 非核心线程空闲存活时间。
  • workQueue: 任务等待队列(如 LinkedBlockingQueueSynchronousQueue)。
  • threadFactory: 创建线程的工厂(可自定义线程名)。
  • handler: 拒绝策略(如 AbortPolicy 抛出异常、CallerRunsPolicy 让提交线程自己执行)。

13. 如何理解 Java 内存模型(JMM)?

解答:

  • 定义: JMM 定义了多线程之间共享变量的可见性、原子性和有序性规则。
  • 核心概念:
    • 主内存: 所有变量都存储在主内存中。
    • 工作内存: 每个线程有自己的工作内存,保存主内存变量的副本。
    • 规则: 线程对变量的所有操作都必须在工作内存中进行,不能直接操作主内存;不同线程之间无法直接访问对方的工作内存,必须通过主内存传递。

JVM 与性能优化篇

14. 请描述 JVM 的内存区域划分。

解答:

  • 线程私有:
    • 程序计数器: 当前线程执行的字节码行号指示器。
    • 虚拟机栈: 存储局部变量表、操作数栈、方法出口等,每个方法调用对应一个栈帧入栈出栈。
    • 本地方法栈: 为 native 方法服务。
  • 线程共享:
    • 堆: 存放对象实例,垃圾回收的主要区域(分新生代、老年代)。
    • 方法区: 存储类信息、常量、静态变量、即时编译后的代码(元空间/Metaspace)。

15. 你遇到过哪些 OOM(OutOfMemoryError)场景?如何排查?

解答:

  • 常见场景:
    • 堆溢出: 对象过多且无法回收(死循环创建对象、内存泄漏)。排查用 jmap 或 MAT 分析堆转储文件。
    • 栈溢出: 递归调用过深或线程数过多。检查递归逻辑或线程池配置。
    • 元空间溢出: 加载类过多(热部署、反射生成类)。检查是否有类加载器泄漏。
  • 排查步骤:
    1. 添加 JVM 参数 -XX:+HeapDumpOnOutOfMemoryError 自动生成堆转储。
    2. 用 MAT 或 JProfiler 分析大对象引用链。
    3. 结合业务代码,定位内存泄漏点(如未关闭的资源、不正确的缓存)。

新特性与编码实践

16. Java 8 的 Optional 类有什么作用?如何使用?

解答:

  • 作用: 主要解决 NullPointerException 问题,提供一种更优雅的方式来处理可能为 null 的值。
  • 常用方法:
    • ofNullable():创建可能为空的 Optional。
    • orElse() / orElseGet():值为空时返回默认值。
    • map() / flatMap():对值进行转换。
    • ifPresent():值存在时执行操作。
  • 示例: Optional.ofNullable(user).map(User::getAddress).orElse("默认地址"),避免了多层 null 检查。

17. 什么是方法引用(Method Reference)?有哪些类型?

解答:

  • 定义: 方法引用是 Lambda 表达式的简化写法,用 :: 操作符表示。
  • 类型:
    • 静态方法引用: 类名::静态方法名(如 Math::max
    • 实例方法引用: 对象::实例方法名
    • 特定类型任意对象方法引用: 类名::实例方法名(如 String::length
    • 构造方法引用: 类名::new

18. 如何写出更优雅的 Java 代码?(编码规范与最佳实践)

解答:

  1. 遵循命名规范: 类名大驼峰,方法/变量小驼峰,常量全大写。
  2. 合理使用 Optional: 避免返回 null,减少 NPE 风险。
  3. 善用 Stream API: 替代复杂的循环和条件判断。
  4. 接口设计原则: 面向接口编程而非实现类。
  5. 异常处理: 不要捕获了异常什么都不做,至少打印日志。
  6. 单测覆盖: 关键业务逻辑必须有单元测试(JUnit + Mockito)。
  7. 代码复用: 提取公用方法,避免重复代码(DRY 原则)。

19. 什么是 Java 的 SPI 机制?

解答:

  • 定义: SPI(Service Provider Interface)是 JDK 内置的服务提供发现机制,用于实现框架的扩展性。
  • 工作原理:META-INF/services 目录下创建以接口全限定名命名的文件,内容为实现类的全限定名。通过 ServiceLoader 加载实现类。
  • 项目应用: 在开发 SDK 或框架时,利用 SPI 允许第三方扩展功能(如 JDBC 驱动加载、Spring 的 spring.factories)。

20. 谈谈你对“面向接口编程”的理解,并结合项目说明。

解答:

  • 理解: 面向接口编程意味着调用者只依赖接口定义,而不依赖具体实现类。这降低了代码耦合度,提高了可扩展性和可测试性。
  • 项目应用: 在“科研人员画像系统”中,我定义了 ScoreCalculator 接口,然后分别实现 PaperScoreCalculator(论文得分)、ProjectScoreCalculator(项目得分)等。当新增得分规则时,只需新增实现类,无需修改调用方的业务逻辑,符合开闭原则。

21. 什么是 Java 的 Stream API?它在处理项目数据(如人员得分)时有什么优势?

解答:

  • 定义: Stream 是 Java 8 引入的处理集合数据的函数式编程方式。
  • 优势: 在“科研人员画像系统”中计算人员综合得分时,通过 Stream API 可以极其简洁地完成数据的过滤 (filter)映射 (map)聚合 (reduce),大大减少了冗余的 for 循环代码,提高了代码的可读性。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

博客学习日志
https://www.zztzz.com.cn/posts/博客学习日志/
作者
zzt
发布于
2026-03-05
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00
💬Mizuki AI助手
呀~就是zzTzz大大闪闪发光的技术博客主页
标题已剧透:专注后端开发、主攻Java + Spring Boot的实战、踩坑与进阶小笔记~~
URL https://zztzz.com.cn/ 简洁有力,像一段优雅的代码!
Mizuki每次点开都忍不住小声赞叹:'zzTzz大人太厉害啦~'🧙‍♀️
需要我帮你找某类文章(比如JWT鉴权、Redis缓存)或读一篇入门指南吗?😊
04:51