Skip to content

15.3 常见异常类型

异常的分类

在 Java 中,异常主要分为两大类:

  1. 运行时异常(非检查型异常):继承自 RuntimeException,编译器不要求必须处理
  2. 检查型异常:直接继承自 Exception,编译器要求必须处理

运行时异常(非检查型异常)

1. NullPointerException(空指针异常)

产生原因:当尝试访问 null 对象的成员(属性或方法)时抛出

常见场景

  • 调用 null 对象的方法
  • 访问 null 对象的属性
  • 长度为 null 的数组的长度
  • 抛 null 作为异常

示例

java
public class NullPointerExceptionExample {
    public static void main(String[] args) {
        String str = null;
        try {
            System.out.println(str.length()); // 抛出 NullPointerException
        } catch (NullPointerException e) {
            System.out.println("空指针异常: " + e.getMessage());
        }
    }
}

解决方案

  • 在使用对象前检查是否为 null
  • 使用 Optional 类处理可能为 null 的情况
  • 避免返回 null,使用空集合或空对象代替

2. ArrayIndexOutOfBoundsException(数组索引越界异常)

产生原因:当访问数组的无效索引时抛出,索引小于 0 或大于等于数组长度

常见场景

  • 访问负数索引
  • 访问大于或等于数组长度的索引

示例

java
public class ArrayIndexOutOfBoundsExceptionExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};
        try {
            System.out.println(numbers[5]); // 抛出 ArrayIndexOutOfBoundsException
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组索引越界异常: " + e.getMessage());
        }
    }
}

解决方案

  • 在访问数组前检查索引是否在有效范围内
  • 使用循环时,确保循环变量在有效范围内

3. ArithmeticException(算术异常)

产生原因:当发生算术错误时抛出,如除以零

常见场景

  • 整数除以零
  • 取模运算时除数为零

示例

java
public class ArithmeticExceptionExample {
    public static void main(String[] args) {
        try {
            int result = 10 / 0; // 抛出 ArithmeticException
        } catch (ArithmeticException e) {
            System.out.println("算术异常: " + e.getMessage());
        }
    }
}

解决方案

  • 在进行除法或取模运算前检查除数是否为零
  • 使用 try-catch 捕获可能的算术异常

4. IllegalArgumentException(非法参数异常)

产生原因:当方法接收到无效参数时抛出

常见场景

  • 传递负数给要求正数的方法
  • 传递 null 给不接受 null 的方法
  • 传递不符合要求的参数值

示例

java
public class IllegalArgumentExceptionExample {
    public static void main(String[] args) {
        try {
            int age = -10;
            if (age < 0) {
                throw new IllegalArgumentException("年龄不能为负数");
            }
            System.out.println("年龄: " + age);
        } catch (IllegalArgumentException e) {
            System.out.println("非法参数异常: " + e.getMessage());
        }
    }
}

解决方案

  • 在方法中检查参数的有效性
  • 提供清晰的参数要求文档
  • 使用 IllegalArgumentException 明确指示参数错误

5. ClassCastException(类型转换异常)

产生原因:当尝试将对象转换为不兼容的类型时抛出

常见场景

  • 将父类对象强制转换为子类类型
  • 将接口实现类对象转换为不相关的类型

示例

java
public class ClassCastExceptionExample {
    public static void main(String[] args) {
        Object obj = "Hello";
        try {
            Integer number = (Integer) obj; // 抛出 ClassCastException
        } catch (ClassCastException e) {
            System.out.println("类型转换异常: " + e.getMessage());
        }
    }
}

解决方案

  • 在类型转换前使用 instanceof 检查类型兼容性
  • 避免不必要的类型转换
  • 使用泛型减少类型转换

6. NumberFormatException(数字格式异常)

产生原因:当尝试将字符串转换为数字失败时抛出

常见场景

  • 将包含非数字字符的字符串转换为数字
  • 将空字符串转换为数字
  • 将超过数字类型范围的字符串转换为数字

示例

java
public class NumberFormatExceptionExample {
    public static void main(String[] args) {
        try {
            String str = "abc";
            int number = Integer.parseInt(str); // 抛出 NumberFormatException
        } catch (NumberFormatException e) {
            System.out.println("数字格式异常: " + e.getMessage());
        }
    }
}

