深入理解 Java 异常体系:Checked vs Unchecked Exception
引言
Java异常体系是Java语言的重要组成部分,它提供了一种优雅的错误处理机制。理解Checked Exception和Unchecked Exception的区别,对于编写高质量的Java代码至关重要。本文将从异常体系的基础概念出发,深入分析两种异常类型的特点、应用场景和最佳实践。
Java异常体系基础
异常体系结构
Java异常体系基于继承关系构建,形成了完整的异常处理框架。
// 异常体系结构演示
public class ExceptionHierarchyDemo {
// 自定义异常类
public static class CustomCheckedException extends Exception {
public CustomCheckedException(String message) {
super(message);
}
public CustomCheckedException(String message, Throwable cause) {
super(message, cause);
}
}
public static class CustomUncheckedException extends RuntimeException {
public CustomUncheckedException(String message) {
super(message);
}
public CustomUncheckedException(String message, Throwable cause) {
super(message, cause);
}
}
// 异常体系关系演示
public static void demonstrateExceptionHierarchy() {
System.out.println("Throwable hierarchy:");
System.out.println(" Throwable");
System.out.println(" ├── Error");
System.out.println(" │ ├── OutOfMemoryError");
System.out.println(" │ ├── StackOverflowError");
System.out.println(" │ └── ...");
System.out.println(" └── Exception");
System.out.println(" ├── RuntimeException");
System.out.println(" │ ├── IllegalArgumentException");
System.out.println(" │ ├── NullPointerException");
System.out.println(" │ └── ...");
System.out.println(" ├── IOException");
System.out.println(" ├── SQLException");
System.out.println(" └── ...");
}
// 异常创建和抛出
public static void createAndThrowExceptions() {
try {
// 抛出Checked异常
throw new CustomCheckedException("This is a checked exception");
} catch (CustomCheckedException e) {
System.out.println("Caught checked exception: " + e.getMessage());
}
try {
// 抛出Unchecked异常
throw new CustomUncheckedException("This is an unchecked exception");
} catch (CustomUncheckedException e) {
System.out.println("Caught unchecked exception: " + e.getMessage());
}
}
}
Throwable类详解
Throwable是所有异常和错误的基类,包含了异常处理的核心功能。
// Throwable类功能演示
public class ThrowableDemo {
public static void demonstrateThrowableFeatures() {
try {
// 模拟异常情况
int result = 10 / 0;
} catch (Exception e) {
// 获取异常信息
System.out.println("Exception class: " + e.getClass().getName());
System.out.println("Exception message: " + e.getMessage());
// 获取栈跟踪信息
StackTraceElement[] stackTrace = e.getStackTrace();
System.out.println("Stack trace elements: " + stackTrace.length);
// 打印栈跟踪
e.printStackTrace();
// 获取异常原因
Throwable cause = e.getCause();
System.out.println("Cause: " + (cause != null ? cause.getMessage() : "null"));
// 异常链演示
try {
throw new RuntimeException("Outer exception", e);
} catch (RuntimeException re) {
System.out.println("Chained exception: " + re.getCause().getMessage());
}
}
}
// 自定义异常信息
public static class DetailedException extends Exception {
private final String errorCode;
private final long timestamp;
public DetailedException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
this.timestamp = System.currentTimeMillis();
}
public String getErrorCode() {
return errorCode; }
public long getTimestamp() {
return timestamp; }
@Override
public String toString() {
return String.format("[%s] %s: %s at %d",
errorCode, getClass().getSimpleName(), getMessage(), timestamp);
}
}
public static void demonstrateCustomException() {
try {
throw new DetailedException("ERR_001", "Custom detailed exception occurred");
} catch (DetailedException e) {
System.out.println("Custom exception: " + e);
System.out.println("Error code: " + e.getErrorCode());
System.out.println("Timestamp: " + e.getTimestamp());
}
}
}
Checked Exception详解
Checked Exception定义和特点
Checked Exception是编译时异常,必须在编译时处理。
// Checked Exception示例
public class CheckedExceptionDemo {
// 自定义Checked异常
public static class FileProcessingException extends Exception {
private final String fileName;
private final String operation;
public FileProcessingException(String fileName, String operation, String message) {
super(String.format("File %s: %s failed - %s", fileName, operation, message));
this.fileName = fileName;
this.operation = operation;
}
public String getFileName() {
return fileName; }
public String getOperation() {
return operation; }
}
// 方法必须声明抛出Checked异常
public static String readFile(String fileName) throws FileProcessingException {
if (fileName == null || fileName.isEmpty()) {
throw new FileProcessingException(fileName, "read", "File name is null or empty");
}
if (fileName.contains("..")) {
throw new FileProcessingException(fileName, "read", "Invalid file path");
}
// 模拟文件读取
return "File content for: " + fileName;
}
// 调用Checked异常方法的处理方式
public static void demonstrateCheckedExceptionHandling() {
// 方式1: try-catch处理
try {
String content = readFile("example.txt");
System.out.println("File content: " + content);
} catch (FileProcessingException e) {
System.out.println("Handled in try-catch: " + e.getMessage());
}
// 方式2: 向上抛出异常
try {
processFile("example.txt");
} catch (FileProcessingException e) {
System.out.println("Propagated exception handled: " + e.getMessage());
}
}
// 方法继续向上抛出异常
public static void processFile(String fileName) throws FileProcessingException {
String content = readFile(fileName);
System.out.println("Processing: " + content);
}
// 多重Checked异常处理
public static class NetworkException extends Exception {
public NetworkException(String message) {
super(message);
}
}
public static class ValidationException extends Exception {
public ValidationException(String message) {
super(message);
}
}
public static void complexMethod() throws FileProcessingException, NetworkException, ValidationException {
// 可能抛出多种Checked异常
readFile("config.txt");
throw new NetworkException("Network connection failed");
}
public static void handleMultipleCheckedExceptions() {
try {
complexMethod();
} catch (FileProcessingException | NetworkException | ValidationException e) {
System.out.println("Multiple exception types handled: " + e.getMessage());
}
}
// Checked异常的优势演示
public static class BankAccount {
private double balance;
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("Insufficient funds: balance=" + balance + ", requested=" + amount);
}
balance -= amount;
}
public double getBalance() {
return balance;
}
}
public static class InsufficientFundsException extends Exception {
public InsufficientFundsException(String message) {
super(message);
}
}
public static void demonstrateCheckedExceptionBenefits() {
BankAccount account = new BankAccount();
try {
account.withdraw(1000); // 编译器强制要求处理异常
} catch (InsufficientFundsException e) {
System.out.println("Checked exception forces error handling: " + e.getMessage());
}
}
}
Checked Exception的使用场景
Checked Exception适用于可恢复的、预期的异常情况。
// Checked Exception实际应用场景
public class CheckedExceptionUseCases {
// 网络操作异常
public static class NetworkOperationException extends Exception {
public NetworkOperationException(String message, Throwable cause) {
super(message, cause);
}
}
public static String fetchDataFromNetwork(String url) throws NetworkOperationException {
try {
// 模拟网络请求
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("URL cannot be null or empty");
}
// 模拟网络错误
if (Math.random() < 0.3) {
throw new IOException("Network timeout");
}
return "Data from " + url;
} catch (IOException e) {
throw new NetworkOperationException("Failed to fetch data from " + url, e);
}
}
// 数据库操作异常
public static class DatabaseOperationException extends Exception {
private final String operation;
private final String sql;
public DatabaseOperationException(String operation, String sql, String message) {
super("Database operation " + operation + " failed: " + message);
this.operation = operation;
this.sql = sql;
}
public String getOperation() {
return operation; }
public String getSql() {
return sql; }
}
public static void executeQuery(String sql) throws DatabaseOperationException {
if (sql == null || !sql.trim().toUpperCase().startsWith("SELECT")) {
throw new DatabaseOperationException("executeQuery", sql, "Invalid SQL query");
}
// 模拟数据库操作
if (Math.random() < 0.2) {
throw new DatabaseOperationException("executeQuery", sql, "Connection timeout");
}
System.out.println("Query executed: " + sql);
}
// 文件操作异常
public static class FileOperationException extends Exception {
private final String operation;
private final String fileName;
public FileOperationException(String operation, String fileName, String message) {
super("File operation " + operation + " on " + fileName + " failed: " + message);
this.operation = operation;
this.fileName = fileName;
}
public String getOperation() {
return operation; }
public String getFileName() {
return fileName; }
}
public static String readFileContent(String fileName) throws FileOperationException {
try {
if (fileName == null) {
throw new FileOperationException("read", "null", "File name is null");
}
// 模拟文件读取
if (fileName.contains("invalid")) {
throw new FileNotFoundException("File not found: " + fileName);
}
return "Content of " + fileName;
} catch (FileNotFoundException e) {
throw new FileOperationException("read", fileName, e.getMessage());
}
}
// 配置加载异常
public static class ConfigurationException extends Exception {
public ConfigurationException(String message) {
super(message);
}
public ConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}
public static Properties loadConfiguration(String configPath) throws ConfigurationException {
try {
Properties props = new Properties();
if (configPath == null) {
throw new IllegalArgumentException("Config path cannot be null");
}
// 模拟配置加载
if (configPath.endsWith(".invalid")) {
throw new IOException("Invalid configuration file format");
}
return props;
} catch (IOException e) {
throw new ConfigurationException("Failed to load configuration from " + configPath, e);
}
}
public static void demonstrateUseCases() {
try {
String data = fetchDataFromNetwork("https://api.example.com");
System.out.println("Fetched: " + data);
} catch (NetworkOperationException e) {
System.out.println("Network error: " + e.getMessage());
}
try {
executeQuery("SELECT * FROM users");
} catch (DatabaseOperationException e) {
System.out.println("Database error: " + e.getMessage());
}
try {
String content = readFileContent("config.properties");
System.out.println("File content: " + content);
} catch (FileOperationException e) {
System.out.println("File error: " + e.getMessage());
}
try {
Properties config = loadConfiguration("app.config");
System.out.println("Configuration loaded successfully");
} catch (ConfigurationException e) {
System.out.println("Configuration error: " + e.getMessage());
}
}
}
Unchecked Exception详解
Unchecked Exception定义和特点
Unchecked Exception是运行时异常,不需要强制处理。
// Unchecked Exception示例
public class UncheckedExceptionDemo {
// 自定义Unchecked异常
public static class BusinessLogicException extends RuntimeException {
private final String businessRule;
private final Object invalidValue;
public BusinessLogicException(String businessRule, Object invalidValue, String message) {
super(String.format("Business rule '%s' violated for value '%s': %s",
businessRule, invalidValue, message));
this.businessRule = businessRule;
this.invalidValue = invalidValue;
}
public String getBusinessRule() {
return businessRule; }
public Object getInvalidValue() {
return invalidValue; }
}
// 抛出Unchecked异常的方法
public static void validateAge(int age) {
if (age < 0) {
throw new BusinessLogicException("age_validation", age, "Age cannot be negative");
}
if (age > 150) {
throw new BusinessLogicException("age_validation", age, "Age seems unrealistic");
}
}
// Unchecked异常的处理
public static void demonstrateUncheckedExceptionHandling() {
// 方式1: 不处理(程序崩溃)
try {
validateAge(-5);
} catch (BusinessLogicException e) {
System.out.println("Caught unchecked exception: " + e.getMessage());
}
// 方式2: 使用通用异常处理器
try {
processUserAge(-10);
} catch (Exception e) {
System.out.println("Generic exception handler: " + e.getMessage());
}
// 方式3: 不处理,让调用者处理
try {
riskyOperation();
} catch (IllegalArgumentException e) {
System.out.println("Caught IllegalArgumentException: " + e.getMessage());
}
}
public static void processUserAge(int age) {
validateAge(age); // Unchecked异常自动向上抛出
System.out.println("Age is valid: " + age);
}
public static void riskyOperation() {
String str = null;
str.length(); // 会抛出NullPointerException
}
// 常见的Unchecked异常
public static void demonstrateCommonUncheckedExceptions() {
// NullPointerException
try {
String nullStr = null;
nullStr.length();
} catch (NullPointerException e) {
System.out.println("Caught NPE: " + e.getMessage());
}
// IndexOutOfBoundsException
try {
List
list.get(10);
} catch (IndexOutOfBoundsException e) {
System.out.println("Caught IndexOutOfBoundsException: " + e.getMessage());
}
// IllegalArgumentException
try {
validateAge(-1);
} catch (IllegalArgumentException e) {
System.out.println("Caught IllegalArgumentException: " + e.getMessage());
}
// ArithmeticException
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Caught ArithmeticException: " + e.getMessage());
}
}
// Unchecked异常的优势演示
public static class Calculator {
public static double divide(double a, double b) {
if (b == 0) {
throw new IllegalArgumentException("Division by zero is not allowed");
}
return a / b;
}
public static int[] getSubArray(int[] array, int start, int end) {
if (array == null) {
throw new NullPointerException("Array cannot be null");
}
if (start < 0 || end > array.length || start > end) {
throw new IndexOutOfBoundsException("Invalid array bounds");
}
return Arrays.copyOfRange(array, start, end);
}
}
public static void demonstrateUncheckedBenefits() {
try {
double result = Calculator.divide(10, 0);
} catch (IllegalArgumentException e) {
System.out.println("Unchecked exception caught: " + e.getMessage());
}
try {
int[] subArray = Calculator.getSubArray(null, 0, 5);
} catch (NullPointerException e) {
System.out.println("NPE caught: " + e.getMessage());
}
}
}
Unchecked Exception的使用场景
Unchecked Exception适用于编程错误和不可恢复的异常情况。
// Unchecked Exception实际应用场景
public class UncheckedExceptionUseCases {
// 参数验证异常
public static class InvalidArgumentException extends IllegalArgumentException {
private final String parameterName;
private final Object parameterValue;
public InvalidArgumentException(String parameterName, Object parameterValue, String message) {
super(String.format("Invalid argument '%s' with value '%s': %s",
parameterName, parameterValue, message));
this.parameterName = parameterName;
this.parameterValue = parameterValue;
}
public String getParameterName() {
return parameterName; }
public Object getParameterValue() {
return parameterValue; }
}
public static void processUser(String name, int age, String email) {
if (name == null || name.trim().isEmpty()) {
throw new InvalidArgumentException("name", name, "Name cannot be null or empty");
}
if (age < 0 || age > 150) {
throw new InvalidArgumentException("age", age, "Age must be between 0 and 150");
}
if (email == null || !email.contains("@")) {
throw new InvalidArgumentException("email", email, "Email format is invalid");
}
System.out.println("Processing user: " + name + ", age: " + age + ", email: " + email);
}
// 状态验证异常
public static class InvalidStateException extends IllegalStateException {
private final String currentState;
private final String requiredState;
public InvalidStateException(String currentState, String requiredState, String message) {
super(String.format("Invalid state: current='%s', required='%s', message='%s'",
currentState, requiredState, message));
this.currentState = currentState;
this.requiredState = requiredState;
}
public String getCurrentState() {
return currentState; }
public String getRequiredState() {
return requiredState; }
}
public static class Connection {
private boolean connected = false;
public void executeQuery(String sql) {
if (!connected) {
throw new InvalidStateException("disconnected", "connected",
"Cannot execute query on disconnected connection");
}
System.out.println("Executing query: " + sql);
}
public void connect() {
connected = true;
}
public void disconnect() {
connected = false;
}
}
// 资源使用异常
public static class ResourceNotAvailableException extends RuntimeException {
private final String resourceType;
private final String resourceId;
public ResourceNotAvailableException(String resourceType, String resourceId, String message) {
super(String.format("%s resource '%s' is not available: %s",
resourceType, resourceId, message));
this.resourceType = resourceType;
this.resourceId = resourceId;
}
public String getResourceType() {
return resourceType; }
public String getResourceId() {
return resourceId; }
}
public static class ResourceManager {
private final Set
public ResourceManager() {
availableResources.add("resource1");
availableResources.add("resource2");
}
public String acquireResource(String resourceId) {
if (!availableResources.contains(resourceId)) {
throw new ResourceNotAvailableException("Resource", resourceId,
"Resource is not available in the pool");
}
availableResources.remove(resourceId);
return "Acquired: " + resourceId;
}
public void releaseResource(String resourceId) {
availableResources.add(resourceId);
}
}
// 断言异常
public static class AssertionError extends RuntimeException {
public AssertionError(String message) {
super(message);
}
}
public static void validateBusinessLogic(int value) {
if (value < 0) {
throw new AssertionError("Business logic violation: value should not be negative");
}
if (value > 1000) {
throw new AssertionError("Business logic violation: value exceeds maximum allowed");
}
}
public static void demonstrateUseCases() {
try {
processUser(null, 25, "test@example.com");
} catch (InvalidArgumentException e) {
System.out.println("Parameter validation failed: " + e.getMessage());
}
Connection conn = new Connection();
try {
conn.executeQuery("SELECT * FROM users"); // Should fail
} catch (InvalidStateException e) {
System.out.println("State validation failed: " + e.getMessage());
}
conn.connect();
try {
conn.executeQuery("SELECT * FROM users");
} catch (Exception e) {
System.out.println("Query execution failed: " + e.getMessage());
}
ResourceManager rm = new ResourceManager();
try {
String resource = rm.acquireResource("nonexistent");
} catch (ResourceNotAvailableException e) {
System.out.println("Resource acquisition failed: " + e.getMessage());
}
try {
validateBusinessLogic(-5);
} catch (AssertionError e) {
System.out.println("Assertion failed: " + e.getMessage());
}
}
}
Checked vs Unchecked Exception对比分析
详细对比表格
特性
Checked Exception
Unchecked Exception
继承关系
继承Exception但不继承RuntimeException
继承RuntimeException
编译时检查
必须处理或声明抛出
不强制处理
适用场景
可恢复的、预期的异常
编程错误、不可恢复的异常
性能影响
相对较小
相对较小
调用者责任
必须处理异常
可选择处理
异常传播
必须在方法签名中声明
自动向上传播
常见类型
IOException, SQLException等
NullPointerException, IllegalArgumentException等
// 对比分析示例
public class ExceptionComparisonDemo {
// Checked异常示例
public static String readConfigFile(String fileName) throws IOException {
if (fileName == null) {
throw new IllegalArgumentException("File name cannot be null");
}
// 模拟文件读取
if (!fileName.endsWith(".properties")) {
throw new IOException("Invalid file format: " + fileName);
}
return "Configuration content";
}
// Unchecked异常示例
public static int calculateAge(int birthYear) {
int currentYear = Calendar.getInstance().get(Calendar.YEAR);
if (birthYear > currentYear) {
throw new IllegalArgumentException("Birth year cannot be in the future: " + birthYear);
}
if (birthYear < 1900) {
throw new IllegalArgumentException("Birth year seems unrealistic: " + birthYear);
}
return currentYear - birthYear;
}
// 异常处理策略对比
public static void demonstrateHandlingStrategies() {
// Checked异常处理策略
System.out.println("=== Checked Exception Handling ===");
try {
String config = readConfigFile("app.config");
System.out.println("Config loaded: " + config);
} catch (IOException e) {
System.out.println("Checked exception handled: " + e.getMessage());
// 可以提供备用配置或用户提示
System.out.println("Using default configuration...");
}
// Unchecked异常处理策略
System.out.println("\n=== Unchecked Exception Handling ===");
try {
int age = calculateAge(2025); // Future year
System.out.println("Age calculated: " + age);
} catch (IllegalArgumentException e) {
System.out.println("Unchecked exception handled: " + e.getMessage());
// 记录错误并返回默认值或抛出更具体的异常
}
// 混合异常处理
System.out.println("\n=== Mixed Exception Handling ===");
try {
handleMixedExceptions();
} catch (IOException e) {
System.out.println("IO exception: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Illegal argument: " + e.getMessage());
} catch (Exception e) {
System.out.println("Other exception: " + e.getMessage());
}
}
public static void handleMixedExceptions() throws IOException {
// 可能抛出Checked异常
readConfigFile("config.properties");
// 可能抛出Unchecked异常
calculateAge(-1);
}
// 异常设计原则演示
public static class DataProcessor {
// Checked异常:外部依赖可能失败,需要调用者处理
public String processDataFromExternal(String input) throws ExternalServiceException {
if (Math.random() < 0.3) {
// 模拟外部服务失败
throw new ExternalServiceException("External service unavailable");
}
return "Processed: " + input;
}
// Unchecked异常:编程错误,应该在开发阶段修复
public int divideSafely(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Division by zero is not allowed");
}
return a / b;
}
}
public static class ExternalServiceException extends Exception {
public ExternalServiceException(String message) {
super(message);
}
}
public static void demonstrateDesignPrinciples() {
DataProcessor processor = new DataProcessor();
try {
String result = processor.processDataFromExternal("input data");
System.out.println("External processing result: " + result);
} catch (ExternalServiceException e) {
System.out.println("External service error: " + e.getMessage());
// 可以重试、使用缓存数据或降级处理
}
try {
int division = processor.divideSafely(10, 0);
System.out.println("Division result: " + division);
} catch (IllegalArgumentException e) {
System.out.println("Programming error: " + e.getMessage());
// 这种错误应该在开发阶段修复,而不是在运行时处理
}
}
}
异常处理最佳实践
异常处理设计原则
// 异常处理最佳实践示例
public class ExceptionBestPractices {
// 1. 异常粒度适中
public static class SpecificExceptions {
public static class UserNotFoundException extends Exception {
private final String userId;
public UserNotFoundException(String userId) {
super("User not found: " + userId);
this.userId = userId;
}
public String getUserId() {
return userId; }
}
public static class DuplicateUserException extends Exception {
private final String userId;
public DuplicateUserException(String userId) {
super("Duplicate user: " + userId);
this.userId = userId;
}
public String getUserId() {
return userId; }
}
public static class UserValidationException extends Exception {
private final String field;
private final String value;
public UserValidationException(String field, String value, String message) {
super(String.format("Validation failed for field '%s' with value '%s': %s",
field, value, message));
this.field = field;
this.value = value;
}
public String getField() {
return field; }
public String getValue() {
return value; }
}
public static User findUser(String userId) throws UserNotFoundException {
if (userId == null || userId.isEmpty()) {
throw new IllegalArgumentException("User ID cannot be null or empty");
}
// 模拟查找用户
if (!"valid_user".equals(userId)) {
throw new UserNotFoundException(userId);
}
return new User(userId, "John Doe");
}
}
// 2. 异常信息详细
public static class DetailedExceptionInfo {
public static class DatabaseConnectionException extends Exception {
private final String host;
private final int port;
private final String database;
private final long connectionTimeout;
private final long timestamp;
public DatabaseConnectionException(String host, int port, String database,
long timeout, String message, Throwable cause) {
super(String.format("Failed to connect to database %s@%s:%d (timeout=%dms): %s",
database, host, port, timeout, message), cause);
this.host = host;
this.port = port;
this.database = database;
this.connectionTimeout = timeout;
this.timestamp = System.currentTimeMillis();
}
// Getters
public String getHost() {
return host; }
public int getPort() {
return port; }
public String getDatabase() {
return database; }
public long getConnectionTimeout() {
return connectionTimeout; }
public long getTimestamp() {
return timestamp; }
}
public static Connection createConnection(String host, int port, String database)
throws DatabaseConnectionException {
try {
// 模拟连接创建
if (host == null || host.isEmpty()) {
throw new IllegalArgumentException("Host cannot be null or empty");
}
if (port <= 0 || port > 65535) {
throw new IllegalArgumentException("Invalid port number: " + port);
}
// 模拟连接失败
if (Math.random() < 0.5) {
throw new SQLException("Connection timeout");
}
return new Connection() {
@Override
public Statement createStatement() throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return null;
}
@Override
public String nativeSQL(String sql) throws SQLException {
return null;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
}
@Override
public boolean getAutoCommit() throws SQLException {
return false;
}
@Override
public void commit() throws SQLException {
}
@Override
public void rollback() throws SQLException {
}
@Override
public void close() throws SQLException {
}
@Override
public boolean isClosed() throws SQLException {
return false;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
return null;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
}
@Override
public boolean isReadOnly() throws SQLException {
return false;
}
@Override
public void setCatalog(String catalog) throws SQLException {
}
@Override
public String getCatalog() throws SQLException {
return null;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
}
@Override
public int getTransactionIsolation() throws SQLException {
return 0;
}
@Override
public SQLWarning getWarnings() throws SQLException {
return null;
}
@Override
public void clearWarnings() throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
return null;
}
@Override
public Map
return null;
}
@Override
public void setTypeMap(Map
}
@Override
public void setHoldability(int holdability) throws SQLException {
}
@Override
public int getHoldability() throws SQLException {
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
return null;
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
return null;
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
return null;
}
@Override
public Clob createClob() throws SQLException {
return null;
}
@Override
public Blob createBlob() throws SQLException {
return null;
}
@Override
public NClob createNClob() throws SQLException {
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
return null;
}
@Override
public boolean isValid(int timeout) throws SQLException {
return false;
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
}
@Override
public String getClientInfo(String name) throws SQLException {
return null;
}
@Override
public Properties getClientInfo() throws SQLException {
return null;
}
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
return null;
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
}
@Override
public String getSchema() throws SQLException {
return null;
}
@Override
public void abort(Executor executor) throws SQLException {
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
}
@Override
public int getNetworkTimeout() throws SQLException {
return 0;
}
};
} catch (SQLException e) {
throw new DatabaseConnectionException(host, port, database, 30000,
"Database connection failed", e);
}
}
}
// 3. 异常链保持
public static class ExceptionChaining {
public static class ServiceLayerException extends Exception {
public ServiceLayerException(String message, Throwable cause) {
super(message, cause);
}
}
public static class DataLayerException extends Exception {
public DataLayerException(String message, Throwable cause) {
super(message, cause);
}
}
public static String getServiceData(String id) throws ServiceLayerException {
try {
return getDataFromDatabase(id);
} catch (DataLayerException e) {
throw new ServiceLayerException("Service layer error while getting data for ID: " + id, e);
}
}
private static String getDataFromDatabase(String id) throws DataLayerException {
try {
// 模拟数据库操作
if (Math.random() < 0.3) {
throw new SQLException("Database query failed");
}
return "Data for " + id;
} catch (SQLException e) {
throw new DataLayerException("Failed to get data from database for ID: " + id, e);
}
}
}
// 4. 资源清理
public static class ResourceCleanup {
public static String readFileWithCleanup(String fileName) throws IOException {
// 使用try-with-resources确保资源清理
try (BufferedReader reader = Files.newBufferedReader(Paths.get(fileName))) {
return reader.lines().collect(Collectors.joining("\n"));
}
// BufferedReader会自动关闭,即使发生异常
}
public static String readFileWithFinally(String fileName) throws IOException {
BufferedReader reader = null;
try {
reader = Files.newBufferedReader(Paths.get(fileName));
return reader.lines().collect(Collectors.joining("\n"));
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.err.println("Error closing reader: " + e.getMessage());
}
}
}
}
}
// 5. 异常日志记录
public static class ExceptionLogging {
private static final Logger logger = LoggerFactory.getLogger(ExceptionLogging.class);
public static void processWithLogging(int value) {
try {
if (value < 0) {
throw new IllegalArgumentException("Value cannot be negative: " + value);
}
System.out.println("Processing value: " + value);
} catch (IllegalArgumentException e) {
logger.error("Invalid argument provided: {}", value, e);
// 可以发送告警或记录到监控系统
throw e; // 重新抛出异常
}
}
}
public static void demonstrateBestPractices() {
// 演示具体异常
try {
SpecificExceptions.User user = SpecificExceptions.findUser("invalid_user");
} catch (SpecificExceptions.UserNotFoundException e) {
System.out.println("Specific exception caught: " + e.getMessage());
System.out.println("User ID: " + e.getUserId());
}
// 演示详细异常信息
try {
DetailedExceptionInfo.createConnection("localhost", 5432, "mydb");
} catch (DetailedExceptionInfo.DatabaseConnectionException e) {
System.out.println("Detailed exception: " + e.getMessage());
System.out.println("Host: " + e.getHost());
System.out.println("Port: " + e.getPort());
System.out.println("Database: " + e.getDatabase());
}
// 演示异常链
try {
ExceptionChaining.getServiceData("test");
} catch (ExceptionChaining.ServiceLayerException e) {
System.out.println("Service exception: " + e.getMessage());
System.out.println("Caused by: " + e.getCause().getMessage());
System.out.println("Root cause: " + e.getCause().getCause().getMessage());
}
}
}
异常处理反模式
// 异常处理反模式示例
public class ExceptionAntiPatterns {
// 1. 吞掉异常(反模式)
public static void swallowedException() {
try {
// 危险:异常被完全忽略
int result = 10 / 0;
} catch (Exception e) {
// 啥也不做!这是反模式
}
}
// 2. 通用异常捕获(反模式)
public static void genericExceptionCatch() {
try {
// 执行一些操作
String str = null;
str.length();
} catch (Exception e) {
// 太宽泛的捕获,无法区分具体异常
System.out.println("Something went wrong");
}
}
// 3. 异常信息丢失(反模式)
public static void exceptionInfoLoss() {
try {
riskyOperation();
} catch (IOException e) {
// 丢失了原始异常信息
throw new RuntimeException("Something went wrong");
}
}
// 4. 返回错误码而不是异常(反模式)
public static class ErrorProneService {
public int processUser(String userData) {
try {
// 处理用户数据
if (userData == null) {
return -1; // 错误码
}
return 0; // 成功
} catch (Exception e) {
return -2; // 另一个错误码
}
}
}
// 5. 异常处理中的异常(反模式)
public static void exceptionInExceptionHandler() {
try {
riskyOperation();
} catch (Exception e) {
// 在异常处理中又抛出异常
String nullStr = null;
nullStr.length(); // 这会抛出NPE
}
}
// 6. 忽略资源清理(反模式)
public static String readFileAntiPattern(String fileName) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(fileName));
return reader.readLine();
} catch (IOException e) {
// 没有关闭资源!
throw new RuntimeException(e);
}
// reader没有在finally块中关闭
}
// 7. 异常类型过于宽泛(反模式)
public static class TooGenericException extends Exception {
public TooGenericException(String message) {
super(message);
}
}
public static void demonstrateAntiPatterns() {
System.out.println("Demonstrating exception anti-patterns:");
// 演示吞掉异常
swallowedException();
System.out.println("1. Exception swallowed - silent failure");
// 演示通用异常捕获
genericExceptionCatch();
System.out.println("2. Generic exception caught - lost specific information");
// 演示异常信息丢失
try {
exceptionInfoLoss();
} catch (RuntimeException e) {
System.out.println("3. Exception info lost: " + e.getMessage());
}
// 演示错误码模式
ErrorProneService service = new ErrorProneService();
int result = service.processUser(null);
System.out.println("4. Using error codes: " + result);
// 演示异常处理中的异常
try {
exceptionInExceptionHandler();
} catch (Exception e) {
System.out.println("5. Exception in handler: " + e.getClass().getSimpleName());
}
}
private static void riskyOperation() throws IOException {
throw new IOException("Simulated IO error");
}
}
实际应用案例
Web应用异常处理
// Web应用异常处理示例
public class WebExceptionHandling {
// 自定义Web异常
public static class WebServiceException extends Exception {
private final int httpStatusCode;
private final String errorCode;
public WebServiceException(int httpStatusCode, String errorCode, String message) {
super(message);
this.httpStatusCode = httpStatusCode;
this.errorCode = errorCode;
}
public int getHttpStatusCode() {
return httpStatusCode; }
public String getErrorCode() {
return errorCode; }
}
// 用户服务
public static class UserService {
public User getUserById(String userId) throws WebServiceException {
if (userId == null || userId.trim().isEmpty()) {
throw new WebServiceException(400, "INVALID_USER_ID", "User ID cannot be null or empty");
}
// 模拟数据库查找
if (Math.random() < 0.2) {
throw new WebServiceException(500, "DATABASE_ERROR", "Database connection failed");
}
if (!isValidUserId(userId)) {
throw new WebServiceException(404, "USER_NOT_FOUND", "User not found: " + userId);
}
return new User(userId, "John Doe");
}
private boolean isValidUserId(String userId) {
return userId.matches("^[a-zA-Z0-9_]{3,20}$");
}
}
// API控制器
public static class UserController {
private final UserService userService = new UserService();
public ResponseEntity
try {
User user = userService.getUserById(userId);
return ResponseEntity.ok(user);
} catch (WebServiceException e) {
return ResponseEntity.status(e.getHttpStatusCode())
.body(new ErrorResponse(e.getErrorCode(), e.getMessage()));
} catch (Exception e) {
// 捕获其他未预期异常
return ResponseEntity.status(500)
.body(new ErrorResponse("INTERNAL_ERROR", "Internal server error"));
}
}
}
// 错误响应类
public static class ErrorResponse {
private final String errorCode;
private final String message;
private final long timestamp;
public ErrorResponse(String errorCode, String message) {
this.errorCode = errorCode;
this.message = message;
this.timestamp = System.currentTimeMillis();
}
// Getters
public String getErrorCode() {
return errorCode; }
public String getMessage() {
return message; }
public long getTimestamp() {
return timestamp; }
}
// 响应实体类
public static class ResponseEntity
private final T body;
private final int statusCode;
private ResponseEntity(T body, int statusCode) {
this.body = body;
this.statusCode = statusCode;
}
public static
return new ResponseEntity<>(body, 200);
}
public static
return new ResponseEntity<>(null, statusCode);
}
public T getBody() {
return body; }
public int getStatusCode() {
return statusCode; }
}
public static void demonstrateWebHandling() {
UserController controller = new UserController();
// 正常情况
ResponseEntity
System.out.println("Status: " + response1.getStatusCode());
// 无效用户ID
ResponseEntity
System.out.println("Error status: " + response2.getStatusCode());
// 用户不存在
ResponseEntity
System.out.println("Not found status: " + response3.getStatusCode());
}
}
数据库操作异常处理
// 数据库操作异常处理示例
public class DatabaseExceptionHandling {
// 数据库异常包装
public static class DatabaseException extends Exception {
private final String operation;
private final String sql;
private final String tableName;
public DatabaseException(String operation, String sql, String tableName, String message, Throwable cause) {
super(String.format("Database operation '%s' on table '%s' failed: %s",
operation, tableName, message), cause);
this.operation = operation;
this.sql = sql;
this.tableName = tableName;
}
public String getOperation() {
return operation; }
public String getSql() {
return sql; }
public String getTableName() {
return tableName; }
}
// 用户数据访问对象
public static class UserDao {
public User findById(String userId) throws DatabaseException {
try {
if (userId == null) {
throw new IllegalArgumentException("User ID cannot be null");
}
// 模拟数据库查询
String sql = "SELECT * FROM users WHERE id = ?";
if (Math.random() < 0.3) {
throw new SQLException("Connection timeout");
}
if (Math.random() < 0.2) {
throw new SQLException("Query execution failed");
}
return new User(userId, "John Doe");
} catch (SQLException e) {
throw new DatabaseException("SELECT", "SELECT * FROM users WHERE id = ?", "users",
"Failed to find user by ID: " + userId, e);
}
}
public void save(User user) throws DatabaseException {
try {
if (user == null || user.getId() == null) {
throw new IllegalArgumentException("User and ID cannot be null");
}
String sql = "INSERT INTO users (id, name) VALUES (?, ?)";
if (Math.random() < 0.2) {
throw new SQLException("Duplicate key violation");
}
System.out.println("User saved: " + user.getName());
} catch (SQLException e) {
throw new DatabaseException("INSERT", "INSERT INTO users VALUES (?, ?)", "users",
"Failed to save user: " + user.getId(), e);
}
}
public void update(User user) throws DatabaseException {
try {
if (user == null || user.getId() == null) {
throw new IllegalArgumentException("User and ID cannot be null");
}
String sql = "UPDATE users SET name = ? WHERE id = ?";
if (Math.random() < 0.1) {
throw new SQLException("Record not found");
}
System.out.println("User updated: " + user.getName());
} catch (SQLException e) {
throw new DatabaseException("UPDATE", "UPDATE users SET name = ? WHERE id = ?", "users",
"Failed to update user: " + user.getId(), e);
}
}
}
// 事务管理
public static class TransactionManager {
public void executeInTransaction(Runnable operation) throws DatabaseException {
boolean success = false;
try {
// 开始事务
System.out.println("Starting transaction");
operation.run();
// 提交事务
System.out.println("Committing transaction");
success = true;
} catch (Exception e) {
// 回滚事务
System.out.println("Rolling back transaction");
if (e instanceof DatabaseException) {
throw e;
} else {
throw new DatabaseException("TRANSACTION", "TRANSACTION_ROLLBACK", "transaction",
"Transaction failed: " + e.getMessage(), e);
}
} finally {
if (!success) {
System.out.println("Transaction completed unsuccessfully");
}
}
}
}
public static void demonstrateDatabaseHandling() {
UserDao userDao = new UserDao();
TransactionManager txManager = new TransactionManager();
try {
// 查找用户
User user = userDao.findById("user123");
System.out.println("Found user: " + user.getName());
// 保存用户
userDao.save(new User("user456", "Jane Doe"));
// 更新用户
user = new User("user123", "John Smith");
userDao.update(user);
} catch (DatabaseException e) {
System.out.println("Database error:");
System.out.println(" Operation: " + e.getOperation());
System.out.println(" Table: " + e.getTableName());
System.out.println(" SQL: " + e.getSql());
System.out.println(" Message: " + e.getMessage());
if (e.getCause() != null) {
System.out.println(" Cause: " + e.getCause().getMessage());
}
}
// 演示事务处理
try {
txManager.executeInTransaction(() -> {
try {
userDao.save(new User("user789", "Bob Wilson"));
userDao.update(new User("user789", "Robert Wilson"));
} catch (DatabaseException e) {
throw new RuntimeException(e);
}
});
} catch (DatabaseException e) {
System.out.println("Transaction error: " + e.getMessage());
}
}
}
并发编程异常处理
// 并发编程异常处理示例
public class ConcurrentExceptionHandling {
// 并发任务异常
public static class TaskExecutionException extends Exception {
private final String taskId;
private final String executor;
public TaskExecutionException(String taskId, String executor, String message, Throwable cause) {
super(String.format("Task '%s' executed by '%s' failed: %s",
taskId, executor, message), cause);
this.taskId = taskId;
this.executor = executor;
}
public String getTaskId() {
return taskId; }
public String getExecutor() {
return executor; }
}
// 任务执行器
public static class TaskExecutor {
private final ExecutorService executorService = Executors.newFixedThreadPool(5);
public CompletableFuture
return CompletableFuture.supplyAsync(() -> {
try {
return task.call();
} catch (Exception e) {
throw new RuntimeException("Task execution failed", e);
}
}, executorService);
}
public List
return tasks.stream()
.map(this::executeTask)
.collect(Collectors.toList());
}
// 异常安全的任务执行
public
throws TaskExecutionException {
try {
return primary.call();
} catch (Exception e) {
System.out.println("Primary task failed, trying fallback: " + e.getMessage());
try {
return fallback.call();
} catch (Exception fallbackException) {
throw new TaskExecutionException("fallback", "fallback",
"Both primary and fallback tasks failed",
new Exception[] {
e, fallbackException});
}
}
}
private TaskExecutionException(String taskId, String executor, String message, Exception[] causes) {
super(message);
this.taskId = taskId;
this.executor = executor;
// 处理多个异常原因
}
}
// 异常传播示例
public static class AsyncExceptionHandler {
public void handleAsyncExceptions() {
CompletableFuture
if (Math.random() < 0.5) {
throw new RuntimeException("Async operation failed");
}
return "Success";
});
future.exceptionally(throwable -> {
System.out.println("Async exception caught: " + throwable.getMessage());
return "Default Value";
}).thenAccept(result -> {
System.out.println("Result: " + result);
});
}
// 异步任务链异常处理
public void handleAsyncChain() {
CompletableFuture
if (Math.random() < 0.3) {
throw new RuntimeException("Step 1 failed");
}
return "Step 1 result";
});
CompletableFuture
if (Math.random() < 0.3) {
throw new RuntimeException("Step 2 failed");
}
return result + " -> Step 2";
});
CompletableFuture
if (throwable != null) {
System.out.println("Chain failed at: " + throwable.getMessage());
return "Recovery result";
}
return result + " -> Step 3";
});
step3.thenAccept(result -> System.out.println("Final result: " + result));
}
}
public static void demonstrateConcurrentHandling() {
TaskExecutor executor = new TaskExecutor();
AsyncExceptionHandler asyncHandler = new AsyncExceptionHandler();
// 演示异步异常处理
asyncHandler.handleAsyncExceptions();
// 演示异步任务链
asyncHandler.handleAsyncChain();
// 演示任务执行器
try {
String result = executor.executeWithFallback(
() -> {
if (Math.random() < 0.7) {
throw new Exception("Primary task failed");
}
return "Primary success";
},
() -> "Fallback result"
);
System.out.println("Task result: " + result);
} catch (TaskExecutionException e) {
System.out.println("Task execution failed: " + e.getMessage());
}
}
}
总结与建议
选择指南表格
场景
推荐异常类型
原因
外部资源不可用
Checked
调用者需要处理资源不可用情况
网络连接失败
Checked
网络问题是可恢复的,需要重试逻辑
文件不存在
Checked
文件操作失败需要调用者决定如何处理
参数验证失败
Unchecked
编程错误,应该在开发阶段修复
空指针访问
Unchecked
编程错误,应该避免
数组越界
Unchecked
编程错误,应该在编码时避免
业务规则违反
Unchecked
逻辑错误,应该修复代码
数据库约束违反
Checked
外部依赖问题,需要处理
// 最佳实践总结示例
public class ExceptionHandlingSummary {
// 综合异常处理示例
public static class RobustService {
private final Logger logger = LoggerFactory.getLogger(RobustService.class);
// 使用Checked异常处理可恢复的外部依赖问题
public String processExternalData(String input) throws ExternalServiceException {
try {
if (input == null || input.trim().isEmpty()) {
throw new IllegalArgumentException("Input cannot be null or empty");
}
// 模拟外部服务调用
String result = callExternalService(input);
if (result == null) {
throw new ExternalServiceException("External service returned null");
}
return result;
} catch (IOException e) {
logger.error("External service call failed for input: {}", input, e);
throw new ExternalServiceException("Failed to call external service", e);
}
}
// 使用Unchecked异常处理编程错误
public int calculateResult(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Division by zero is not allowed");
}
if (a < 0 || b < 0) {
throw new IllegalArgumentException("Inputs must be non-negative");
}
return a / b;
}
// 异常安全的资源管理
public String readConfigFile(String fileName) throws ConfigException {
try (BufferedReader reader = Files.newBufferedReader(Paths.get(fileName))) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (IOException e) {
logger.error("Failed to read config file: {}", fileName, e);
throw new ConfigException("Cannot read configuration file: " + fileName, e);
}
}
private String callExternalService(String input) throws IOException {
// 模拟外部服务调用
if (Math.random() < 0.3) {
throw new IOException("Service timeout");
}
return "Processed: " + input;
}
public static class ExternalServiceException extends Exception {
public ExternalServiceException(String message) {
super(message);
}
public ExternalServiceException(String message, Throwable cause) {
super(message, cause);
}
}
public static class ConfigException extends Exception {
public ConfigException(String message, Throwable cause) {
super(message, cause);
}
}
}
// 异常处理最佳实践总结
public static void bestPracticesSummary() {
System.out.println("Java异常处理最佳实践总结:");
System.out.println();
System.out.println("1. 异常分类原则:");
System.out.println(" - Checked Exception: 可恢复的、预期的外部依赖问题");
System.out.println(" - Unchecked Exception: 编程错误、不可恢复的内部问题");
System.out.println("\n2. 异常设计原则:");
System.out.println(" - 创建具体的异常类型");
System.out.println(" - 提供详细的异常信息");
System.out.println(" - 保持异常链");
System.out.println(" - 在适当的地方处理异常");
System.out.println("\n3. 资源管理:");
System.out.println(" - 使用try-with-resources");
System.out.println(" - 确保资源正确释放");
System.out.println(" - 在finally块中清理资源");
System.out.println("\n4. 日志记录:");
System.out.println(" - 记录异常信息");
System.out.println(" - 包含上下文信息");
System.out.println(" - 避免记录敏感信息");
System.out.println("\n5. 性能考虑:");
System.out.println(" - 异常创建有性能开销");
System.out.println(" - 避免使用异常进行流程控制");
System.out.println(" - 合理使用异常类型");
}
public static void demonstrateSummary() {
RobustService service = new RobustService();
try {
String result = service.processExternalData("test input");
System.out.println("External processing result: " + result);
} catch (RobustService.ExternalServiceException e) {
System.out.println("External service error: " + e.getMessage());
}
try {
int calculation = service.calculateResult(10, 0);
System.out.println("Calculation result: " + calculation);
} catch (IllegalArgumentException e) {
System.out.println("Calculation error: " + e.getMessage());
}
bestPracticesSummary();
}
}
Java异常体系是构建健壮应用程序的重要组成部分。通过正确理解Checked Exception和Unchecked Exception的区别,选择合适的异常处理策略,开发者可以构建出更加稳定、可维护的Java应用程序。
关于作者
🌟 我是suxiaoxiang,一位热爱技术的开发者 💡 专注于Java生态和前沿技术分享 🚀 持续输出高质量技术内容
如果这篇文章对你有帮助,请支持一下:
👍 点赞
⭐ 收藏
👀 关注
您的支持是我持续创作的动力!感谢每一位读者的关注与认可!