Java 错误与异常(Throwable)
Throwable 是所有异常和错误的根类。实现 Throwable 或其子类的对象才能被 throw 或 catch。
- Error: 表示严重的系统级问题,通常不应该被捕获或处理,程序通常无法从中恢复。
- Exception: 表示程序可以处理的问题。分为 运行时异常、 受检异常 两类。
- 运行时异常: 不强制要求 try-catch 处理。通常是程序逻辑错误。
- 受检异常: 编译时必须处理(try-catch 或 throws)。表示外部环境问题或可恢复的异常。
java.lang.Throwable : ├── java.lang.Error : 错误│ ├── java.lang.VirtualMachineError : JVM 出现严重问题│ │ ├── java.lang.OutOfMemoryError : 内存不足│ │ ├── java.lang.StackOverflowError : 栈溢出│ │ ├── java.lang.UnknownError : 未知的虚拟机错误│ │ └── ...│ ├── java.lang.LinkageError : 类依赖问题│ │ ├── java.lang.ClassFormatError : 类文件格式错误│ │ ├── java.lang.NoClassDefFoundError : 类在编译时存在,运行时找不到│ │ ├── java.lang.UnsupportedClassVersionError : JVM 版本不支持该类版本│ │ └── ...│ ├── java.lang.ThreadDeath : 线程被终止(调用 thread.stop())│ ├── java.lang.ExceptionInInitializerError : 静态初始化出错│ └── ...│└── java.lang.Exception : 异常 ├── java.lang.RuntimeException : 运行时异常(非受检异常) │ ├── java.lang.ArithmeticException : 算术异常(除0) │ ├── java.lang.ArrayStoreException : 数组存储类型不匹配 │ ├── java.lang.ClassCastException : 类型转换异常 │ ├── java.lang.IllegalArgumentException : 非法参数 │ │ ├── java.lang.NumberFormatException : 字符串转数字失败 │ │ └── ... │ ├── java.lang.IndexOutOfBoundsException : 索引越界 │ │ ├── java.lang.ArrayIndexOutOfBoundsException : 数组索引越界 │ │ └── java.lang.StringIndexOutOfBoundsException : 字符串索引越界(charAt, substring, indexOf, codePointAt) │ ├── java.lang.NullPointerException : 空指针异常 │ ├── java.lang.SecurityException : 安全限制阻止操作 │ └── ... │ ├── java.io.IOException : I/O异常(受检异常) │ ├── java.io.FileNotFoundException : 文件不存在 │ ├── java.io.InterruptedIOException : I/O被中断 │ └── ... │ ├── java.lang.ClassNotFoundException : 找不到类(受检异常) ├── java.lang.CloneNotSupportedException : 不允许克隆(受检异常) ├── java.lang.IllegalAccessException : 访问权限不足(受检异常) ├── java.lang.InstantiationException : 无法实例化类(受检异常) ├── java.lang.NoSuchFieldException : 字段不存在(受检异常) ├── java.lang.NoSuchMethodException : 方法不存在(受检异常) ├── java.sql.SQLException : 数据库操作失败(受检异常) └── 自定义异常(如:MyException)
1. Exception
1.1 自定义异常
可以根据需要定义自己的异常类,通常继承 Exception(受检)或 RuntimeException(非受检)。
// 自定义受检异常public class MyException extends Exception { public MyException(String message) { super(message); }}// 自定义运行时异常public class MyRuntimeException extends RuntimeException { public MyRuntimeException(String message) { super(message); }}
1.2 RuntimeException
运行时异常。
1.2.1 ArithmeticException 算术异常
数学运算中出现异常情况 时抛出,例如 整数除以零 或 取模运算时除数为零。
浮点数除0不会抛异常,返回Infinity。
/** * ArithmeticException: 算术异常 */public static void test_ArithmeticException() { try { // 除 0 System.out.println(10/0); } catch (ArithmeticException e) { // java.lang.ArithmeticException: / by zero e.printStackTrace(); } // 浮点数除0不会抛异常!!! double result = 10.0 / 0.0; System.out.println(result); // Infinity}
1.2.2 ArrayStoreException 数组存储类型不匹配
将一个类型不兼容的对象插入到数组中时,在运行时抛出 ArrayStoreException。
/** * ArrayStoreException: 数组存储类型不匹配 */public static void test_ArrayStoreException() { try { Object[] strings = new String[3]; strings[0] = \"hello\"; // 尝试将一个非 String 类型的元素放入数组 strings[1] = 123; } catch (ArrayStoreException e) { // java.lang.ArrayStoreException: java.lang.Integer e.printStackTrace(); }}
1.2.3 ClassCastException 类型转换异常
将一个对象转换为它不兼容的类型时,Java 就会在运行时抛出 ClassCastException。
/** * ClassCastException: 类型转换异常 */public static void test_ClassCastException() { try { Object obj = new Integer(10); // 尝试向下转型为不兼容的类型 String str = (String) obj; System.out.println(str); } catch (ClassCastException e) { // java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String e.printStackTrace(); }}
1.2.4 IllegalArgumentException 类型转换异常
在 方法接收到非法或不合适的参数值 时抛出,表示传入的参数不符合方法的预期要求。
/** * IllegalArgumentException: 非法参数 */public static void test_IllegalArgumentException(int i) { try { if(i > 5) { // 手动抛出 throw new IllegalArgumentException(\"传入参数值不能超过5\"); } } catch (IllegalArgumentException e) { // java.lang.IllegalArgumentException: 传入参数值不能超过5 e.printStackTrace(); }}
1.2.5 NumberFormatException 字符串转数字失败
将字符串转换为数值类型(如 int、double 等)失败时抛出。
/** * NumberFormatException: 字符串转数字失败 */public static void test_NumberFormatException() { try { String str = \"123abc\"; Integer.parseInt(str); } catch (NumberFormatException e) { // java.lang.NumberFormatException: For input string: \"123abc\" e.printStackTrace(); }}
1.2.6 ArrayIndexOutOfBoundsException 数组越界异常
访问数组元素时使用了非法索引(负数或大于等于数组长度的值) 时抛出。
/** * ArrayIndexOutOfBoundsException: 数组越界异常 */public static void test_ArrayIndexOutOfBoundsException() { try { Integer[] integers = new Integer[5]; integers[5] = 0; } catch (ArrayIndexOutOfBoundsException e) { // java.lang.ArrayIndexOutOfBoundsException: 5 e.printStackTrace(); }}
1.2.7 StringIndexOutOfBoundsException 字符串越界异常
访问字符串中字符时使用了非法索引(负数或大于等于字符串长度) 时抛出。
/** * StringIndexOutOfBoundsException: 字符串越界异常 */public static void test_StringIndexOutOfBoundsException() { try { String s = \"12345\"; s.charAt(5); } catch (StringIndexOutOfBoundsException e) { // java.lang.StringIndexOutOfBoundsException: String index out of range: 5 e.printStackTrace(); }}
1.2.8 NullPointerException 空指针异常
尝试在 一个为 null 的对象引用上调用方法或访问属性 时,就会抛出 NullPointerException。
/** * NullPointerException: 空指针异常 */public static void test_NullPointerException() { try { String s = null; s.length(); } catch (NullPointerException e) { // java.lang.NullPointerException e.printStackTrace(); }}
1.2.9 SecurityException 安全限制阻止操作
程序试图执行某些受安全管理器限制的操作时抛出。
/** * SecurityException: 安全限制阻止操作 */public static void test_SecurityException() { // 设置一个安全管理器 System.setSecurityManager(new SecurityManager()); try { String property = System.getProperty(\"user.name\"); System.out.println(property); } catch (SecurityException e) { // java.security.AccessControlException: access denied (\"java.util.PropertyPermission\" \"user.name\" \"read\") e.printStackTrace(); }}
1.3 IOException
在进行文件读写、网络通信、流操作等 I/O 操作时抛出。
1.3.1 FileNotFoundException(受检异常)
试图打开一个不存在的文件 或 路径无效 时抛出的异常。
- FileNotFoundException:
- InterruptedIOException: I/O 操作被中断(线程被中断)时抛出。
/** * IOException: IO异常(受检异常) */public static void test_IOException() { // 设置一个安全管理器 try { FileReader fileReader = new FileReader(\"file.txt\"); int read = fileReader.read(); while(read != -1) { System.out.println((char)read); read = fileReader.read(); } fileReader.close(); } catch (IOException e) { // FileNotFoundException 文件不存在异常 // java.io.FileNotFoundException: file.txt (系统找不到指定的文件。) e.printStackTrace(); }}
1.3.2 InterruptedIOException
I/O 操作被中断(线程被中断)时抛出。
/** * InterruptedIOException: I/O 操作中断 */public static void test_InterruptedIOException() { Thread ioThread = new Thread(() -> { try (InputStream is = new PipedInputStream()) { System.out.println(\"开始读取...\"); is.read(); // 阻塞等待输入 } catch (InterruptedIOException e) { System.out.println(\"I/O 操作被中断: \" + e.getMessage()); } catch (IOException e) { // java.io.IOException: Pipe not connected e.printStackTrace(); } }); ioThread.start(); // 中断线程 ioThread.interrupt();}
1.4 反射相关异常
1.4.1 ClassNotFoundException 找不到类(受检异常)
该异常通常在 使用类名字符串动态加载类(如通过 Class.forName())时抛出。
/** * ClassNotFoundException: 找不到类 */public static void test_ClassNotFoundException() { try { // 尝试加载一个不存在的类 Class<?> clazz = Class.forName(\"com.example.NonExistentClass\"); } catch (ClassNotFoundException e) { // java.lang.ClassNotFoundException: com.example.NonExistentClass e.printStackTrace(); }}
1.4.2 IllegalAccessException 访问权限不足(受检异常)
在运行时通过反射机制尝试访问类、方法、字段等成员时,如果该成员的访问权限不足(例如是 private),并且没有通过 setAccessible(true) 显式开启访问权限,就会抛出该异常。
/** * IllegalAccessException : 访问权限不足 */public static void test_IllegalAccessException() { class Person { private String name = \"Alice\"; } try { Person person = new Person(); Field field = person.getClass().getDeclaredField(\"name\"); // 尝试获取私有字段的值,但未设置可访问 System.out.println(field.get(person)); // 抛出 IllegalAccessException } catch (NoSuchFieldException | IllegalAccessException e) { // java.lang.IllegalAccessException: Class com.lkm.test.Test_Throwable can not access a member of class com.lkm.test.Test_Throwable$1Person with modifiers \"private\" e.printStackTrace(); }}
1.4.3 InstantiationException 无法实例化类(受检异常)
通常在使用 反射机制 创建类的实例时抛出,尤其是在调用 Class.newInstance() 或 Constructor.newInstance() 时,如果目标类无法被实例化,就会抛出这个异常。
- 类是抽象类(abstract):抽象类不能直接实例化
- 类是接口(interface):接口也不能直接实例化
- 没有无参构造函数:如果类没有默认构造函数(0个参数),而你使用 Class.newInstance()
- 构造函数抛出异常:构造函数执行过程中抛出了异常,也会导致此异常
1.4.4 NoSuchFieldException 字段不存在(受检异常)
通常在使用 反射(Reflection) 机制访问类的字段(成员变量)时抛出,表示你尝试访问的字段在目标类中 不存在。
/** * NoSuchFieldException : 字段不存在 */public static void test_NoSuchFieldException() { class Person { private String name; public int age; } try { Class<?> clazz = Person.class; Field field = clazz.getField(\"gender\"); // 尝试访问不存在的 public 字段 } catch (NoSuchFieldException e) { // java.lang.NoSuchFieldException: gender e.printStackTrace(); }}
1.4.5 NoSuchMethodException 方法不存在(受检异常)
通常在使用 反射(Reflection) 机制访问类的方法时抛出,表示你尝试调用的方法在目标类中 不存在,或者参数类型不匹配。
/** * NoSuchMethodException : 方法不存在 */public static void test_NoSuchMethodException() { class Person { public void sayHello() { System.out.println(\"Hello\"); } } try { Class<?> clazz = Person.class; Method method = clazz.getMethod(\"sayGoodbye\"); // 方法不存在 } catch (NoSuchMethodException e) { // java.lang.NoSuchMethodException: com.lkm.test.Test_Throwable$1Person.sayGoodbye() e.printStackTrace(); }}
1.5 CloneNotSupportedException 不允许克隆(受检异常)
该异常通常在调用 Object.clone() 方法时,如果对象的类 没有实现 Cloneable 接口,就会抛出。
// class Person implements Cloneable// 没有实现 Cloneable 是不允许调用 clone 进行克隆的static class Person { String name; public Person(String name) { this.name = name; } @Override public Person clone() throws CloneNotSupportedException { return (Person) super.clone(); }}/** * CloneNotSupportedException : 不允许克隆 */public static void test_CloneNotSupportedException() { Person p1 = new Person(\"Alice\"); try { Person p2 = (Person) p1.clone(); } catch (CloneNotSupportedException e) { // java.lang.CloneNotSupportedException: com.lkm.test.Test_Throwable$Person e.printStackTrace(); }}
1.6 SQLException 数据库操作失败(受检异常)
用于处理 数据库访问错误 的一个 受检异常(Checked Exception),它通常在使用 JDBC(Java Database Connectivity)与数据库交互时抛出。当执行 SQL 语句、连接数据库、处理结果集等操作失败时,JDBC 驱动会抛出 SQLException。
- 数据库连接失败:错误的 URL、用户名、密码
- SQL 语法错误:SQL 语句拼写错误、表名不存在
- 数据库服务未启动:数据库服务器宕机或无法访问
- 权限不足:当前用户没有执行该操作的权限
- 网络问题:数据库服务器无法连接(超时)
- 数据类型不匹配:插入或查询时类型不匹配
- 主键冲突:插入重复主键
- 空指针访问:使用已关闭的 Connection、Statement、ResultSet
/** * SQLException : 数据库操作失败 */public static void test_SQLException() { String url = \"jdbc:mysql://localhost:3306/nonexistentdb\"; // 错误的数据库名 String user = \"root\"; String password = \"wrongpassword\"; // 错误密码 try { Connection conn = DriverManager.getConnection(url, user, password); } catch (SQLException e) { System.out.println(\"SQLException: \" + e.getMessage()); // SQLException: No suitable driver found for jdbc:mysql://localhost:3306/nonexistentdb System.out.println(\"SQLState: \" + e.getSQLState()); // SQLState: 08001 System.out.println(\"Error Code: \" + e.getErrorCode()); // Error Code: 0 // java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/nonexistentdb e.printStackTrace(); }}
2. Error
2.1 VirtualMachineError
JVM 出现严重问题。
2.1.1 OutOfMemoryError 内存不足
表示 Java 虚拟机(JVM)在运行过程中无法分配对象,因为内存不足,并且垃圾收集器已经尝试回收内存但仍然没有足够的空间。
// 内存泄漏// 最后报错 Caused by: java.lang.OutOfMemoryError: Java heap spacepublic static void test_OutOfMemoryError() { List<byte[]> list = new ArrayList<>(); while (true) { list.add(new byte[1024 * 1024]); // 每次分配1MB }}
2.1.2 StackOverflowError 栈溢出
通常发生在程序递归调用过深或调用栈溢出的情况下,也就是说,程序调用了太多方法而没有返回,导致 JVM 的调用栈空间不足。
// 最后报错 Caused by: java.lang.StackOverflowErrorpublic static void test_StackOverflowError() { System.out.println(\"Recursion\"); test_StackOverflowError(); // 无限递归}
2.1.3 UnknownError
未知的虚拟机错误。
- JVM 内部错误:JVM 在运行过程中遇到不可恢复的内部错误。
- 本地方法调用失败:通过 JNI(Java Native Interface)调用的本地代码(如 C/C++)出现了严重错误。
- 资源耗尽但未归类为 OutOfMemoryError:某些资源(如文件句柄、线程、内存映射等)耗尽,但 JVM 没有为该错误定义特定的 Error 类型。
- JVM bug:在极少数情况下,可能是 JVM 本身的 bug 导致抛出该错误。
2.2 LinkageError
类依赖问题。
2.2.1 ClassFormatError 类文件格式错误
表示 JVM 试图读取一个 .class 文件,但该文件的格式不符合 Java 类文件的规范。这通常发生在类文件损坏、被非法修改,或者是由不兼容的编译器生成的情况下。
// 最后报错 java.lang.ClassNotFoundException: InvalidClasspublic static void test_ClassFormatError() { try { // 尝试加载一个非法格式的类文件 ClassLoader.getSystemClassLoader().loadClass(\"InvalidClass\"); } catch (Throwable t) { t.printStackTrace(); }}
2.2.2 NoClassDefFoundError 类在编译时存在,运行时找不到
表示 JVM 在编译时能找到某个类,但在运行时找不到该类的定义。这通常是由类路径(classpath)配置错误、依赖缺失或类加载失败引起的。
2.2.3 UnsupportedClassVersionError JVM 版本不支持该类版本
表示 JVM 试图加载一个类文件,但该类文件是由更高版本的 Java 编译器编译的,当前 JVM 无法识别或支持该类文件的版本。
类文件由高版本 JDK 编译,但运行在低版本 JVM 上
例如:使用 JDK 17 编译 .class 文件,但在 JRE 8 上运行。
JVM 版本与类文件版本不兼容
每个 Java 版本都有一个对应的类文件主版本号(major version),JVM 只能加载它支持的类文件版本。
2.3 ThreadDeath 线程被终止
表示一个线程被强制终止(通过调用 Thread.stop() 方法)。这个类的设计目的是为了在 Thread.stop() 被调用时抛出一个特殊的 Error,以表明线程是被外部强制终止的,而不是正常退出。
在 Java 的早期版本中,Thread.stop() 方法曾被用来立即终止一个线程的执行。为了表示线程不是正常退出,JVM 会抛出一个 ThreadDeath 异常(它是 Error 的子类),并允许线程通过 try-catch 捕获它。
现在已经不推荐使用 Thread.stop() 了,因此了解一下即可。
// 最后抛出 ThreadDeath,打印了 Caught ThreadDeath, doing cleanup...public static void test_ThreadDeath() { Thread t = new Thread(() -> { try { while (true) { System.out.println(\"Thread is running...\"); Thread.sleep(1000); } } catch (ThreadDeath td) { System.out.println(\"Caught ThreadDeath, doing cleanup...\"); // 做一些清理工作 throw td; // 重新抛出以确保线程真正终止 } catch (InterruptedException e) { e.printStackTrace(); } }); t.start(); try { Thread.sleep(3000); // 主线程等待3秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(\"Stopping thread...\"); t.stop(); // 强制终止线程,抛出 ThreadDeath}
2.4 ExceptionInInitializerError 静态初始化出错
表示在类初始化过程中发生了异常。这个错误通常发生在类的静态初始化块(static initializer block)或静态变量初始化时抛出了异常。
static { if (true) { throw new RuntimeException(\"Static initializer failed!\"); }}// Exception in thread \"main\" java.lang.ExceptionInInitializerError// Caused by: java.lang.RuntimeException: Static initializer failed!public static void test_ExceptionInInitializerError() { System.out.println(\"This will not be printed.\");}