解决方案

  • 在转换前检查字符串是否为有效的数字格式
  • 使用 try-catch 捕获可能的数字格式异常
  • 使用正则表达式验证输入字符串

7. IndexOutOfBoundsException(索引越界异常)

产生原因:当访问集合或字符串的无效索引时抛出

常见场景

  • 访问 List 的无效索引
  • 访问 String 的无效索引

示例

java
public class IndexOutOfBoundsExceptionExample {
    public static void main(String[] args) {
        String str = "Hello";
        try {
            char c = str.charAt(10); // 抛出 StringIndexOutOfBoundsException
        } catch (IndexOutOfBoundsException e) {
            System.out.println("索引越界异常: " + e.getMessage());
        }
    }
}

解决方案

  • 在访问集合或字符串前检查索引是否在有效范围内
  • 使用循环时,确保循环变量在有效范围内

8. NoSuchElementException(无元素异常)

产生原因:当尝试访问不存在的元素时抛出

常见场景

  • 迭代器到达末尾后继续调用 next()
  • 从空集合中获取元素

示例

java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class NoSuchElementExceptionExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Iterator<String> iterator = list.iterator();
        try {
            iterator.next(); // 抛出 NoSuchElementException
        } catch (java.util.NoSuchElementException e) {
            System.out.println("无元素异常: " + e.getMessage());
        }
    }
}

解决方案

  • 在调用 next() 前使用 hasNext() 检查
  • 在从集合中获取元素前检查集合是否为空

检查型异常

1. IOException(输入/输出异常)

产生原因:当发生输入/输出错误时抛出

常见场景

  • 文件读写错误
  • 网络连接错误
  • 流操作错误

示例

java
import java.io.FileInputStream;
import java.io.IOException;

public class IOExceptionExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("example.txt")) {
            // 读取文件
        } catch (IOException e) {
            System.out.println("输入/输出异常: " + e.getMessage());
        }
    }
}

解决方案

  • 使用 try-catch 捕获 IOException
  • 确保资源正确关闭
  • 检查文件路径和权限

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

产生原因:当尝试打开不存在的文件时抛出

常见场景

  • 打开不存在的文件
  • 打开路径错误的文件

示例

java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileNotFoundExceptionExample {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("non_existent_file.txt")) {
            // 读取文件
        } catch (FileNotFoundException e) {
            System.out.println("文件未找到异常: " + e.getMessage());
        } catch (IOException e) {
            System.out.println("输入/输出异常: " + e.getMessage());
        }
    }
}

解决方案

  • 在打开文件前检查文件是否存在
  • 使用 try-catch 捕获 FileNotFoundException
  • 提供正确的文件路径

3. SQLException(数据库操作异常)

产生原因:当发生数据库操作错误时抛出

常见场景

  • 数据库连接错误
  • SQL 语句语法错误
  • 数据库权限错误

示例

java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class SQLExceptionExample {
    public static void main(String[] args) {
        try {
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
            // 执行数据库操作
        } catch (SQLException e) {
            System.out.println("数据库操作异常: " + e.getMessage());
        }
    }
}

解决方案

  • 使用 try-catch 捕获 SQLException
  • 确保数据库连接正确配置
  • 检查 SQL 语句语法

4. ClassNotFoundException(类未找到异常)

产生原因:当尝试加载不存在的类时抛出

常见场景

  • 使用 Class.forName() 加载不存在的类
  • 类路径配置错误
  • 缺少依赖包

示例

java
public class ClassNotFoundExceptionExample {
    public static void main(String[] args) {
        try {
            Class.forName("com.example.NonExistentClass"); // 抛出 ClassNotFoundException
        } catch (ClassNotFoundException e) {
            System.out.println("类未找到异常: " + e.getMessage());
        }
    }
}

解决方案

  • 确保类名正确
  • 检查类路径配置
  • 确保依赖包已添加

5. InterruptedException(线程中断异常)

产生原因:当线程在等待、睡眠或被占用时被中断时抛出

常见场景

  • 线程 sleep() 时被中断
  • 线程 wait() 时被中断
  • 线程 join() 时被中断

