1. Java面试核心要点解析
作为Java开发者,面试是职业生涯中不可避免的重要环节。本文将深入剖析Java面试中的核心知识点,从基础概念到高级特性,从内存模型到多线程并发,帮助你在面试中游刃有余。这些内容不仅适用于面试准备,更是日常开发中需要掌握的关键技能。
2. Java基础核心概念
2.1 多态的实现与价值
多态是面向对象编程的三大特性之一,它的实现需要三个关键要素:继承关系、方法重写,以及父类引用指向子类对象。这种机制带来的最直接好处是消除了类型之间的紧密耦合,显著提升了代码的可扩展性和灵活性。
在实际开发中,多态允许我们通过统一的接口处理不同类型的对象。比如,假设我们有一个图形绘制系统,可以定义一个Shape父类,然后派生出Circle、Rectangle等子类。当需要绘制图形时,我们只需要调用Shape的draw()方法,而不必关心具体是哪种图形。这种设计带来的优势是显而易见的:当需要添加新的图形类型时,只需创建新的子类并实现draw()方法,现有的绘图代码完全不需要修改。
多态的设计遵循了开闭原则(OCP)——对扩展开放,对修改关闭。这是构建可维护、可扩展系统的重要原则。
2.2 反射机制深度解析
反射是Java中一项强大的特性,它允许程序在运行时动态获取类的完整结构信息,并能操作这些信息。反射的核心思想是:对于任意一个类,在运行时都能知道它的所有属性和方法;对于任意一个对象,都能调用它的任意方法。
反射的典型应用场景包括:
- 访问权限不足的类成员(慎用,可能破坏封装性)
- 实现自定义注解处理器
- 动态加载第三方JAR包中的类
- 实现延迟加载,优化启动性能
获取Class对象的三种常用方式:
java复制// 1. 通过Class.forName()
Class<?> clazz1 = Class.forName("java.lang.String");
// 2. 通过类字面常量
Class<String> clazz2 = String.class;
// 3. 通过对象的getClass()方法
String str = "hello";
Class<? extends String> clazz3 = str.getClass();
反射虽然强大,但也要注意其性能开销和安全隐患。过度使用反射会使代码难以理解和维护,并可能带来性能问题。
2.3 对象创建的多种方式
Java中创建对象主要有五种方式:
-
new关键字:最常用的方式,直接调用构造函数
java复制Object obj = new Object(); -
Class.newInstance():通过反射创建对象(要求类有无参构造函数)
java复制Object obj = Object.class.newInstance(); -
Constructor.newInstance():更灵活的反射创建方式
java复制Constructor<Object> constructor = Object.class.getConstructor(); Object obj = constructor.newInstance(); -
clone()方法:实现Cloneable接口的对象复制
java复制Object obj = originalObject.clone(); -
反序列化:通过ObjectInputStream从字节流重建对象
java复制ObjectInputStream in = new ObjectInputStream(inputStream); Object obj = in.readObject();
每种方式都有其适用场景,理解它们的区别对于编写高质量的Java代码非常重要。
3. 并发编程基础
3.1 线程、程序与进程
程序是存储在磁盘上的静态代码文件,它包含了执行特定任务所需的指令和数据。程序本身是没有生命力的,只有当它被加载到内存中执行时,才会成为一个活跃的实体。
进程是程序的一次执行实例,是操作系统进行资源分配和调度的基本单位。每个进程都有自己独立的内存空间和系统资源(如文件句柄、网络连接等)。进程之间的通信需要通过特定的IPC(进程间通信)机制。
线程是比进程更小的执行单位,一个进程可以包含多个线程。与进程不同,同一进程内的线程共享内存空间和系统资源。线程间的切换比进程切换要轻量得多,因此被称为"轻量级进程"。
关键区别:
- 进程是资源分配的单位,线程是CPU调度的单位
- 进程间相互独立,线程间可能相互影响
- 进程切换开销大,线程切换开销小
- 进程通信复杂,线程通信简单(但需注意同步问题)
3.2 ArrayList特性与使用技巧
ArrayList是Java集合框架中最常用的列表实现之一,它的核心特点包括:
- 基于数组实现:底层使用Object[]数组存储元素
- 动态扩容:当元素数量达到当前容量时,会自动扩容(通常增加50%)
- 随机访问高效:通过索引访问元素的时间复杂度是O(1)
- 线程不安全:多线程环境下需要外部同步
性能优化建议:
- 如果能预估元素数量,使用
ensureCapacity()预先设置大小,避免多次扩容 - 在列表中间插入/删除元素会导致数组拷贝,性能较差,应考虑使用LinkedList
- 迭代时修改列表会抛出ConcurrentModificationException,需要使用Iterator的remove方法
java复制// 预先设置容量优化示例
ArrayList<String> list = new ArrayList<>();
list.ensureCapacity(1000); // 避免后续添加元素时的多次扩容
for (int i = 0; i < 1000; i++) {
list.add("item-" + i);
}
3.3 HashMap与Hashtable对比
HashMap和Hashtable都是键值对存储结构,但存在重要区别:
| 特性 | HashMap | Hashtable |
|---|---|---|
| 诞生版本 | JDK 1.2 | JDK 1.0 |
| 父类 | AbstractMap | Dictionary |
| 线程安全 | 非线程安全 | 线程安全(方法同步) |
| null键/值 | 允许 | 不允许 |
| 哈希计算 | 重新计算hash | 直接使用对象的hashCode() |
| 初始容量 | 16 | 11 |
| 扩容机制 | 2倍 | 2n+1 |
| 迭代器 | Iterator | Iterator + Enumeration |
现代Java开发中,Hashtable已经很少使用,它的线程安全特性通常被ConcurrentHashMap取代,后者提供了更好的并发性能。
4. JVM核心原理
4.1 JVM内存模型详解
JVM内存模型是Java程序员必须理解的核心概念,它决定了程序如何分配和使用内存。JVM内存主要分为以下几个区域:
- 程序计数器:线程私有,记录当前线程执行的字节码位置
- 虚拟机栈:线程私有,存储方法调用的栈帧(局部变量、操作数栈等)
- 本地方法栈:线程私有,为Native方法服务
- 堆:线程共享,存放对象实例,是GC主要工作区域
- 方法区:线程共享,存储类信息、常量、静态变量等
JDK 1.8的重大变化是将方法区的实现从永久代(PermGen)改为元空间(MetaSpace),元空间使用本地内存,避免了永久代的内存溢出问题。
4.2 垃圾回收机制
垃圾回收(GC)是JVM自动管理内存的核心机制,它负责回收不再使用的对象所占用的内存空间。GC主要关注堆内存的回收,其工作过程可以分为以下几个步骤:
- 标记:识别哪些对象还在被引用(存活对象)
- 清除:回收未被标记的对象占用的空间
- 压缩(可选):整理内存,减少碎片
判断对象是否可回收的算法:
- 引用计数法:简单但无法解决循环引用问题
- 可达性分析法:从GC Roots出发,不可达的对象判定为可回收
GC Roots包括:
- 虚拟机栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
4.3 类加载机制
类加载是JVM将.class文件加载到内存,并对数据进行校验、转换解析和初始化的过程。类加载的全生命周期包括:
- 加载:查找并加载类的二进制数据
- 验证:确保类文件的正确性和安全性
- 准备:为静态变量分配内存并设置初始值
- 解析:将符号引用转换为直接引用
- 初始化:执行类构造器
()方法 - 使用:类的正常使用
- 卸载:从内存中移除类
类加载遵循双亲委派模型,即一个类加载器在尝试加载类时,首先会委托给父类加载器,只有在父类加载器无法加载时,才会尝试自己加载。这种机制保证了Java核心库的类型安全。
5. 多线程编程进阶
5.1 线程创建与生命周期
Java中创建线程主要有三种方式:
-
继承Thread类
java复制class MyThread extends Thread { public void run() { // 线程执行逻辑 } } new MyThread().start(); -
实现Runnable接口
java复制class MyRunnable implements Runnable { public void run() { // 线程执行逻辑 } } new Thread(new MyRunnable()).start(); -
使用Callable和Future
java复制Callable<String> callable = () -> "result"; ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = executor.submit(callable); String result = future.get(); // 获取异步结果
线程的生命周期包括:
- NEW:新建状态
- RUNNABLE:可运行状态
- BLOCKED:阻塞状态
- WAITING:等待状态
- TIMED_WAITING:计时等待
- TERMINATED:终止状态
5.2 线程同步与锁机制
在多线程环境下,共享资源的访问需要同步控制。Java提供了多种同步机制:
-
synchronized关键字
- 修饰实例方法:锁定当前实例
- 修饰静态方法:锁定当前类的Class对象
- 同步代码块:可指定锁对象
-
volatile关键字
- 保证变量的可见性
- 禁止指令重排序
- 但不保证原子性
-
Lock接口
java复制Lock lock = new ReentrantLock(); lock.lock(); try { // 临界区代码 } finally { lock.unlock(); } -
原子变量
java复制AtomicInteger counter = new AtomicInteger(0); counter.incrementAndGet(); // 原子操作
5.3 线程池最佳实践
线程池是管理线程的有效工具,它可以:
- 减少线程创建销毁的开销
- 控制并发线程数量
- 提供定时执行等功能
Java通过Executor框架提供线程池支持,常用线程池类型:
- FixedThreadPool:固定大小线程池
- CachedThreadPool:可缓存线程池
- ScheduledThreadPool:定时任务线程池
- SingleThreadExecutor:单线程池
创建线程池的正确方式:
java复制ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, // 空闲线程存活时间
TimeUnit.MILLISECONDS, // 时间单位
new LinkedBlockingQueue<Runnable>() // 工作队列
);
线程池使用注意事项:
- 合理设置线程池大小(CPU密集型 vs IO密集型)
- 使用有界队列避免内存溢出
- 正确处理任务抛出的异常
- 合理设置拒绝策略
6. 集合框架深度解析
6.1 ArrayList与LinkedList对比
ArrayList和LinkedList是List接口的两种主要实现,它们的区别主要体现在底层数据结构和操作性能上:
| 操作 | ArrayList | LinkedList |
|---|---|---|
| 随机访问 | O(1) | O(n) |
| 头部插入/删除 | O(n) | O(1) |
| 尾部插入/删除 | O(1)(摊销) | O(1) |
| 中间插入/删除 | O(n) | O(n)(需要先定位节点) |
| 内存占用 | 较小(仅存储实际数据) | 较大(需要存储前后节点引用) |
选择建议:
- 需要频繁随机访问:ArrayList
- 需要频繁在头部/中间插入删除:LinkedList
- 不确定时:通常ArrayList表现更好
6.2 HashMap实现原理
HashMap是Java中最常用的Map实现,它的核心实现原理包括:
-
数组+链表+红黑树结构
- JDK1.8前:数组+链表
- JDK1.8后:当链表长度>8时转换为红黑树
-
哈希函数
java复制static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } -
扩容机制
- 默认初始容量:16
- 负载因子:0.75
- 扩容阈值:容量×负载因子
- 扩容时重新哈希,容量变为2倍
-
线程安全性
- 非线程安全
- 多线程环境下可能产生死循环(JDK1.7及之前)
- 替代方案:ConcurrentHashMap
6.3 ConcurrentHashMap并发优化
ConcurrentHashMap是线程安全的HashMap实现,它的并发优化策略经历了多次演进:
JDK1.7实现:
- 分段锁(Segment)
- 默认16个段,理论上支持16个线程并发写
JDK1.8实现:
- 取消分段锁
- 使用CAS+synchronized实现更细粒度的锁
- 链表长度>8时转为红黑树
- 扩容时支持多线程协助迁移
关键优化点:
- 减小锁粒度(从段级别到桶级别)
- 使用CAS减少锁竞争
- 优化扩容机制
- 引入红黑树提升查询效率
7. JVM性能调优
7.1 内存溢出分析与解决
内存溢出(OOM)是Java应用中常见的问题,主要类型包括:
-
Java堆溢出
- 错误信息:java.lang.OutOfMemoryError: Java heap space
- 原因:对象数量超过堆容量
- 解决:增大堆大小(-Xmx)、优化对象使用
-
方法区溢出
- 错误信息:java.lang.OutOfMemoryError: PermGen space/Metaspace
- 原因:加载的类过多
- 解决:增大方法区大小(-XX:MaxPermSize/-XX:MaxMetaspaceSize)
-
栈溢出
- 错误信息:java.lang.StackOverflowError
- 原因:递归调用过深或栈帧过大
- 解决:增大栈大小(-Xss)、优化递归
-
直接内存溢出
- 错误信息:java.lang.OutOfMemoryError: Direct buffer memory
- 原因:NIO直接内存分配过多
- 解决:增大-XX:MaxDirectMemorySize
7.2 GC日志分析与优化
GC日志是诊断内存问题的重要工具,启用GC日志的参数:
code复制-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log
常见的GC问题模式:
- 频繁GC:应用分配速率过高或堆大小设置不合理
- 长时间GC暂停:老年代空间不足导致Full GC
- 内存泄漏:GC后可用内存持续减少
优化建议:
- 合理设置堆大小(-Xms, -Xmx)
- 选择合适的GC算法
- 避免大对象直接进入老年代
- 减少对象分配速率
7.3 JVM参数调优
常用JVM调优参数:
堆内存设置:
code复制-Xms512m # 初始堆大小
-Xmx2g # 最大堆大小
-Xmn256m # 新生代大小
方法区设置:
code复制-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=256m
GC算法选择:
code复制-XX:+UseG1GC # G1垃圾回收器
-XX:+UseConcMarkSweepGC # CMS垃圾回收器
-XX:+UseParallelGC # 并行垃圾回收器
其他重要参数:
code复制-XX:+HeapDumpOnOutOfMemoryError # OOM时生成堆转储
-XX:HeapDumpPath=/path/to/dump.hprof
-XX:+PrintFlagsFinal # 打印所有JVM参数
调优原则:
- 先满足功能需求,再考虑性能优化
- 基于实际监控数据调优,而非猜测
- 每次只调整一个参数,观察效果
- 记录每次变更及其影响
8. 面试常见问题精讲
8.1 红黑树特性与应用
红黑树是一种自平衡的二叉查找树,它在HashMap和TreeMap等集合中有着重要应用。红黑树必须满足以下性质:
- 每个节点要么是红色,要么是黑色
- 根节点是黑色
- 每个叶子节点(NIL节点)是黑色
- 红色节点的子节点必须是黑色(不能有两个连续的红色节点)
- 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点
这些性质保证了红黑树的关键特性:从根到最远叶子节点的路径不超过最近叶子节点路径的两倍,因此红黑树是近似平衡的。
在HashMap中,当链表长度超过8时,会将链表转换为红黑树,这样可以将最坏情况下的查找时间从O(n)降低到O(log n)。
8.2 synchronized实现原理
synchronized是Java中最基本的同步机制,它的实现经历了多次优化:
早期实现(重量级锁):
- 依赖操作系统的互斥锁(Mutex Lock)
- 线程切换需要从用户态切换到内核态
- 性能开销大
现代优化:
- 偏向锁:无竞争时消除同步开销
- 轻量级锁:使用CAS操作避免互斥量
- 自旋锁:减少线程切换的开销
- 锁消除:JIT编译器优化掉不必要的锁
- 锁粗化:合并多个相邻的同步块
synchronized的锁状态存储在对象头中,包含以下信息:
- 锁标志位
- 偏向锁标记
- 持有锁的线程ID
- 锁的计数器
8.3 ThreadLocal原理与内存泄漏
ThreadLocal提供了线程局部变量,每个线程都有自己独立的变量副本。它的核心实现原理:
- ThreadLocalMap:每个Thread对象内部维护一个ThreadLocalMap
- 弱引用键:ThreadLocalMap使用弱引用持有ThreadLocal对象
- 自动清理:get/set时会清理失效的Entry
潜在的内存泄漏问题:
- ThreadLocal对象被回收后,对应的Entry键为null,但值仍然存在
- 线程池中的线程长期存活会导致累积的内存泄漏
正确使用方式:
java复制try {
threadLocal.set(value);
// 使用threadLocal
} finally {
threadLocal.remove(); // 必须清理
}
9. 实战经验分享
9.1 性能优化案例
案例背景:一个电商平台的订单查询接口在高并发时响应时间变长,GC频繁。
分析过程:
- 通过JVisualVM监控发现频繁的Young GC和偶尔的Full GC
- 堆内存分析显示大量重复的查询条件对象
- 代码审查发现没有使用缓存,每次查询都创建新对象
解决方案:
- 引入对象池复用常用查询条件对象
- 增加本地缓存减少重复对象创建
- 调整JVM参数:增大新生代大小(-Xmn),设置合理的存活阈值(-XX:MaxTenuringThreshold)
效果:GC频率降低70%,接口响应时间减少50%
9.2 并发问题排查
问题现象:系统偶尔出现数据不一致,但没有明显错误日志。
排查步骤:
- 代码审查发现共享变量的非原子操作
- 使用Thread Dump分析线程状态
- 通过字节码分析发现volatile变量的不恰当使用
根本原因:复合操作(读-改-写)缺乏适当的同步,虽然使用了volatile保证可见性,但不能保证原子性。
修复方案:
- 使用AtomicInteger替代基本类型
- 对复合操作使用synchronized或Lock
- 增加适当的日志记录并发问题
9.3 JVM问题诊断技巧
-
内存泄漏诊断:
- 使用jmap生成堆转储文件
- 用MAT或VisualVM分析对象引用链
- 关注大对象和异常集合
-
CPU高负载诊断:
- top命令找出高CPU的Java进程
- top -Hp找出高CPU的线程
- jstack获取线程堆栈,定位热点代码
-
死锁检测:
- jstack自动检测死锁
- 分析线程等待的锁和持有的锁
- 使用Thread Dump可视化工具
-
GC问题诊断:
- 分析GC日志的时间分布
- 关注Full GC的频率和持续时间
- 检查内存分配模式
10. 面试准备建议
10.1 知识体系构建
-
分层学习:
- 基础层:Java语法、集合、IO
- 核心层:并发、JVM、设计模式
- 应用层:框架原理、分布式、性能优化
-
建立知识关联:
- 将知识点串联起来,如HashMap实现→哈希冲突解决→红黑树
- 理解技术演进的背景和原因
-
实践验证:
- 通过代码实验验证理论
- 参与开源项目或自己造轮子
10.2 常见问题应对策略
-
概念性问题:
- 先给出标准定义
- 补充自己的理解
- 结合实际应用场景
-
原理性问题:
- 从设计目的出发
- 讲解核心实现机制
- 分析优缺点
-
场景性问题:
- 明确问题边界
- 分步骤给出解决方案
- 讨论可能的优化方向
-
编码问题:
- 先确认需求
- 写出清晰可读的代码
- 考虑边界条件和异常处理
10.3 项目经验提炼
-
选择有深度的项目:
- 避免简单CRUD项目
- 突出技术难点和解决方案
-
STAR法则描述:
- Situation:项目背景
- Task:你的职责
- Action:采取的措施
- Result:取得的成果
-
准备技术细节:
- 架构设计决策
- 遇到的典型问题
- 性能优化点
- 团队协作经验
-
量化成果:
- 性能提升百分比
- 资源使用减少量
- 系统稳定性指标
11. 高级特性解析
11.1 Java模块系统
Java 9引入的模块系统(JPMS)是对传统JAR包机制的重大改进,主要特性包括:
-
模块化声明:通过module-info.java定义模块
java复制module com.example.myapp { requires java.base; requires transitive java.sql; exports com.example.myapp.api; } -
强封装性:
- 未导出的包对外不可见
- 打破了传统的反射访问能力
-
服务加载机制:
java复制
provides com.example.Service with com.example.ServiceImpl; uses com.example.Service;
模块系统的优势:
- 减少运行时依赖问题
- 提高安全性和可维护性
- 支持更小的运行时镜像
11.2 响应式编程
响应式编程是一种基于异步数据流的编程范式,Java中的实现主要有:
-
Reactive Streams规范:
- Publisher:数据发布者
- Subscriber:数据订阅者
- Subscription:订阅关系
- Processor:处理中间环节
-
Project Reactor:
- Mono:0-1个结果的异步序列
- Flux:0-N个结果的异步序列
java复制Flux.range(1, 10) .filter(n -> n % 2 == 0) .map(n -> n * n) .subscribe(System.out::println); -
Spring WebFlux:
- 非阻塞的Web框架
- 支持函数式路由
- 与Servlet API解耦
响应式编程适合高并发、低延迟的场景,但学习曲线较陡,调试困难。
11.3 记录类型与模式匹配
Java 14引入的record是一种新的类声明形式,用于简化不可变数据载体的定义:
java复制public record Point(int x, int y) {
// 编译器自动生成:
// - final字段
// - 构造方法
// - equals/hashCode/toString
}
Java 17引入的模式匹配特性进一步增强了语言表达能力:
-
instanceof模式匹配:
java复制if (obj instanceof String s) { // 可以直接使用s System.out.println(s.length()); } -
switch模式匹配:
java复制return switch (shape) { case Circle c -> "Circle with radius " + c.radius(); case Rectangle r -> "Rectangle"; default -> "Unknown shape"; };
这些新特性使Java代码更简洁、更安全,减少了样板代码。
12. 微服务相关技术
12.1 Spring Cloud核心组件
Spring Cloud为微服务架构提供了一整套解决方案:
-
服务发现:
- Eureka:服务注册与发现
- Consul:多数据中心支持
- Zookeeper:分布式协调服务
-
客户端负载均衡:
- Ribbon:客户端负载均衡器
- @LoadBalanced:RestTemplate集成
-
声明式REST客户端:
- Feign:基于接口的HTTP客户端
- 支持熔断和降级
-
API网关:
- Spring Cloud Gateway:基于WebFlux的高性能网关
- 路由、过滤、限流等功能
-
配置中心:
- Spring Cloud Config:集中式配置管理
- 支持Git、SVN等版本库
12.2 服务容错保护
微服务架构中,服务容错是关键保障:
-
Hystrix(已停更):
- 熔断机制
- 线程隔离
- 请求缓存
-
Resilience4j:
- 轻量级替代方案
- 熔断器(CircuitBreaker)
- 限流(RateLimiter)
- 重试(Retry)
-
Sentinel:
- 流量控制
- 熔断降级
- 系统自适应保护
容错策略配置示例:
java复制CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofMillis(1000))
.ringBufferSizeInHalfOpenState(2)
.ringBufferSizeInClosedState(2)
.build();
CircuitBreaker circuitBreaker = CircuitBreaker.of("backendService", config);
12.3 分布式事务解决方案
微服务环境下的分布式事务挑战:
-
2PC/XA:
- 传统两阶段提交
- 数据库层面支持
- 性能较差
-
TCC(Try-Confirm-Cancel):
- 业务层面补偿
- 需要实现三个接口
- 一致性较好
-
SAGA:
- 长事务解决方案
- 每个步骤有补偿操作
- 最终一致性
-
Seata:
- 开源的分布式事务解决方案
- 支持AT、TCC、SAGA模式
- 全局事务ID贯穿服务调用
Spring Cloud集成示例:
java复制@GlobalTransactional
public void purchase() {
// 1. 扣减库存
storageFeignClient.deduct();
// 2. 创建订单
orderFeignClient.create();
// 3. 扣减余额
accountFeignClient.debit();
}
13. 新版本特性解读
13.1 Java 8核心特性
Java 8是Java语言的重要里程碑,引入了许多革命性特性:
-
Lambda表达式:
java复制List<String> names = Arrays.asList("Alice", "Bob"); names.sort((a, b) -> a.compareTo(b)); -
Stream API:
java复制List<String> result = names.stream() .filter(s -> s.startsWith("A")) .map(String::toUpperCase) .collect(Collectors.toList()); -
Optional类:
java复制Optional<String> opt = Optional.ofNullable(getName()); String name = opt.orElse("default"); -
新的日期时间API:
java复制LocalDate today = LocalDate.now(); LocalDate tomorrow = today.plusDays(1); -
接口默认方法:
java复制interface Vehicle { default void print() { System.out.println("I'm a vehicle!"); } }
13.2 Java 11重要更新
Java 11是继Java 8后的下一个LTS版本,主要特性:
-
HTTP Client API(标准化):
java复制HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://example.com")) .build(); HttpResponse<String> response = client.send(request, BodyHandlers.ofString()); -
局部变量类型推断增强:
java复制var list = new ArrayList<String>(); // ArrayList<String> -
字符串API增强:
java复制" ".isBlank(); // true "Java".repeat(3); // "JavaJavaJava" -
单文件源代码程序:
bash复制
java HelloWorld.java -
移除项:
- Java EE和CORBA模块
- Nashorn JavaScript引擎
13.3 Java 17新特性
Java 17是当前最新的LTS版本,值得关注的特性:
-
密封类(Sealed Classes):
java复制public sealed class Shape permits Circle, Square { // ... } -
模式匹配增强:
java复制if (obj instanceof String s && s.length() > 5) { System.out.println(s); } -
新的API:
- 伪随机数生成器接口
- 新的Apple Silicon支持
- 改进的序列化过滤
-
弃用和移除:
- 弃用Applet API
- 移除RMI激活机制
-
性能改进:
- 新的向量API(孵化中)
- 改进的垃圾回收器
14. 设计模式实践
14.1 常用设计模式解析
-
单例模式:
- 确保类只有一个实例
- 实现方式:饿汉式、懒汉式、双重检查锁、静态内部类、枚举
java复制public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } } -
工厂模式:
- 解耦对象的创建与使用
- 简单工厂、工厂方法、抽象工厂
java复制public interface ShapeFactory { Shape createShape(); } -
观察者模式:
- 对象间的一对多依赖关系
- Java内置实现:Observable和Observer
java复制public class Subject extends Observable { public void change() { setChanged(); notifyObservers(); } } -
策略模式:
- 定义算法族,分别封装
- 使算法可以互相替换
java复制public interface SortingStrategy { void sort(List<Integer> list); }
14.2 Spring中的设计模式
Spring框架大量使用了设计模式:
- 控制反转(IoC):工厂模式
- 依赖注入(DI):组合模式
- AOP代理:代理模式
- Bean作用域:单例模式、原型模式
- 事件机制:观察者模式
- JdbcTemplate:模板方法模式
- HandlerMapping:策略模式
- 适配器:适配器模式
理解这些模式有助于深入掌握Spring的工作原理。
14.3 设计模式面试要点
设计模式面试常见问题:
-
概念性问题:
- 设计模式的六大原则
- 创建型、结构型、行为型模式的区别
-
模式识别:
- 给定代码片段识别使用的模式
- 分析框架中特定功能的模式应用
-
场景设计:
- 针对特定需求选择合适的设计模式
- 比较不同模式的适用场景
-
优缺点分析:
- 特定模式的优点和局限性
- 模式滥用的风险
回答策略:
- 结合具体项目经验
- 展示对模式本质的理解
- 讨论模式的变体和组合使用
15. 性能优化专题
15.1 代码级优化技巧
-
集合使用优化:
- 预估集合大小,避免扩容
- 使用基本类型集合(如FastUtil)
- 避免在循环中调用size()
-
字符串处理:
- 使用StringBuilder拼接字符串
- 预编译正则表达式
- 利用String.intern()减少内存占用
-
对象池技术:
- 复用频繁创建销毁的对象
- Apache Commons Pool实现
- 注意池大小和清理策略
-
并发优化:
- 减小锁粒度
- 使用读写锁替代独占锁
- 考虑无锁数据结构
15.2 JVM层优化
-
内存分配优化:
- 合理设置新生代和老年代比例
- 避免大对象直接进入老年代
- 使用TLAB(Thread Local Allocation Buffer)
-
GC调优:
- 根据应用特点选择GC算法
- 调整GC触发阈值
- 监控GC日志分析瓶颈
-
JIT优化:
- 热点代码识别
- 方法内联
- 逃逸分析
-
Native内存优化:
- 直接内存管理
- JNI调用优化
- 文件映射内存使用
15.3 数据库性能优化
-
SQL优化:
- 避免SELECT *
- 合理使用索引
- 注意JOIN性能
-
连接池配置:
- 合理设置最大最小连接数