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 space
public 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.StackOverflowError
public 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: InvalidClass
public 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.");
}
总结
到此这篇关于Java中错误与异常(Throwable)的文章就介绍到这了,更多相关Java错误与异常Throwable内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