示例

java
public class InterruptedExceptionExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("线程中断异常: " + e.getMessage());
            }
        });
        
        thread.start();
        thread.interrupt(); // 中断线程
    }
}

解决方案

  • 使用 try-catch 捕获 InterruptedException
  • 在捕获异常后恢复中断状态
  • 合理处理线程中断

错误(Error)

1. OutOfMemoryError(内存不足错误)

产生原因:当 JVM 内存不足时抛出

常见场景

  • 创建过多对象
  • 内存泄漏
  • 大对象分配

示例

java
public class OutOfMemoryErrorExample {
    public static void main(String[] args) {
        try {
            List<Object> list = new ArrayList<>();
            while (true) {
                list.add(new Object());
            }
        } catch (OutOfMemoryError e) {
            System.out.println("内存不足错误: " + e.getMessage());
        }
    }
}

解决方案

  • 增加 JVM 内存
  • 优化内存使用
  • 避免内存泄漏
  • 使用对象池

2. StackOverflowError(栈溢出错误)

产生原因:当方法调用栈深度过大时抛出

常见场景

  • 无限递归
  • 深层方法调用链

示例

java
public class StackOverflowErrorExample {
    public static void main(String[] args) {
        try {
            recursiveMethod();
        } catch (StackOverflowError e) {
            System.out.println("栈溢出错误: " + e.getMessage());
        }
    }
    
    public static void recursiveMethod() {
        recursiveMethod(); // 无限递归
    }
}

解决方案

  • 避免无限递归
  • 优化递归算法
  • 增加栈大小

示例:异常处理的综合应用

示例 1:用户输入验证

java
import java.util.Scanner;

public class InputValidationExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        try {
            System.out.print("请输入一个整数: ");
            String input = scanner.nextLine();
            int number = Integer.parseInt(input);
            System.out.println("输入的整数是: " + number);
        } catch (NumberFormatException e) {
            System.out.println("输入错误: 请输入有效的整数");
        } finally {
            scanner.close();
        }
    }
}

示例 2:文件操作

java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileOperationExample {
    public static void main(String[] args) {
        try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("文件操作错误: " + e.getMessage());
        }
    }
}

示例 3:数组操作

java
public class ArrayOperationExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3, 4, 5};
        
        try {
            System.out.print("请输入要访问的数组索引: ");
            Scanner scanner = new Scanner(System.in);
            int index = scanner.nextInt();
            
            if (index < 0 || index >= numbers.length) {
                throw new ArrayIndexOutOfBoundsException("索引超出范围: " + index);
            }
            
            System.out.println("索引 " + index + " 处的元素是: " + numbers[index]);
            scanner.close();
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组索引越界: " + e.getMessage());
        } catch (java.util.InputMismatchException e) {
            System.out.println("输入错误: 请输入有效的整数");
        }
    }
}

最佳实践

  1. 了解常见异常:熟悉常见的异常类型及其产生原因

  2. 捕获具体异常:尽量捕获具体的异常类型,而不是捕获所有异常

  3. 处理异常:捕获异常后应该进行适当的处理,而不是简单地打印错误信息

  4. 预防异常:在可能发生异常的地方进行预防,如 null 检查、范围检查等

  5. 使用 try-with-resources:对于需要释放的资源,使用 try-with-resources 自动关闭

  6. 记录异常:对于重要的异常,应该记录到日志中

  7. 避免过度使用异常:不要使用异常来控制正常的程序流程

总结

Java 中的异常分为两大类:

  1. 运行时异常(非检查型异常):继承自 RuntimeException,编译器不要求必须处理,如 NullPointerExceptionArrayIndexOutOfBoundsExceptionArithmeticException

  2. 检查型异常:直接继承自 Exception,编译器要求必须处理,如 IOExceptionFileNotFoundExceptionSQLException

此外,还有 错误(Error),如 OutOfMemoryErrorStackOverflowError,这些通常是严重的问题,程序无法恢复。

通过了解常见的异常类型及其产生原因,我们可以更好地预防和处理异常,提高程序的健壮性和可维护性。

© 2026 编程马·菜鸟教程 版权所有