文章目录
-
- 一、异常处理的核心:为什么我们需要Exception和Error?
-
- 1.1 现实世界的异常场景
- 二、异常体系全景图
-
- 2.1 Throwable:所有异常和错误的超类
- 三、Exception详解:可恢复的程序异常
-
- 3.1 Checked Exception vs Unchecked Exception
- 3.2 异常处理最佳实践
- 四、Error详解:不可恢复的系统错误
-
- 4.1 JVM错误类型深度分析
- 4.2 Error处理策略
- 五、Exception vs Error:核心区别总结
- 六、现代Java异常处理的最佳实践
-
- 6.1 Java 8+的异常处理改进
- 七、性能考虑和最佳实践总结
-
- 7.1 异常处理的性能影响
- 八、总结
-
- **核心区别总结:**
- **关键要点:**
- **最佳实践:**
一、异常处理的核心:为什么我们需要Exception和Error?
在Java的世界里,异常处理是程序健壮性的基石。想象一下,你正在开发一个电商系统,用户下单时突然网络中断,或者支付过程中银行接口返回异常——这些"意外情况"正是异常处理机制要解决的核心问题。
1.1 现实世界的异常场景
| 用户输入错误格式的手机号 | IllegalArgumentException | 提示用户重新输入 |
| 读取不存在的配置文件 | FileNotFoundException | 使用默认配置或创建新文件 |
| 数据库连接突然中断 | SQLException | 重试连接或使用缓存数据 |
| 内存耗尽无法创建对象 | OutOfMemoryError | 清理缓存或优雅关闭应用 |
| 栈调用深度过大 | StackOverflowError | 优化递归算法或增加栈大小 |
二、异常体系全景图
让我们先通过一个完整的异常体系图来理解Java异常处理的层次结构:
#mermaid-svg-AF0WA1xjKtYlcRTQ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AF0WA1xjKtYlcRTQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AF0WA1xjKtYlcRTQ .error-icon{fill:#552222;}#mermaid-svg-AF0WA1xjKtYlcRTQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AF0WA1xjKtYlcRTQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AF0WA1xjKtYlcRTQ .marker.cross{stroke:#333333;}#mermaid-svg-AF0WA1xjKtYlcRTQ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AF0WA1xjKtYlcRTQ p{margin:0;}#mermaid-svg-AF0WA1xjKtYlcRTQ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-AF0WA1xjKtYlcRTQ .cluster-label text{fill:#333;}#mermaid-svg-AF0WA1xjKtYlcRTQ .cluster-label span{color:#333;}#mermaid-svg-AF0WA1xjKtYlcRTQ .cluster-label span p{background-color:transparent;}#mermaid-svg-AF0WA1xjKtYlcRTQ .label text,#mermaid-svg-AF0WA1xjKtYlcRTQ span{fill:#333;color:#333;}#mermaid-svg-AF0WA1xjKtYlcRTQ .node rect,#mermaid-svg-AF0WA1xjKtYlcRTQ .node circle,#mermaid-svg-AF0WA1xjKtYlcRTQ .node ellipse,#mermaid-svg-AF0WA1xjKtYlcRTQ .node polygon,#mermaid-svg-AF0WA1xjKtYlcRTQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AF0WA1xjKtYlcRTQ .rough-node .label text,#mermaid-svg-AF0WA1xjKtYlcRTQ .node .label text,#mermaid-svg-AF0WA1xjKtYlcRTQ .image-shape .label,#mermaid-svg-AF0WA1xjKtYlcRTQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-AF0WA1xjKtYlcRTQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AF0WA1xjKtYlcRTQ .rough-node .label,#mermaid-svg-AF0WA1xjKtYlcRTQ .node .label,#mermaid-svg-AF0WA1xjKtYlcRTQ .image-shape .label,#mermaid-svg-AF0WA1xjKtYlcRTQ .icon-shape .label{text-align:center;}#mermaid-svg-AF0WA1xjKtYlcRTQ .node.clickable{cursor:pointer;}#mermaid-svg-AF0WA1xjKtYlcRTQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AF0WA1xjKtYlcRTQ .arrowheadPath{fill:#333333;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AF0WA1xjKtYlcRTQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AF0WA1xjKtYlcRTQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AF0WA1xjKtYlcRTQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AF0WA1xjKtYlcRTQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AF0WA1xjKtYlcRTQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AF0WA1xjKtYlcRTQ .cluster text{fill:#333;}#mermaid-svg-AF0WA1xjKtYlcRTQ .cluster span{color:#333;}#mermaid-svg-AF0WA1xjKtYlcRTQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AF0WA1xjKtYlcRTQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AF0WA1xjKtYlcRTQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-AF0WA1xjKtYlcRTQ .icon-shape,#mermaid-svg-AF0WA1xjKtYlcRTQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AF0WA1xjKtYlcRTQ .icon-shape p,#mermaid-svg-AF0WA1xjKtYlcRTQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AF0WA1xjKtYlcRTQ .icon-shape rect,#mermaid-svg-AF0WA1xjKtYlcRTQ .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AF0WA1xjKtYlcRTQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AF0WA1xjKtYlcRTQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AF0WA1xjKtYlcRTQ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Throwable
Error
Exception
VirtualMachineError
LinkageError
IOError
OutOfMemoryError
StackOverflowError
InternalError
NoClassDefFoundError
IncompatibleClassChangeError
RuntimeException
Checked Exception
NullPointerException
ArrayIndexOutOfBoundsException
IllegalArgumentException
ArithmeticException
IOException
SQLException
ClassNotFoundException
FileNotFoundException
EOFException
2.1 Throwable:所有异常和错误的超类
/**
* Throwable类是所有异常和错误的根类
* 包含三个核心信息:错误消息、原因和栈轨迹
*/
public class ThrowableAnatomy {
public static void main(String[] args) {
try {
dangerousOperation();
} catch (Throwable t) {
// 1. 获取错误信息
System.out.println("Message: " + t.getMessage());
// 2. 获取原因(链式异常)
System.out.println("Cause: " + t.getCause());
// 3. 打印栈轨迹
System.out.println("Stack Trace:");
t.printStackTrace();
// 4. 获取栈轨迹元素(程序化访问)
StackTraceElement[] stackTrace = t.getStackTrace();
for (StackTraceElement element : stackTrace) {
System.out.printf(" at %s.%s(%s:%d)%n",
element.getClassName(),
element.getMethodName(),
element.getFileName(),
element.getLineNumber());
}
// 5. 获取抑制的异常(try-with-resources)
Throwable[] suppressed = t.getSuppressed();
if (suppressed.length > 0) {
System.out.println("Suppressed exceptions:");
for (Throwable suppressedEx : suppressed) {
System.out.println(" – " + suppressedEx);
}
}
}
}
private static void dangerousOperation() throws Throwable {
Throwable t = new Throwable("原始异常");
t.addSuppressed(new IOException("抑制异常1"));
t.addSuppressed(new SQLException("抑制异常2"));
throw t;
}
}
三、Exception详解:可恢复的程序异常
3.1 Checked Exception vs Unchecked Exception
import java.io.*;
import java.sql.*;
/**
* 检查型异常 vs 非检查型异常
* 关键区别:编译器是否强制要求处理
*/
public class ExceptionClassification {
// ========== 检查型异常(Checked Exception) ==========
// 编译器强制要求处理(try-catch或throws)
/**
* IOException – 典型的检查型异常
* 必须处理,否则编译不通过
*/
public void readFileChecked(String filename) throws IOException {
// 可能抛出FileNotFoundException(IOException的子类)
FileReader reader = new FileReader(filename);
try {
// 读取文件内容
System.out.println("读取文件: " + filename);
} finally {
reader.close();
}
}
/**
* SQLException – 另一个常见的检查型异常
*/
public void databaseOperation() throws SQLException {
// 模拟数据库操作异常
throw new SQLException("数据库连接失败");
}
// ========== 非检查型异常(Unchecked Exception) ==========
// 通常是编程错误,编译器不强制要求处理
/**
* NullPointerException – 最常见的运行时异常
*/
public void nullPointerExample() {
String str = null;
// 这行代码会在运行时抛出NullPointerException
System.out.println(str.length()); // 编译通过,运行时异常!
}
/**
* IllegalArgumentException – 参数非法异常
*/
public void setAge(int age) {
if (age < 0 || age > 150) {
// 运行时异常,编译器不强制处理
throw new IllegalArgumentException("年龄必须在0-150之间");
}
System.out.println("年龄设置为: " + age);
}
/**
* ArrayIndexOutOfBoundsException – 数组越界
*/
public void arrayAccess() {
int[] arr = new int[5];
System.out.println(arr[10]); // 运行时抛出异常
}
/**
* ArithmeticException – 算术异常
*/
public void divide(int a, int b) {
System.out.println(a / b); // b=0时抛出异常
}
// ========== 自定义异常 ==========
/**
* 自定义检查型异常
*/
static class BusinessException extends Exception {
private final String errorCode;
public BusinessException(String message, String errorCode) {
super(message);
this.errorCode = errorCode;
}
public String getErrorCode() {
return errorCode;
}
}
/**
* 自定义运行时异常
*/
static class ValidationException extends RuntimeException {
private final String fieldName;
public ValidationException(String message, String fieldName) {
super(message);
this.fieldName = fieldName;
}
public String getFieldName() {
return fieldName;
}
}
public static void main(String[] args) {
ExceptionClassification demo = new ExceptionClassification();
// 测试各种异常
try {
demo.setAge(–5); // 抛出IllegalArgumentException
} catch (IllegalArgumentException e) {
System.out.println("捕获到异常: " + e.getMessage());
}
// 处理检查型异常
try {
demo.readFileChecked("nonexistent.txt");
} catch (IOException e) {
System.out.println("文件操作异常: " + e.getMessage());
}
}
}
3.2 异常处理最佳实践
import java.util.*;
import java.util.logging.*;
/**
* 异常处理的最佳实践示例
*/
public class ExceptionBestPractices {
private static final Logger logger = Logger.getLogger(ExceptionBestPractices.class.getName());
/**
* 实践1:精确捕获异常,避免空的catch块
*/
public void preciseCatchExample() {
try {
// 可能抛出多种异常的代码
processData();
} catch (FileNotFoundException e) {
// 精确处理特定异常
logger.log(Level.WARNING, "文件不存在,使用默认配置", e);
useDefaultConfig();
} catch (IOException e) {
// 处理更通用的IO异常
logger.log(Level.SEVERE, "IO操作失败", e);
throw new RuntimeException("系统IO错误", e);
} catch (SQLException e) {
// 处理数据库异常
logger.log(Level.SEVERE, "数据库操作失败", e);
retryDatabaseOperation();
}
}
/**
* 实践2:使用try-with-resources自动管理资源
*/
public void readFileWithResources(String filename) {
// Java 7+ 自动关闭资源
try (FileReader reader = new FileReader(filename);
BufferedReader br = new BufferedReader(reader)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
// 资源会自动关闭,即使发生异常
logger.log(Level.SEVERE, "读取文件失败: " + filename, e);
}
// 不需要finally块来关闭资源!
}
/**
* 实践3:异常链 – 保留原始异常信息
*/
public void exceptionChaining() {
try {
lowLevelOperation();
} catch (LowLevelException e) {
// 创建高层异常,包含原始异常作为cause
HighLevelException highLevelEx = new HighLevelException(
"高层操作失败,原因: " + e.getMessage(),
e // 设置cause
);
// 添加上下文信息
highLevelEx.addContext("timestamp", new Date());
highLevelEx.addContext("user", "admin");
throw highLevelEx;
}
}
/**
* 实践4:防御性编程 – 提前检查
*/
public void processUserInput(String input, Integer count) {
// 提前检查,避免后续的运行时异常
Objects.requireNonNull(input, "输入不能为null");
if (count == null || count <= 0) {
throw new IllegalArgumentException("count必须大于0");
}
if (input.isEmpty()) {
throw new IllegalArgumentException("输入不能为空字符串");
}
// 安全的执行代码
for (int i = 0; i < count; i++) {
System.out.println(input);
}
}
/**
* 实践5:使用Optional避免NullPointerException
*/
public void optionalExample() {
// 传统方式 – 容易产生NPE
String traditionalResult = getResultTraditional();
if (traditionalResult != null) {
System.out.println(traditionalResult.length());
}
// 使用Optional – 更安全
Optional<String> optionalResult = getResultOptional();
optionalResult.ifPresent(result ->
System.out.println(result.length())
);
// 链式操作
int length = optionalResult
.map(String::length)
.orElse(0);
System.out.println("长度: " + length);
}
/**
* 实践6:异常转换 – 将检查型异常转换为非检查型
*/
public void uncheckedExceptionWrapper() {
// 有时需要将检查型异常包装为运行时异常
try {
throw new IOException("原始IO异常");
} catch (IOException e) {
// 包装为运行时异常,避免throws声明
throw new UncheckedIOException(e);
}
}
/**
* 实践7:异常处理的模板方法模式
*/
public <T> T executeWithRetry(Supplier<T> operation, int maxRetries) {
int attempts = 0;
while (true) {
try {
return operation.get();
} catch (RecoverableException e) {
attempts++;
if (attempts >= maxRetries) {
throw new MaxRetriesExceededException("超过最大重试次数", e);
}
// 指数退避策略
long delay = (long) Math.pow(2, attempts) * 1000;
logger.warning("操作失败,第" + attempts + "次重试,等待" + delay + "ms");
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new OperationInterruptedException("操作被中断", ie);
}
}
}
}
// 辅助类和方法
static class LowLevelException extends Exception {
public LowLevelException(String message) { super(message); }
}
static class HighLevelException extends RuntimeException {
private final Map<String, Object> context = new HashMap<>();
public HighLevelException(String message, Throwable cause) {
super(message, cause);
}
public void addContext(String key, Object value) {
context.put(key, value);
}
public Map<String, Object> getContext() {
return Collections.unmodifiableMap(context);
}
}
static class RecoverableException extends RuntimeException {}
static class MaxRetriesExceededException extends RuntimeException {
public MaxRetriesExceededException(String message, Throwable cause) {
super(message, cause);
}
}
static class OperationInterruptedException extends RuntimeException {
public OperationInterruptedException(String message, Throwable cause) {
super(message, cause);
}
}
private void processData() throws IOException, SQLException {
// 模拟操作
}
private void useDefaultConfig() {
// 使用默认配置
}
private void retryDatabaseOperation() {
// 重试数据库操作
}
private void lowLevelOperation() throws LowLevelException {
throw new LowLevelException("低层操作失败");
}
private String getResultTraditional() {
// 可能返回null
return Math.random() > 0.5 ? "结果" : null;
}
private Optional<String> getResultOptional() {
// 明确表示可能没有值
return Math.random() > 0.5 ? Optional.of("结果") : Optional.empty();
}
@FunctionalInterface
interface Supplier<T> {
T get() throws RecoverableException;
}
}
四、Error详解:不可恢复的系统错误
4.1 JVM错误类型深度分析
import java.lang.management.*;
import java.nio.ByteBuffer;
import java.util.*;
/**
* Error类型深度解析
* Error表示严重的系统问题,通常不应该被捕获
*/
public class ErrorAnalysis {
/**
* 1. OutOfMemoryError – 内存耗尽错误
* 这是最常见的Error类型之一
*/
public static void demonstrateOutOfMemoryError() {
System.out.println("=== 演示OutOfMemoryError ===");
// 获取内存管理MXBean
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
System.out.println("堆内存使用情况:");
MemoryUsage heapUsage = memoryMXBean.getHeapMemoryUsage();
System.out.printf(" 初始: %.2f MB%n", heapUsage.getInit() / 1024.0 / 1024.0);
System.out.printf(" 已使用: %.2f MB%n", heapUsage.getUsed() / 1024.0 / 1024.0);
System.out.printf(" 最大: %.2f MB%n", heapUsage.getMax() / 1024.0 / 1024.0);
try {
// 方法1:创建大数组导致堆内存溢出
System.out.println("\\n尝试通过大数组导致堆内存溢出…");
List<byte[]> memoryHog = new ArrayList<>();
while (true) {
// 每次分配10MB
memoryHog.add(new byte[10 * 1024 * 1024]);
System.out.println("已分配: " + (memoryHog.size() * 10) + "MB");
Thread.sleep(100); // 给GC一点时间
}
} catch (OutOfMemoryError e) {
System.out.println("捕获到OutOfMemoryError: " + e.getMessage());
System.out.println("错误类型: " + e.getClass().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 2. StackOverflowError – 栈溢出错误
* 通常由无限递归引起
*/
public static void demonstrateStackOverflowError() {
System.out.println("\\n=== 演示StackOverflowError ===");
try {
// 无限递归导致栈溢出
recursiveMethod(0);
} catch (StackOverflowError e) {
System.out.println("捕获到StackOverflowError");
System.out.println("栈深度: " + getStackTraceDepth(e));
// 获取线程信息
Thread currentThread = Thread.currentThread();
System.out.println("线程: " + currentThread.getName());
System.out.println("线程状态: " + currentThread.getState());
}
}
private static void recursiveMethod(int depth) {
// 每次递归调用都会在栈上创建一个栈帧
System.out.println("递归深度: " + depth);
// 添加一些局部变量增加栈帧大小
int a = depth * 2;
String str = "深度" + depth;
Object[] array = new Object[10];
// 递归调用(没有终止条件!)
recursiveMethod(depth + 1);
}
private static int getStackTraceDepth(Throwable t) {
return t.getStackTrace().length;
}
/**
* 3. NoClassDefFoundError vs ClassNotFoundException
* 重要区别:编译时 vs 运行时
*/
public static void demonstrateClassLoadingErrors() {
System.out.println("\\n=== 演示类加载错误 ===");
// ClassNotFoundException – 检查型异常
try {
System.out.println("尝试加载不存在的类…");
Class<?> clazz = Class.forName("com.example.NonExistentClass");
System.out.println("类加载成功: " + clazz.getName());
} catch (ClassNotFoundException e) {
System.out.println("捕获到ClassNotFoundException: " + e.getMessage());
System.out.println("这是检查型异常,必须在编译时处理");
}
// NoClassDefFoundError – 错误(运行时)
try {
System.out.println("\\n尝试使用编译时存在但运行时缺失的类…");
createMissingClassInstance();
} catch (NoClassDefFoundError e) {
System.out.println("捕获到NoClassDefFoundError: " + e.getMessage());
System.out.println("通常是因为:");
System.out.println(" 1. 类文件在编译后丢失");
System.out.println(" 2. 静态初始化失败");
System.out.println(" 3. 类路径配置错误");
}
}
/**
* 4. 直接内存溢出(堆外内存)
*/
public static void demonstrateDirectMemoryError() {
System.out.println("\\n=== 演示直接内存溢出 ===");
List<ByteBuffer> buffers = new ArrayList<>();
try {
while (true) {
// 分配直接内存(堆外内存)
ByteBuffer buffer = ByteBuffer.allocateDirect(10 * 1024 * 1024); // 10MB
buffers.add(buffer);
System.out.println("已分配直接内存: " + (buffers.size() * 10) + "MB");
// 给系统一点时间
Thread.sleep(50);
}
} catch (OutOfMemoryError e) {
System.out.println("直接内存溢出: " + e.getMessage());
System.out.println("可能原因: -XX:MaxDirectMemorySize设置过小");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 清理直接内存
buffers.clear();
System.gc(); // 建议GC回收直接内存
}
}
/**
* 5. 内存泄漏检测示例
*/
public static class MemoryLeakDemo {
static class LeakyClass {
private final byte[] data = new byte[1024 * 1024]; // 1MB
private final String id;
public LeakyClass(String id) {
this.id = id;
}
@Override
protected void finalize() throws Throwable {
System.out.println("LeakyClass " + id + " 被垃圾回收");
super.finalize();
}
}
// 静态集合持有引用,导致内存泄漏
private static final List<LeakyClass> LEAK_LIST = new ArrayList<>();
public static void createMemoryLeak() {
for (int i = 0; i < 100; i++) {
LEAK_LIST.add(new LeakyClass("对象-" + i));
System.out.println("创建对象: " + i);
if (i % 10 == 0) {
// 建议GC,但对象不会被回收(因为有强引用)
System.gc();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
}
/**
* 6. 监控和诊断工具示例
*/
public static void monitorAndDiagnose() {
System.out.println("\\n=== 内存监控和诊断 ===");
// 获取运行时信息
Runtime runtime = Runtime.getRuntime();
System.out.println("JVM内存信息:");
System.out.printf(" 总内存: %.2f MB%n", runtime.totalMemory() / 1024.0 / 1024.0);
System.out.printf(" 空闲内存: %.2f MB%n", runtime.freeMemory() / 1024.0 / 1024.0);
System.out.printf(" 最大内存: %.2f MB%n", runtime.maxMemory() / 1024.0 / 1024.0);
System.out.printf(" 可用处理器: %d%n", runtime.availableProcessors());
// 获取内存池信息
List<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean pool : pools) {
System.out.println("\\n内存池: " + pool.getName());
System.out.println(" 类型: " + pool.getType());
MemoryUsage usage = pool.getUsage();
System.out.printf(" 使用量: %.2f MB%n", usage.getUsed() / 1024.0 / 1024.0);
System.out.printf(" 提交量: %.2f MB%n", usage.getCommitted() / 1024.0 / 1024.0);
System.out.printf(" 最大值: %.2f MB%n", usage.getMax() / 1024.0 / 1024.0);
// 内存使用率告警
if (usage.getMax() > 0) {
double usagePercent = (double) usage.getUsed() / usage.getMax() * 100;
if (usagePercent > 80) {
System.out.printf(" 警告: 内存使用率 %.1f%%%n", usagePercent);
}
}
}
}
// 这个方法会触发NoClassDefFoundError(假设MissingClass在编译后删除)
private static void createMissingClassInstance() {
// 假设这个类编译时存在,但运行时.class文件被删除
// new MissingClass();
}
public static void main(String[] args) {
// 运行演示
demonstrateOutOfMemoryError();
demonstrateStackOverflowError();
demonstrateClassLoadingErrors();
demonstrateDirectMemoryError();
System.out.println("\\n=== 创建内存泄漏 ===");
MemoryLeakDemo.createMemoryLeak();
// 监控诊断
monitorAndDiagnose();
System.out.println("\\n=== 演示完成 ===");
}
}
4.2 Error处理策略
虽然Error通常不应该被捕获,但在某些特殊场景下,我们可以采取一些策略:
/**
* Error处理的高级策略
*/
public class ErrorHandlingStrategies {
/**
* 策略1:优雅关闭 – 在发生不可恢复错误时清理资源
*/
public static class GracefulShutdownHook {
private static volatile boolean shutdownInProgress = false;
static {
// 注册JVM关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("JVM关闭钩子执行中…");
shutdownInProgress = true;
cleanupResources();
}));
}
public static void handleFatalError(Error error) {
System.err.println("发生致命错误: " + error.getClass().getName());
error.printStackTrace();
if (!shutdownInProgress) {
// 记录错误到文件或监控系统
logErrorToFile(error);
// 尝试优雅关闭
cleanupResources();
// 退出程序
System.exit(1);
}
}
private static void cleanupResources() {
System.out.println("清理资源…");
// 关闭数据库连接
// 释放文件锁
// 保存状态到磁盘
// 通知监控系统
}
private static void logErrorToFile(Error error) {
// 实现错误日志记录
}
}
/**
* 策略2:内存监控和预防
*/
public static class MemoryMonitor {
private static final long MEMORY_THRESHOLD = Runtime.getRuntime().maxMemory() * 80 / 100;
private static final List<MemoryWarningListener> listeners = new ArrayList<>();
static {
// 启动内存监控线程
Thread monitorThread = new Thread(() -> {
while (true) {
checkMemoryUsage();
try {
Thread.sleep(5000); // 每5秒检查一次
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}, "Memory-Monitor-Thread");
monitorThread.setDaemon(true);
monitorThread.start();
}
public interface MemoryWarningListener {
void memoryUsageHigh(long usedMemory, long maxMemory);
void memoryCritical(long usedMemory, long maxMemory);
}
public static void addListener(MemoryWarningListener listener) {
listeners.add(listener);
}
private static void checkMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
long usedMemory = runtime.totalMemory() – runtime.freeMemory();
long maxMemory = runtime.maxMemory();
double usagePercent = (double) usedMemory / maxMemory * 100;
if (usagePercent > 90) {
System.err.printf("内存严重不足: %.1f%% 已使用%n", usagePercent);
for (MemoryWarningListener listener : listeners) {
listener.memoryCritical(usedMemory, maxMemory);
}
// 触发紧急GC
System.gc();
} else if (usagePercent > 80) {
System.out.printf("内存使用率高: %.1f%% 已使用%n", usagePercent);
for (MemoryWarningListener listener : listeners) {
listener.memoryUsageHigh(usedMemory, maxMemory);
}
}
}
public static void preventiveCleanup() {
System.out.println("执行预防性清理…");
// 清理缓存
// 关闭闲置连接
// 释放临时资源
}
}
/**
* 策略3:线程隔离 – 防止一个线程的错误影响整个应用
*/
public static class IsolatedThreadExecutor {
private final ExecutorService executor;
private final Thread.UncaughtExceptionHandler errorHandler;
public IsolatedThreadExecutor(int poolSize) {
this.errorHandler = (thread, throwable) -> {
if (throwable instanceof Error) {
System.err.println("线程 " + thread.getName() + " 发生致命错误");
// 只关闭这个线程,不影响其他线程
thread.interrupt();
}
};
this.executor = Executors.newFixedThreadPool(poolSize, r -> {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(errorHandler);
t.setDaemon(true); // 设置为守护线程
return t;
});
}
public <T> Future<T> submit(Callable<T> task) {
return executor.submit(() -> {
try {
return task.call();
} catch (Throwable t) {
if (t instanceof Error) {
// 记录错误但不要传播
logError((Error) t);
throw new ExecutionException("任务执行失败", t);
}
throw t;
}
});
}
private void logError(Error error) {
// 错误记录逻辑
}
public void shutdown() {
executor.shutdownNow();
}
}
/**
* 策略4:防御性编程 – 防止StackOverflowError
*/
public static class StackSafeRecursion {
// 传统递归 – 可能栈溢出
public static int factorialTraditional(int n) {
if (n <= 1) return 1;
return n * factorialTraditional(n – 1);
}
// 尾递归优化(Java不支持尾递归优化,但我们可以模拟)
public static int factorialTailRecursive(int n) {
return factorialHelper(n, 1);
}
private static int factorialHelper(int n, int accumulator) {
if (n <= 1) return accumulator;
return factorialHelper(n – 1, n * accumulator);
}
// 使用循环避免递归
public static int factorialIterative(int n) {
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
// 安全递归 – 检查栈深度
public static int factorialSafe(int n) {
// 检查递归深度
if (Thread.currentThread().getStackTrace().length > 1000) {
throw new RuntimeException("递归深度过大,可能栈溢出");
}
if (n <= 1) return 1;
return n * factorialSafe(n – 1);
}
// 使用备忘录模式减少递归调用
private static final Map<Integer, Integer> memo = new ConcurrentHashMap<>();
public static int factorialMemoized(int n) {
if (n <= 1) return 1;
// 检查是否已经计算过
if (memo.containsKey(n)) {
return memo.get(n);
}
// 计算并存储结果
int result = n * factorialMemoized(n – 1);
memo.put(n, result);
return result;
}
}
}
五、Exception vs Error:核心区别总结
让我们通过一个详细的对比表格来总结两者的核心区别:
/**
* Exception和Error的全面对比
*/
public class ExceptionVsErrorComparison {
/**
* 对比维度表格
*/
public static void showComparisonTable() {
System.out.println("========== Exception vs Error 全面对比 ==========\\n");
String[][] comparisonData = {
// 维度, Exception, Error
{"设计目的", "程序可以处理的异常情况", "JVM或系统级别的严重问题"},
{"是否应该捕获", "应该被捕获和处理", "通常不应该捕获(特殊场景除外)"},
{"是否可恢复", "通常可恢复", "通常不可恢复"},
{"继承关系", "extends Throwable", "extends Throwable"},
{"检查类型", "分为Checked和Unchecked", "都是Unchecked"},
{"常见示例",
"IOException, SQLException, NullPointerException",
"OutOfMemoryError, StackOverflowError, NoClassDefFoundError"},
{"处理策略",
"try-catch-finally, throws声明, 优雅降级",
"记录日志, 优雅关闭, 资源清理"},
{"对程序影响",
"可能导致单个操作失败,但程序可以继续运行",
"通常导致JVM或程序崩溃"},
{"产生原因",
"业务逻辑错误、用户输入错误、外部依赖问题",
"内存不足、系统资源耗尽、JVM内部错误"},
{"最佳实践",
"精确捕获、异常链、防御性编程",
"监控预防、资源隔离、优雅关闭"}
};
// 打印对比表格
System.out.println("| 对比维度 | Exception | Error |");
System.out.println("|———-|———–|——-|");
for (String[] row : comparisonData) {
System.out.printf("| %-10s | %-20s | %-30s |%n",
row[0], formatCell(row[1]), formatCell(row[2]));
}
}
private static String formatCell(String text) {
// 简化显示,实际可以更复杂
return text.length() > 30 ? text.substring(0, 27) + "…" : text;
}
/**
* 实战场景对比
*/
public static class PracticalScenarios {
/**
* 场景1:文件读取
*/
public void readFileScenario(String filePath) {
// Exception场景 – 可恢复
try {
Files.readAllLines(Paths.get(filePath));
} catch (IOException e) {
// 可恢复:尝试其他文件或使用默认值
System.out.println("文件读取失败,使用默认配置");
useDefaultConfiguration();
}
// Error场景 – 不可恢复(但可以优雅处理)
try {
byte[] hugeFile = new byte[Integer.MAX_VALUE]; // 可能导致OOM
} catch (OutOfMemoryError e) {
// 不可恢复:记录日志并优雅关闭
System.err.println("内存不足,无法读取文件");
GracefulShutdownHook.handleFatalError(e);
}
}
/**
* 场景2:数据库操作
*/
public void databaseScenario() {
// Exception场景
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test")) {
// 执行数据库操作
} catch (SQLException e) {
// 可恢复:重试或使用缓存
if (shouldRetry(e)) {
retryDatabaseOperation();
} else {
useCachedData();
}
}
// Error场景
try {
// 假设数据库驱动有bug导致JVM内部错误
someNativeDatabaseOperation();
} catch (InternalError e) {
// JVM内部错误,不可恢复
System.err.println("JVM内部错误,可能是数据库驱动bug");
reportBugToVendor(e);
GracefulShutdownHook.handleFatalError(e);
}
}
/**
* 场景3:递归计算
*/
public void recursionScenario(int n) {
// Exception风格处理
try {
int result = calculateRecursive(n);
System.out.println("结果: " + result);
} catch (IllegalArgumentException e) {
System.out.println("参数错误: " + e.getMessage());
}
// Error风险处理
try {
int result = deepRecursion(n);
System.out.println("深度递归结果: " + result);
} catch (StackOverflowError e) {
// 栈溢出,需要改变算法
System.out.println("递归深度过大,使用迭代算法");
int result = calculateIterative(n);
System.out.println("迭代结果: " + result);
}
}
// 辅助方法
private void useDefaultConfiguration() {
// 使用默认配置
}
private boolean shouldRetry(SQLException e) {
// 判断是否应该重试
return e.getErrorCode() == 1205; // 死锁错误码示例
}
private void retryDatabaseOperation() {
// 重试数据库操作
}
private void useCachedData() {
// 使用缓存数据
}
private void someNativeDatabaseOperation() {
// 模拟原生操作
}
private void reportBugToVendor(InternalError e) {
// 报告bug给供应商
}
private int calculateRecursive(int n) {
if (n < 0) {
throw new IllegalArgumentException("n不能为负数");
}
if (n <= 1) return 1;
return n * calculateRecursive(n – 1);
}
private int deepRecursion(int n) {
if (n <= 0) return 0;
return deepRecursion(n – 1) + deepRecursion(n – 2); // 指数级递归
}
private int calculateIterative(int n) {
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
}
/**
* 决策流程图:何时使用Exception vs Error
*/
public static void decisionFlowchart() {
System.out.println("\\n========== 异常处理决策流程图 ==========\\n");
System.out.println("开始处理问题");
System.out.println("↓");
System.out.println("判断:问题是程序逻辑错误还是系统资源问题?");
System.out.println("├── 程序逻辑错误 → 使用Exception");
System.out.println("│ ├── 调用者应该处理 → Checked Exception");
System.out.println("│ └── 调用者无法合理处理 → Unchecked Exception");
System.out.println("↓");
System.out.println("└── 系统资源问题 → 判断是否可恢复");
System.out.println(" ├── 可恢复(如临时性错误) → 使用Exception");
System.out.println(" └── 不可恢复(如内存耗尽) → 使用Error");
System.out.println("↓");
System.out.println("处理策略:");
System.out.println("├── Exception → try-catch、重试、降级");
System.out.println("└── Error → 记录日志、清理资源、优雅关闭");
}
public static void main(String[] args) {
// 显示对比表格
showComparisonTable();
// 显示决策流程图
decisionFlowchart();
// 演示实战场景
PracticalScenarios scenarios = new PracticalScenarios();
System.out.println("\\n========== 实战场景演示 ==========");
// 注意:以下演示可能需要调整参数才能看到效果
System.out.println("\\n1. 文件读取场景:");
scenarios.readFileScenario("test.txt");
System.out.println("\\n2. 递归计算场景:");
scenarios.recursionScenario(10000);
}
}
六、现代Java异常处理的最佳实践
6.1 Java 8+的异常处理改进
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
/**
* Java 8+ 异常处理新特性
*/
public class ModernExceptionHandling {
/**
* 1. try-with-resources 增强(Java 9+)
*/
public void enhancedTryWithResources() throws IOException {
// Java 7: 需要在try中声明资源
try (FileReader reader = new FileReader("file.txt")) {
// 使用reader
}
// Java 9+: 可以在外部声明资源
FileReader reader = new FileReader("file.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
try (reader; bufferedReader) { // 多个资源,自动关闭
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
}
// reader和bufferedReader都会自动关闭
}
/**
* 2. Optional 与异常处理结合
*/
public Optional<String> safeReadFirstLine(Path file) {
try {
return Optional.of(Files.readAllLines(file).get(0));
} catch (IOException | IndexOutOfBoundsException e) {
// 文件不存在或为空文件
return Optional.empty();
}
}
public void processFileWithOptional(Path file) {
safeReadFirstLine(file)
.map(String::toUpperCase)
.ifPresentOrElse(
line -> System.out.println("第一行: " + line),
() -> System.out.println("文件为空或不存在")
);
}
/**
* 3. Stream API 中的异常处理
*/
public List<String> readAllFiles(List<Path> files) {
return files.stream()
.map(this::safeReadFile) // 处理可能抛异常的操作
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}
private Optional<String> safeReadFile(Path file) {
try {
return Optional.of(Files.readString(file));
} catch (IOException e) {
System.err.println("无法读取文件: " + file);
return Optional.empty();
}
}
/**
* 4. CompletableFuture 异常处理
*/
public CompletableFuture<String> asyncOperationWithExceptionHandling() {
return CompletableFuture.supplyAsync(() -> {
// 可能抛出异常的操作
if (Math.random() > 0.5) {
throw new RuntimeException("随机失败");
}
return "成功结果";
})
.exceptionally(throwable -> {
// 异常处理:提供默认值
System.err.println("操作失败: " + throwable.getMessage());
return "默认值";
})
.thenApply(result -> {
// 后续处理
return "处理后的: " + result;
});
}
/**
* 5. 函数式异常处理包装器
*/
@FunctionalInterface
public interface ThrowingFunction<T, R, E extends Exception> {
R apply(T t) throws E;
}
public static <T, R, E extends Exception>
Function<T, Optional<R>> wrap(ThrowingFunction<T, R, E> throwingFunction) {
return t -> {
try {
return Optional.of(throwingFunction.apply(t));
} catch (Exception e) {
return Optional.empty();
}
};
}
public void functionalExceptionHandling() {
List<String> filePaths = Arrays.asList("file1.txt", "file2.txt");
List<String> contents = filePaths.stream()
.map(wrap(path -> Files.readString(Paths.get(path))))
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
System.out.println("成功读取的内容: " + contents.size());
}
/**
* 6. 模式匹配(Java 17预览特性)
*/
public void patternMatchingExceptionHandling(Object obj) {
// 传统方式
if (obj instanceof IOException) {
IOException ioEx = (IOException) obj;
System.out.println("IO异常: " + ioEx.getMessage());
} else if (obj instanceof SQLException) {
SQLException sqlEx = (SQLException) obj;
System.out.println("SQL异常,错误码: " + sqlEx.getErrorCode());
}
// Java 17+ 模式匹配
if (obj instanceof IOException ioEx) {
System.out.println("IO异常: " + ioEx.getMessage());
} else if (obj instanceof SQLException sqlEx) {
System.out.println("SQL异常,错误码: " + sqlEx.getErrorCode());
}
}
/**
* 7. 记录异常(Java 16+ Record类)
*/
public record ErrorRecord(
String errorCode,
String message,
Instant timestamp,
String userId,
Map<String, String> context
) {
public static ErrorRecord fromException(Exception e, String userId) {
return new ErrorRecord(
e.getClass().getSimpleName(),
e.getMessage(),
Instant.now(),
userId,
Map.of("thread", Thread.currentThread().getName())
);
}
}
public void recordExceptionExample() {
try {
throw new IOException("文件操作失败");
} catch (IOException e) {
ErrorRecord errorRecord = ErrorRecord.fromException(e, "user123");
System.out.println("记录异常: " + errorRecord);
// 可以序列化到JSON或数据库
}
}
/**
* 8. 密封类(Sealed Classes)定义异常层次
*/
public sealed interface DataAccessException
permits DatabaseException, NetworkException, ValidationException {
String getErrorCode();
String getUserMessage();
}
public static final class DatabaseException extends RuntimeException
implements DataAccessException {
private final String sqlState;
public DatabaseException(String message, String sqlState) {
super(message);
this.sqlState = sqlState;
}
@Override
public String getErrorCode() {
return "DB-" + sqlState;
}
@Override
public String getUserMessage() {
return "数据库操作失败,请稍后重试";
}
}
public static final class NetworkException extends RuntimeException
implements DataAccessException {
private final int httpStatus;
public NetworkException(String message, int httpStatus) {
super(message);
this.httpStatus = httpStatus;
}
@Override
public String getErrorCode() {
return "NET-" + httpStatus;
}
@Override
public String getUserMessage() {
return "网络连接异常,请检查网络设置";
}
}
public static final class ValidationException extends RuntimeException
implements DataAccessException {
private final String fieldName;
public ValidationException(String message, String fieldName) {
super(message);
this.fieldName = fieldName;
}
@Override
public String getErrorCode() {
return "VAL-" + fieldName.toUpperCase();
}
@Override
public String getUserMessage() {
return "输入验证失败,请检查" + fieldName + "字段";
}
}
public void handleDataAccessException(DataAccessException e) {
// 使用密封类,可以穷举所有情况
System.out.println("错误代码: " + e.getErrorCode());
System.out.println("用户提示: " + e.getUserMessage());
// 更精细的处理
switch (e) {
case DatabaseException dbEx ->
System.out.println("SQL状态: " + ((DatabaseException) e).sqlState);
case NetworkException netEx ->
System.out.println("HTTP状态: " + ((NetworkException) e).httpStatus);
case ValidationException valEx ->
System.out.println("字段名: " + ((ValidationException) e).fieldName);
}
}
}
/**
* 全局异常处理器示例
*/
public class GlobalExceptionHandler {
/**
* 全局未捕获异常处理器
*/
public static class GlobalUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private final Logger logger = Logger.getLogger(GlobalUncaughtExceptionHandler.class.getName());
private final ErrorReportingService errorReporter;
public GlobalUncaughtExceptionHandler() {
this.errorReporter = new ErrorReportingService();
// 设置为所有线程的默认未捕获异常处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
// 记录错误
logger.log(Level.SEVERE,
String.format("线程 %s 发生未捕获异常", t.getName()),
e);
// 分类处理
if (e instanceof Error) {
handleError((Error) e, t);
} else if (e instanceof Exception) {
handleException((Exception) e, t);
}
// 报告到监控系统
errorReporter.report(e, t);
// 对于非主线程,可以选择重启线程
if (t != Thread.currentThread()) {
restartThreadIfNeeded(t, e);
}
}
private void handleError(Error error, Thread thread) {
// Error处理策略
if (error instanceof OutOfMemoryError) {
logger.severe("内存不足错误,尝试清理内存");
System.gc();
// 通知内存监控系统
MemoryMonitor.preventiveCleanup();
} else if (error instanceof StackOverflowError) {
logger.warning("栈溢出错误,线程: " + thread.getName());
} else if (error instanceof NoClassDefFoundError) {
logger.severe("类定义未找到: " + error.getMessage());
}
}
private void handleException(Exception exception, Thread thread) {
// Exception处理策略
if (exception instanceof IOException) {
logger.warning("IO异常,可能临时性问题");
} else if (exception instanceof RuntimeException) {
logger.warning("运行时异常,可能编程错误");
// 对于某些RuntimeException可以重试
if (isRecoverable(exception)) {
logger.info("可恢复异常,将重试操作");
}
}
}
private boolean isRecoverable(Throwable t) {
// 判断是否可恢复
return t instanceof IOException ||
t instanceof SQLException ||
(t instanceof RuntimeException &&
t.getMessage() != null &&
t.getMessage().contains("timeout"));
}
private void restartThreadIfNeeded(Thread thread, Throwable cause) {
// 根据需要重启线程
if (thread.getName().startsWith("Worker-")) {
logger.info("重启工作线程: " + thread.getName());
// 创建新线程执行相同任务
new Thread(thread.getThreadGroup(),
thread.getRunnable(),
thread.getName() + "-restarted").start();
}
}
}
/**
* 错误报告服务
*/
static class ErrorReportingService {
public void report(Throwable error, Thread thread) {
// 实现错误报告逻辑
System.err.println("报告错误到监控系统:");
System.err.println(" 线程: " + thread.getName());
System.err.println(" 错误: " + error.getClass().getName());
System.err.println(" 消息: " + error.getMessage());
// 可以发送到:
// 1. 日志文件
// 2. 监控系统(如Prometheus)
// 3. 通知系统(邮件、Slack等)
// 4. 错误追踪系统(Sentry、Bugsnag)
}
}
/**
* Spring风格的全局异常处理器
*/
@RestControllerAdvice
public static class RestGlobalExceptionHandler {
private final Logger logger = Logger.getLogger(RestGlobalExceptionHandler.class.getName());
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e, HttpServletRequest request) {
// 记录日志
logger.log(Level.WARNING, "处理请求异常: " + request.getRequestURI(), e);
// 构建错误响应
ErrorResponse errorResponse = new ErrorResponse(
"INTERNAL_ERROR",
"服务器内部错误",
request.getRequestURI(),
Instant.now()
);
// 根据异常类型设置不同的HTTP状态码
HttpStatus status = determineHttpStatus(e);
return ResponseEntity
.status(status)
.body(errorResponse);
}
@ExceptionHandler(Error.class)
public ResponseEntity<ErrorResponse> handleError(Error error, HttpServletRequest request) {
// Error处理:记录并返回500错误
logger.log(Level.SEVERE, "JVM错误: " + error.getMessage(), error);
ErrorResponse errorResponse = new ErrorResponse(
"SYSTEM_ERROR",
"系统错误,请稍后重试",
request.getRequestURI(),
Instant.now()
);
// 触发优雅关闭
GracefulShutdownHook.handleFatalError(error);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(errorResponse);
}
private HttpStatus determineHttpStatus(Throwable t) {
if (t instanceof IllegalArgumentException) {
return HttpStatus.BAD_REQUEST;
} else if (t instanceof ResourceNotFoundException) {
return HttpStatus.NOT_FOUND;
} else if (t instanceof AccessDeniedException) {
return HttpStatus.FORBIDDEN;
} else if (t instanceof AuthenticationException) {
return HttpStatus.UNAUTHORIZED;
} else {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}
static class ErrorResponse {
private final String code;
private final String message;
private final String path;
private final Instant timestamp;
// 构造函数、getter等
}
// 自定义异常类
static class ResourceNotFoundException extends RuntimeException {}
}
}
七、性能考虑和最佳实践总结
7.1 异常处理的性能影响
/**
* 异常处理性能分析
*/
public class ExceptionPerformanceAnalysis {
private static final int ITERATIONS = 1000000;
/**
* 基准测试:正常返回 vs 异常返回
*/
public static void benchmark() {
System.out.println("=== 异常处理性能基准测试 ===\\n");
// 测试1:正常返回
long start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
normalReturn(i);
}
long normalTime = System.nanoTime() – start;
// 测试2:使用异常控制流程(反模式!)
start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
try {
exceptionAsControlFlow(i);
} catch (Exception e) {
// 忽略
}
}
long exceptionTime = System.nanoTime() – start;
// 测试3:预先检查
start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
preCheck(i);
}
long preCheckTime = System.nanoTime() – start;
System.out.println("测试结果(" + ITERATIONS + "次迭代):");
System.out.printf("正常返回: %,d ns (基准)%n", normalTime);
System.out.printf("异常控制流程: %,d ns (慢 %.1fx)%n",
exceptionTime, (double)exceptionTime / normalTime);
System.out.printf("预先检查: %,d ns (慢 %.1fx)%n",
preCheckTime, (double)preCheckTime / normalTime);
System.out.println("\\n性能建议:");
System.out.println("1. 不要使用异常作为控制流程");
System.out.println("2. 在可能的情况下进行预先检查");
System.out.println("3. 保持栈轨迹简短(异常构造时记录栈轨迹)");
System.out.println("4. 重用异常对象(如果需要频繁抛出相同异常)");
}
private static int normalReturn(int i) {
return i * 2;
}
private static int exceptionAsControlFlow(int i) throws Exception {
if (i < 0) {
throw new Exception("负数");
}
return i * 2;
}
private static int preCheck(int i) {
if (i < 0) {
return –1; // 使用特殊值而不是异常
}
return i * 2;
}
/**
* 优化技巧:重用异常对象
*/
public static class OptimizedExceptions {
// 对于频繁抛出的相同异常,可以重用
private static final IllegalArgumentException REUSED_EXCEPTION =
new IllegalArgumentException("参数错误");
public static void validateWithReusedException(Object param) {
if (param == null) {
// 注意:重用异常会共享栈轨迹,可能不是期望的行为
throw REUSED_EXCEPTION;
}
}
public static void validateWithNewException(Object param) {
if (param == null) {
throw new IllegalArgumentException("参数错误");
}
}
}
/**
* 栈轨迹深度的影响
*/
public static void stackTraceDepthImpact() {
System.out.println("\\n=== 栈轨迹深度对性能的影响 ===\\n");
// 深度调用链
long start = System.nanoTime();
try {
deepCallChain(10);
} catch (Exception e) {
// 捕获但不使用栈轨迹
}
long timeWithoutStackTrace = System.nanoTime() – start;
start = System.nanoTime();
try {
deepCallChain(10);
} catch (Exception e) {
// 捕获并使用栈轨迹
StackTraceElement[] stackTrace = e.getStackTrace();
}
long timeWithStackTrace = System.nanoTime() – start;
System.out.println("栈轨迹访问性能:");
System.out.printf("不访问栈轨迹: %,d ns%n", timeWithoutStackTrace);
System.out.printf("访问栈轨迹: %,d ns (慢 %.1fx)%n",
timeWithStackTrace, (double)timeWithStackTrace / timeWithoutStackTrace);
}
private static void deepCallChain(int depth) throws Exception {
if (depth <= 0) {
throw new Exception("底层异常");
}
deepCallChain(depth – 1);
}
}
/**
* 异常处理最佳实践总结
*/
public class ExceptionBestPracticesSummary {
/**
* DOs – 应该做的事
*/
public static class Dos {
// 1. 使用具体的异常类型
public void useSpecificException() {
try {
readConfigFile();
} catch (FileNotFoundException e) { // 具体异常
// 处理文件不存在
} catch (IOException e) { // 更通用的异常
// 处理其他IO问题
}
}
// 2. 提供有意义的错误信息
public void validateInput(String input) {
if (input == null || input.trim().isEmpty()) {
// 好:提供具体信息
throw new IllegalArgumentException("输入不能为空或空白");
// 不好:throw new IllegalArgumentException();
}
}
// 3. 保持异常链
public void preserveExceptionChain() {
try {
lowLevelOperation();
} catch (LowLevelException e) {
// 保持原始异常作为cause
throw new HighLevelException("高层操作失败", e);
}
}
// 4. 清理资源
public void cleanupResources() {
Connection conn = null;
try {
conn = getConnection();
// 使用连接
} catch (SQLException e) {
// 处理异常
} finally {
// 确保资源被清理
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// 记录但不要覆盖原始异常
log.warn("关闭连接失败", e);
}
}
}
}
// 5. 记录异常
public void logExceptions(Exception e) {
// 记录完整信息
log.error("操作失败: {}", e.getMessage(), e);
// 包含上下文信息
log.error("用户: {}, 操作: {}, 错误: {}",
getCurrentUser(), getCurrentOperation(), e.toString());
}
// 辅助方法
private void readConfigFile() throws IOException {
// 读取配置文件
}
private void lowLevelOperation() throws LowLevelException {
// 低层操作
}
private Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost/test");
}
// 日志和上下文
private Logger log = Logger.getLogger(Dos.class.getName());
private String getCurrentUser() { return "user123"; }
private String getCurrentOperation() { return "processData"; }
}
/**
* DON'Ts – 不应该做的事
*/
public static class Donts {
// 1. 不要捕获Throwable(除非你知道在做什么)
public void dontCatchThrowable() {
try {
dangerousOperation();
} catch (Exception e) { // 好:只捕获Exception
// 处理异常
}
// catch (Throwable t) { // 不好:可能捕获Error
// // 可能导致程序在不健康状态下继续运行
// }
}
// 2. 不要忽略异常
public void dontIgnoreExceptions() {
try {
processData();
} catch (IOException e) {
// 不好:空的catch块
// 应该至少记录日志
log.error("处理数据失败", e);
}
}
// 3. 不要使用异常控制流程
public void dontUseExceptionsForControlFlow(int[] array, int index) {
// 不好:使用异常检查数组边界
// try {
// return array[index];
// } catch (ArrayIndexOutOfBoundsException e) {
// return -1;
// }
// 好:预先检查
if (index >= 0 && index < array.length) {
return array[index];
}
return –1;
}
// 4. 不要暴露实现细节
public void dontExposeImplementationDetails() {
try {
databaseOperation();
} catch (SQLException e) {
// 不好:暴露SQL细节给用户
// throw new RuntimeException("SQL错误: " + e.getMessage());
// 好:提供用户友好的消息
throw new RuntimeException("数据库操作失败,请稍后重试");
}
}
// 5. 不要过度使用检查型异常
public void dontOveruseCheckedExceptions() {
// 对于调用者无法合理处理的情况,使用运行时异常
// 而不是强制每个调用者都处理检查型异常
}
// 辅助方法
private void dangerousOperation() {
// 危险操作
}
private void processData() throws IOException {
// 处理数据
}
private void databaseOperation() throws SQLException {
// 数据库操作
}
private Logger log = Logger.getLogger(Donts.class.getName());
}
/**
* 领域特定异常处理模式
*/
public static class DomainSpecificPatterns {
// 模式1:重试模式
public <T> T retry(Supplier<T> operation, int maxRetries) {
int attempts = 0;
while (true) {
try {
return operation.get();
} catch (RecoverableException e) {
if (++attempts >= maxRetries) {
throw new MaxRetriesExceededException("超过最大重试次数", e);
}
waitBeforeRetry(attempts);
}
}
}
// 模式2:熔断器模式
public static class CircuitBreaker {
private enum State { CLOSED, OPEN, HALF_OPEN }
private State state = State.CLOSED;
private int failureCount = 0;
private long lastFailureTime = 0;
public <T> T execute(Supplier<T> operation) {
if (state == State.OPEN) {
if (System.currentTimeMillis() – lastFailureTime > 5000) {
state = State.HALF_OPEN;
} else {
throw new CircuitBreakerOpenException("熔断器开启");
}
}
try {
T result = operation.get();
if (state == State.HALF_OPEN) {
state = State.CLOSED;
failureCount = 0;
}
return result;
} catch (Exception e) {
handleFailure();
throw e;
}
}
private void handleFailure() {
failureCount++;
lastFailureTime = System.currentTimeMillis();
if (failureCount >= 3) {
state = State.OPEN;
}
}
}
// 模式3:降级模式
public String getDataWithFallback() {
try {
return getDataFromPrimarySource();
} catch (Exception e) {
log.warn("主数据源失败,使用备用数据源", e);
return getDataFromSecondarySource();
}
}
// 模式4:空对象模式
public User getCurrentUser() {
try {
return userService.getCurrentUser();
} catch (AuthenticationException e) {
// 返回空对象而不是null
return new AnonymousUser();
}
}
// 辅助类和方法
static class RecoverableException extends RuntimeException {}
static class MaxRetriesExceededException extends RuntimeException {
MaxRetriesExceededException(String msg, Throwable cause) {
super(msg, cause);
}
}
static class CircuitBreakerOpenException extends RuntimeException {
CircuitBreakerOpenException(String msg) { super(msg); }
}
private void waitBeforeRetry(int attempt) {
try {
Thread.sleep(1000 * attempt); // 指数退避
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private String getDataFromPrimarySource() {
return "主数据";
}
private String getDataFromSecondarySource() {
return "备用数据";
}
// 空对象模式示例
interface User {
String getName();
boolean isAuthenticated();
}
class AnonymousUser implements User {
@Override public String getName() { return "匿名用户"; }
@Override public boolean isAuthenticated() { return false; }
}
// 模拟依赖
UserService userService = new UserService();
class UserService {
User getCurrentUser() throws AuthenticationException {
throw new AuthenticationException("未认证");
}
}
class AuthenticationException extends Exception {
AuthenticationException(String msg) { super(msg); }
}
private Logger log = Logger.getLogger(DomainSpecificPatterns.class.getName());
}
}
public class Main {
public static void main(String[] args) {
// 显示异常体系
ExceptionVsErrorComparison.showComparisonTable();
ExceptionVsErrorComparison.decisionFlowchart();
// 性能分析
ExceptionPerformanceAnalysis.benchmark();
ExceptionPerformanceAnalysis.stackTraceDepthImpact();
System.out.println("\\n=== Java异常处理学习路径 ===");
System.out.println("1. 理解Throwable、Exception、Error的继承关系");
System.out.println("2. 掌握try-catch-finally的基本用法");
System.out.println("3. 学习Checked vs Unchecked异常的区别");
System.out.println("4. 实践异常处理的最佳实践");
System.out.println("5. 了解现代Java的异常处理特性");
System.out.println("6. 学习异常处理的设计模式");
System.out.println("7. 掌握性能优化技巧");
}
}
八、总结
通过本文的全面探讨,我们深入理解了Java中Exception和Error的区别:
核心区别总结:
关键要点:
- 使用具体的异常类型,提供有意义的错误信息
- 保持异常链,不要丢失原始异常信息
- 合理使用检查型异常和非检查型异常
- 对于Error,重点是预防和优雅关闭,而不是捕获处理
- 利用现代Java特性改进异常处理代码
最佳实践:
记住:良好的异常处理不仅是技术问题,更是架构设计问题。它直接影响到系统的稳定性、可维护性和用户体验。

