18.1.4 Java异常处理机制与最佳实践

1. 异常体系结构

1.1 异常类层次结构

Throwable
├── Error (系统级错误,不应被捕获)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   └── VirtualMachineError
└── Exception (程序级异常)
    ├── RuntimeException (运行时异常,非受检异常)
    │   ├── NullPointerException
    │   ├── IllegalArgumentException
    │   ├── IndexOutOfBoundsException
    │   └── ClassCastException
    └── 受检异常 (编译时必须处理)
        ├── IOException
        ├── SQLException
        └── ClassNotFoundException

1.2 异常分类详解

public class ExceptionClassificationDemo {
    
    // 1. Error示例 - 不应该被捕获
    public void demonstrateError() {
        // StackOverflowError
        recursiveMethod();
        
        // OutOfMemoryError
        List<byte[]> list = new ArrayList<>();
        while (true) {
            list.add(new byte[1024 * 1024]);  // 持续分配内存
        }
    }
    
    private void recursiveMethod() {
        recursiveMethod();  // 无限递归
    }
    
    // 2. 运行时异常示例 - 非受检异常
    public void demonstrateRuntimeException() {
        // NullPointerException
        String str = null;
        int length = str.length();  // 运行时抛出NPE
        
        // IllegalArgumentException
        Thread.sleep(-1);  // 非法参数
        
        // IndexOutOfBoundsException
        List<String> list = new ArrayList<>();
        String item = list.get(0);  // 索引越界
        
        // ClassCastException
        Object obj = "Hello";
        Integer num = (Integer) obj;  // 类型转换异常
    }
    
    // 3. 受检异常示例 - 编译时必须处理
    public void demonstrateCheckedException() throws IOException, SQLException {
        // IOException - 必须声明或捕获
        FileReader file = new FileReader("nonexistent.txt");
        
        // SQLException - 必须声明或捕获
        Connection conn = DriverManager.getConnection("invalid_url");
        
        // ClassNotFoundException - 必须声明或捕获
        Class.forName("com.nonexistent.Class");
    }
}

2. 异常处理语法

2.1 try-catch-finally基本语法

public class BasicExceptionHandling {
    
    public void basicTryCatch() {
        try {
            // 可能抛出异常的代码
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            // 处理特定异常
            System.out.println("除零异常: " + e.getMessage());
        } catch (Exception e) {
            // 处理其他异常
            System.out.println("其他异常: " + e.getMessage());
        } finally {
            // 无论是否发生异常都会执行
            System.out.println("清理资源");
        }
    }
    
    // 多重catch的顺序很重要
    public void multipleCatch() {
        try {
            String[] array = {"1", "2", "abc"};
            for (String str : array) {
                int num = Integer.parseInt(str);
                System.out.println(10 / num);
            }
        } catch (NumberFormatException e) {
            // 具体异常要放在前面
            System.out.println("数字格式异常: " + e.getMessage());
        } catch (ArithmeticException e) {
            System.out.println("算术异常: " + e.getMessage());
        } catch (Exception e) {
            // 通用异常放在最后
            System.out.println("其他异常: " + e.getMessage());
        }
    }
}

2.2 try-with-resources语法

public class TryWithResourcesDemo {
    
    // JDK 7+ try-with-resources
    public void readFileWithTryWithResources() {
        try (FileReader file = new FileReader("data.txt");
             BufferedReader reader = new BufferedReader(file)) {
            
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            
        } catch (IOException e) {
            System.out.println("文件读取异常: " + e.getMessage());
        }
        // 资源会自动关闭,不需要finally块
    }
    
    // 传统方式对比
    public void readFileTraditional() {
        FileReader file = null;
        BufferedReader reader = null;
        
        try {
            file = new FileReader("data.txt");
            reader = new BufferedReader(file);
            
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            
        } catch (IOException e) {
            System.out.println("文件读取异常: " + e.getMessage());
        } finally {
            // 手动关闭资源
            try {
                if (reader != null) reader.close();
            } catch (IOException e) {
                System.out.println("关闭reader失败");
            }
            try {
                if (file != null) file.close();
            } catch (IOException e) {
                System.out.println("关闭file失败");
            }
        }
    }
    
