常见的五个编译时异常和常见的五个编译时异常

目录

一、简要介绍

java 复制代码
// Java异常体系
Throwable
    ├── Error (错误,如内存溢出)
    ├── Exception (异常)
        ├── RuntimeException (运行时异常 - unchecked)
        └── 其他Exception (编译时异常 - checked)


简单理解:当你把代码写出来的时候,编译器有红色波浪线出现的时候为编译时异常,运行时出现的异常为运行时异常。

二、编译时异常

1. IOException(输入输出异常)

java 复制代码
import java.io.*;

public class IOExceptionExample {
    
    public void readFileContent(String filePath) {
        try {
            // 文件读取操作可能抛出IOException
            FileReader reader = new FileReader(filePath);
            BufferedReader br = new BufferedReader(reader);
            
            String line;
            while ((line = br.readLine()) != null) { // 读取时可能发生IO异常
                System.out.println(line);
            }
            
            br.close();
            
        } catch (IOException e) { // 必须捕获或声明抛出
            System.out.println("文件读取失败: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    // 或者在方法声明中抛出
    public void writeToFile(String filePath, String content) throws IOException {
        FileWriter writer = new FileWriter(filePath);
        writer.write(content); // 写入时可能发生IO异常
        writer.close();
    }
}

出现的情况:

文件读取/写入时磁盘故障
网络传输中断
流操作异常
文件权限不足

2、FileNotFoundException(文件未找到异常)

java 复制代码
import java.io.*;

public class FileNotFoundExceptionExample {
    
    public void processFile(String filename) {
        try {
            // 尝试打开不存在的文件
            FileInputStream fis = new FileInputStream(filename); // 可能抛出FileNotFoundException
            // 处理文件...
            fis.close();
            
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在: " + filename);
            // 创建新文件或提示用户
            createNewFile(filename);
            
        } catch (IOException e) {
            System.out.println("IO错误: " + e.getMessage());
        }
    }
    
    private void createNewFile(String filename) {
        try {
            File file = new File(filename);
            if (file.createNewFile()) {
                System.out.println("创建新文件: " + filename);
            }
        } catch (IOException e) {
            System.out.println("创建文件失败: " + e.getMessage());
        }
    }
}

文件路径错误
文件名拼写错误
文件被移动或删除
路径权限限制

3、SQLException(数据库异常)

java 复制代码
import java.sql.*;

public class SQLExceptionExample {
    
    private static final String URL = "jdbc:mysql://localhost:3306/test";
    private static final String USER = "root";
    private static final String PASSWORD = "password";
    
    public void queryUserData(int userId) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        
        try {
            // 数据库连接可能失败
            conn = DriverManager.getConnection(URL, USER, PASSWORD);
            
            // SQL执行可能失败
            pstmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
            pstmt.setInt(1, userId);
            
            rs = pstmt.executeQuery(); // 可能抛出SQLException
            
            while (rs.next()) {
                System.out.println("用户: " + rs.getString("username"));
            }
            
        } catch (SQLException e) {
            System.out.println("数据库操作失败:");
            System.out.println("错误码: " + e.getErrorCode());
            System.out.println("SQL状态: " + e.getSQLState());
            System.out.println("错误信息: " + e.getMessage());
            
        } finally {
            // 关闭资源
            try {
                if (rs != null) rs.close();
                if (pstmt != null) pstmt.close();
                if (conn != null) conn.close();
            } catch (SQLException e) {
                System.out.println("关闭连接失败: " + e.getMessage());
            }
        }
    }
}

数据库连接失败

SQL语法错误

表或列不存在

违反约束(如主键重复)

事务回滚

4、ClassNotFoundException(类未找到异常)

java 复制代码
public class ClassNotFoundExceptionExample {
    
    public void loadClassDynamically(String className) {
        try {
            // 动态加载类
            Class<?> clazz = Class.forName(className); // 可能抛出ClassNotFoundException
            System.out.println("成功加载类: " + clazz.getName());
            
            // 创建实例
            Object instance = clazz.newInstance();
            System.out.println("创建实例: " + instance);
            
        } catch (ClassNotFoundException e) {
            System.out.println("类未找到: " + className);
            System.out.println("请检查:");
            System.out.println("1. 类名拼写是否正确");
            System.out.println("2. 类是否在classpath中");
            System.out.println("3. 依赖包是否已添加");
            
        } catch (InstantiationException | IllegalAccessException e) {
            System.out.println("实例化失败: " + e.getMessage());
        }
    }
    
    // 实际应用场景:插件系统
    public void loadPlugin(String pluginClassName) {
        try {
            Class<?> pluginClass = Class.forName(pluginClassName);
            Plugin plugin = (Plugin) pluginClass.newInstance();
            plugin.execute();
            
        } catch (ClassNotFoundException e) {
            System.out.println("插件类未找到: " + pluginClassName);
        } catch (Exception e) {
            System.out.println("插件加载失败: " + e.getMessage());
        }
    }
}

interface Plugin {
    void execute();
}

动态类加载时类路径错误
缺少必要的JAR包
类名拼写错误
类文件被删除或损坏

5、InterruptedException(线程中断异常)

java 复制代码
public class InterruptedExceptionExample {
    
    public void timeConsumingTask() {
        Thread workerThread = new Thread(() -> {
            try {
                System.out.println("任务开始执行...");
                
                // 模拟耗时操作
                for (int i = 0; i < 10; i++) {
                    System.out.println("处理第 " + i + " 个步骤");
                    
                    // 检查线程中断状态
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("任务被中断,开始清理...");
                        return;
                    }
                    
                    // 线程睡眠 - 可能被中断
                    Thread.sleep(1000); // 可能抛出InterruptedException
                }
                
                System.out.println("任务完成");
                
            } catch (InterruptedException e) {
                // 线程在sleep/wait/join时被中断
                System.out.println("任务被中断,恢复中断状态");
                Thread.currentThread().interrupt(); // 恢复中断状态
                
                // 执行清理操作
                cleanup();
            }
        });
        
        workerThread.start();
        
        // 3秒后中断任务
        try {
            Thread.sleep(3000);
            workerThread.interrupt(); // 中断线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    private void cleanup() {
        System.out.println("执行清理操作...");
        // 释放资源、回滚操作等
    }
    
    // 实际应用:超时控制
    public void executeWithTimeout(Runnable task, long timeoutMs) {
        Thread taskThread = new Thread(task);
        taskThread.start();
        
        try {
            taskThread.join(timeoutMs); // 等待任务完成或超时
            
            if (taskThread.isAlive()) {
                System.out.println("任务超时,中断执行");
                taskThread.interrupt();
            }
            
        } catch (InterruptedException e) {
            System.out.println("等待被中断");
            taskThread.interrupt();
        }
    }
}

线程在sleep()时被中断
线程在wait()时被中断
线程在join()时被中断
执行超时控制
优雅停止线程

三、运行时异常

1、 NullPointerException(空指针异常)

java 复制代码
public class NullPointerExceptionExample {
    
    public void commonNPEscenarios() {
        // 场景1:调用null对象的方法
        String str = null;
        System.out.println(str.length()); // NullPointerException
        
        // 场景2:访问null数组元素
        int[] arr = null;
        System.out.println(arr[0]); // NullPointerException
        
        // 场景3:自动拆箱null
        Integer number = null;
        int value = number; // NullPointerException(自动拆箱)
    }
    
    // 实际开发中的防御性编程
    public void processUser(User user) {
        // ❌ 错误做法
        if (user.getName().equals("admin")) { // 如果user或user.getName()为null,NPE
            // ...
        }
        
        // ✅ 正确做法1:提前判空
        if (user != null && user.getName() != null 
            && user.getName().equals("admin")) {
            // ...
        }
        
        // ✅ 正确做法2:使用Objects.requireNonNull
        Objects.requireNonNull(user, "用户对象不能为空");
        Objects.requireNonNull(user.getName(), "用户名不能为空");
        
        // ✅ 正确做法3:使用Optional(Java 8+)
        Optional.ofNullable(user)
                .map(User::getName)
                .filter(name -> name.equals("admin"))
                .ifPresent(name -> System.out.println("管理员用户"));
    }
    
    // 深度嵌套对象的安全访问
    public void safeDeepAccess(User user) {
        // ❌ 危险的多层调用
        String city = user.getAddress().getCity().getName(); // 随时可能NPE
        
        // ✅ 安全的多层访问
        String safeCity = Optional.ofNullable(user)
                .map(User::getAddress)
                .map(Address::getCity)
                .map(City::getName)
                .orElse("未知城市");
        
        System.out.println("城市: " + safeCity);
    }
}

class User {
    private String name;
    private Address address;
    // getters/setters...
}

class Address {
    private City city;
    // getters/setters...
}

class City {
    private String name;
    // getters/setters...
}

调用null对象的方法或属性
数组为null时访问元素
集合操作中的null元素
链式调用中的中间对象为null

2、ArrayIndexOutOfBoundsException(数组越界异常)

java 复制代码
public class ArrayIndexExceptionExample {
    
    public void arrayOperations() {
        // 场景1:访问超出长度的索引
        int[] arr = {1, 2, 3};
        System.out.println(arr[3]); // 数组越界,有效索引:0-2
        
        // 场景2:循环边界错误
        for (int i = 0; i <= arr.length; i++) { // 应为 i < arr.length
            System.out.println(arr[i]); // 最后一次循环会越界
        }
        
        // 场景3:负数索引
        System.out.println(arr[-1]); // 数组越界
    }
    
    // 安全访问数组
    public void safeArrayAccess(int[] arr, int index) {
        // ✅ 方法1:检查边界
        if (index >= 0 && index < arr.length) {
            System.out.println("元素: " + arr[index]);
        } else {
            System.out.println("索引 " + index + " 越界,数组长度: " + arr.length);
        }
        
        // ✅ 方法2:使用try-catch
        try {
            System.out.println("元素: " + arr[index]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组访问越界: " + e.getMessage());
            // 返回默认值或抛出业务异常
        }
    }
    
    // 实际应用:矩阵操作
    public void matrixOperation(int[][] matrix, int row, int col) {
        // 安全访问二维数组
        if (row >= 0 && row < matrix.length 
            && col >= 0 && col < matrix[row].length) {
            System.out.println("Matrix[" + row + "][" + col + "] = " + matrix[row][col]);
        } else {
            throw new IllegalArgumentException("矩阵索引越界");
        }
    }
    
    // 使用增强for循环避免越界
    public void safeIteration() {
        String[] names = {"Alice", "Bob", "Charlie"};
        
        // ✅ 安全的迭代方式
        for (String name : names) {
            System.out.println(name); // 不会越界
        }
        
        // 如果需要索引
        for (int i = 0; i < names.length; i++) {
            System.out.println(i + ": " + names[i]);
        }
    }
}

访问超出数组长度的索引
循环条件错误导致索引超限
使用负数索引
多维数组访问越界

3、ClassCastException(类型转换异常)

java 复制代码
public class ClassCastExceptionExample {
    
    public void typeCastingIssues() {
        // 场景1:错误的向下转型
        Object obj = "Hello World";
        Integer number = (Integer) obj; // ClassCastException: String不能转为Integer
        
        // 场景2:集合中的错误类型
        List list = new ArrayList();
        list.add("字符串");
        list.add(123); // 自动装箱为Integer
        
        for (Object item : list) {
            String str = (String) item; // 当item是Integer时抛出异常
        }
    }
    
    // 安全的类型检查和转换
    public void safeTypeCasting(Object obj) {
        // ✅ 方法1:使用instanceof检查
        if (obj instanceof String) {
            String str = (String) obj;
            System.out.println("字符串长度: " + str.length());
        } else if (obj instanceof Integer) {
            Integer num = (Integer) obj;
            System.out.println("数值: " + num);
        } else {
            System.out.println("不支持的类型: " + obj.getClass());
        }
        
        // ✅ 方法2:Java 16+的模式匹配
        if (obj instanceof String str) {
            System.out.println("字符串: " + str);
        }
    }
    
    // 泛型集合的类型安全
    public void genericCollectionSafety() {
        // ❌ 原始类型(危险)
        List rawList = new ArrayList();
        rawList.add("test");
        rawList.add(123); // 允许添加不同类型
        
        // ✅ 使用泛型(安全)
        List<String> stringList = new ArrayList<>();
        stringList.add("test");
        // stringList.add(123); // 编译错误,类型安全
        
        // 类型安全的转换
        List<Object> mixedList = new ArrayList<>();
        mixedList.add("Hello");
        mixedList.add(42);
        
        for (Object item : mixedList) {
            if (item instanceof String) {
                processString((String) item);
            } else if (item instanceof Integer) {
                processInteger((Integer) item);
            }
        }
    }
    
    // 实际应用:多态处理
    public void handleAnimals(List<Animal> animals) {
        for (Animal animal : animals) {
            animal.makeSound();
            
            // 安全的特定类型处理
            if (animal instanceof Dog dog) {
                dog.fetch(); // Dog特有方法
            } else if (animal instanceof Cat cat) {
                cat.climb(); // Cat特有方法
            }
        }
    }
}

class Animal {
    void makeSound() {
        System.out.println("动物叫声");
    }
}

class Dog extends Animal {
    @Override void makeSound() {
        System.out.println("汪汪");
    }
    
    void fetch() {
        System.out.println("捡球");
    }
}

class Cat extends Animal {
    @Override void makeSound() {
        System.out.println("喵喵");
    }
    
    void climb() {
        System.out.println("爬树");
    }
}

错误的向下转型
原始类型集合的类型混淆
反射创建对象后错误转型
多态对象的错误类型判断

4、 IllegalArgumentException(非法参数异常)

java 复制代码
public class IllegalArgumentExceptionExample {
    
    // 参数验证的典型应用
    public void setAge(int age) {
        // ✅ 防御性编程:验证参数
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException(
                "年龄必须在0-150之间,当前值: " + age);
        }
        this.age = age;
    }
    
    public void createUser(String username, String email) {
        // 验证用户名
        if (username == null || username.trim().isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        
        if (username.length() < 3 || username.length() > 20) {
            throw new IllegalArgumentException(
                "用户名长度必须在3-20之间");
        }
        
        // 验证邮箱格式
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("邮箱格式不正确");
        }
        
        // 创建用户逻辑...
        System.out.println("创建用户: " + username);
    }
    
    // 集合操作的参数验证
    public void processList(List<String> list, int start, int end) {
        Objects.requireNonNull(list, "列表不能为空");
        
        if (start < 0 || end > list.size() || start > end) {
            throw new IllegalArgumentException(
                String.format("索引范围无效: start=%d, end=%d, size=%d", 
                    start, end, list.size()));
        }
        
        // 处理子列表
        List<String> subList = list.subList(start, end);
        // ...
    }
    
    // 使用自定义异常提供更详细信息
    public void validateOrder(Order order) {
        if (order == null) {
            throw new IllegalArgumentException("订单不能为空");
        }
        
        if (order.getItems() == null || order.getItems().isEmpty()) {
            throw new EmptyOrderException("订单商品不能为空");
        }
        
        if (order.getTotalAmount() <= 0) {
            throw new InvalidAmountException("订单金额必须大于0");
        }
        
        // 更多验证...
    }
}

// 自定义业务异常
class EmptyOrderException extends IllegalArgumentException {
    public EmptyOrderException(String message) {
        super(message);
    }
}

class InvalidAmountException extends IllegalArgumentException {
    public InvalidAmountException(String message) {
        super(message);
    }
}

方法参数不符合预期范围
参数为null或空字符串
参数格式不正确
业务规则验证失败

5、NumberFormatException(数字格式异常)

java 复制代码
public class NumberFormatExceptionExample {
    
    public void parseNumbers() {
        // 场景1:解析非数字字符串
        String str1 = "abc123";
        int num1 = Integer.parseInt(str1); // NumberFormatException
        
        // 场景2:空字符串或null
        String str2 = "";
        int num2 = Integer.parseInt(str2); // NumberFormatException
        
        // 场景3:包含特殊字符
        String str3 = "123.45";
        int num3 = Integer.parseInt(str3); // NumberFormatException(有小数点)
        
        // 场景4:超出范围
        String str4 = "99999999999999999999";
        long num4 = Long.parseLong(str4); // 可能超出范围
    }
    
    // 安全的数字解析
    public Integer safeParseInt(String str) {
        // ✅ 方法1:try-catch
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            System.out.println("数字格式错误: " + str);
            return null; // 或返回默认值
        }
    }
    
    public Integer safeParseIntWithDefault(String str, Integer defaultValue) {
        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
    
    // ✅ 方法2:使用正则验证
    public boolean isValidInteger(String str) {
        if (str == null || str.trim().isEmpty()) {
            return false;
        }
        
        // 允许正负号开头
        String pattern = "^[-+]?\\d+$";
        return str.matches(pattern);
    }
    
    public Integer parseWithValidation(String str) {
        if (!isValidInteger(str)) {
            throw new IllegalArgumentException("无效的整数格式: " + str);
        }
        
        try {
            return Integer.valueOf(str);
        } catch (NumberFormatException e) {
            // 理论上不会到达这里,但作为额外保护
            throw new IllegalArgumentException("数字超出范围: " + str);
        }
    }
    
    // 实际应用:处理用户输入
    public void processUserInput(String input) {
        // 清理输入
        String cleaned = input.trim();
        
        // 多种解析尝试
        Number number = null;
        
        try {
            // 先尝试整数
            number = Integer.parseInt(cleaned);
        } catch (NumberFormatException e1) {
            try {
                // 再尝试浮点数
                number = Double.parseDouble(cleaned);
            } catch (NumberFormatException e2) {
                try {
                    // 尝试科学计数法
                    number = new java.math.BigDecimal(cleaned);
                } catch (NumberFormatException e3) {
                    System.out.println("无法解析的数字: " + input);
                    return;
                }
            }
        }
        
        System.out.println("解析成功: " + number);
    }
    
    // 使用Scanner进行安全解析
    public void safeParseWithScanner(String input) {
        Scanner scanner = new Scanner(input);
        
        if (scanner.hasNextInt()) {
            int value = scanner.nextInt();
            System.out.println("整数: " + value);
        } else if (scanner.hasNextDouble()) {
            double value = scanner.nextDouble();
            System.out.println("浮点数: " + value);
        } else {
            System.out.println("不是有效数字: " + input);
        }
        
        scanner.close();
    }
}

将非数字字符串转为数字类型
字符串包含非数字字符
数字格式不符合预期(如包含逗号、小数点等)
数字超出类型的范围

相关推荐
m0_661279181 小时前
学习笔记-安装并启动 Jupyter Noteboo
开发语言·python
烽火聊员1 小时前
SSLSocket 服务器端WPF C#测试代码
开发语言·c#·wpf·ssl
茉莉玫瑰花茶1 小时前
ProtoBuf - 1 - 下载和环境配置
开发语言·c++·protobuf
_OP_CHEN1 小时前
C++进阶:(十六)从裸指针到智能指针,C++ 内存管理的 “自动驾驶” 进化之路
开发语言·c++
007php0071 小时前
redis缓存功能结合实际项目面试之问题与解析
网络·redis·nginx·缓存·面试·职场和发展·php
努力学习的小廉1 小时前
【QT(二)】—— 初识QT
开发语言·qt
爱学习的小邓同学1 小时前
C++ --- map/set的使用
开发语言·c++
weixin_421133411 小时前
JShielder
开发语言
MSTcheng.1 小时前
【C++进阶】继承(下)——挖掘继承深处的奥秘!
开发语言·c++