    // 自定义资源类
    public static class CustomResource implements AutoCloseable {
        private String name;
        
        public CustomResource(String name) {
            this.name = name;
            System.out.println("打开资源: " + name);
        }
        
        public void doSomething() {
            System.out.println("使用资源: " + name);
        }
        
        @Override
        public void close() {
            System.out.println("关闭资源: " + name);
        }
    }
    
    public void useCustomResource() {
        try (CustomResource resource = new CustomResource("数据库连接")) {
            resource.doSomething();
            // 可能抛出异常
            throw new RuntimeException("模拟异常");
        } catch (Exception e) {
            System.out.println("捕获异常: " + e.getMessage());
        }
        // 资源会自动关闭
    }
}

3. 异常抛出和声明

3.1 throw和throws的使用

public class ThrowAndThrowsDemo {
    
    // throws声明方法可能抛出的异常
    public void readFile(String filename) throws IOException, FileNotFoundException {
        if (filename == null) {
            // throw主动抛出异常
            throw new IllegalArgumentException("文件名不能为null");
        }
        
        if (!new File(filename).exists()) {
            throw new FileNotFoundException("文件不存在: " + filename);
        }
        
        // 可能抛出IOException的代码
        Files.readAllLines(Paths.get(filename));
    }
    
    // 方法调用者必须处理受检异常
    public void callReadFile() {
        try {
            readFile("data.txt");
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("IO异常: " + e.getMessage());
        }
    }
    
    // 或者继续向上抛出
    public void callReadFileAndThrow() throws IOException {
        readFile("data.txt");  // 继续向上抛出
    }
}

3.2 异常链和异常包装

public class ExceptionChainingDemo {
    
    public void demonstrateExceptionChaining() {
        try {
            lowLevelMethod();
        } catch (Exception e) {
            System.out.println("原始异常: " + e.getMessage());
            System.out.println("异常链:");
            
            Throwable cause = e;
            while (cause != null) {
                System.out.println("  " + cause.getClass().getSimpleName() + ": " + cause.getMessage());
                cause = cause.getCause();
            }
        }
    }
    
    private void lowLevelMethod() throws Exception {
        try {
            // 模拟底层异常
            int result = 10 / 0;
        } catch (ArithmeticException e) {
            // 包装异常,保留原始异常信息
            throw new Exception("业务处理失败", e);
        }
    }
    
    // 自定义异常包装
    public class BusinessException extends Exception {
        public BusinessException(String message, Throwable cause) {
            super(message, cause);
        }
    }
    
    public void businessMethod() throws BusinessException {
        try {
            // 调用可能失败的底层方法
            databaseOperation();
        } catch (SQLException e) {
            // 包装为业务异常
            throw new BusinessException("用户数据处理失败", e);
        }
    }
    
    private void databaseOperation() throws SQLException {
        throw new SQLException("数据库连接失败");
    }
}

4. 自定义异常

4.1 自定义异常类设计

// 自定义受检异常
public class UserNotFoundException extends Exception {
    private String userId;
    
    public UserNotFoundException(String userId) {
        super("用户不存在: " + userId);
        this.userId = userId;
    }
    
    public UserNotFoundException(String userId, Throwable cause) {
        super("用户不存在: " + userId, cause);
        this.userId = userId;
    }
    
    public String getUserId() {
        return userId;
    }
}

// 自定义运行时异常
public class InvalidConfigurationException extends RuntimeException {
    private String configKey;
    private String configValue;
    
    public InvalidConfigurationException(String configKey, String configValue) {
        super(String.format("无效配置 [%s=%s]", configKey, configValue));
        this.configKey = configKey;
        this.configValue = configValue;
    }
    
    public InvalidConfigurationException(String configKey, String configValue, Throwable cause) {
        super(String.format("无效配置 [%s=%s]", configKey, configValue), cause);
        this.configKey = configKey;
        this.configValue = configValue;
    }
    
    public String getConfigKey() { return configKey; }
    public String getConfigValue() { return configValue; }
}

// 使用自定义异常
public class UserService {
    private Map<String, User> users = new HashMap<>();
    
    public User findUser(String userId) throws UserNotFoundException {
        if (us

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论

相关推荐

投递美团等公司10个岗位
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务