Java异常
异常表示程序执行中发生的错误事件,它们可以是由程序自身的逻辑错误引起,也可以是由外部因素(如用户输入、硬件故障或系统资源限制)引起。
在Java中,异常是一个类的实例,这些类继承自java.lang.Throwable
类。
Java的异常处理机制允许程序在遇到异常时进行适当的处理,从而保证程序的稳定性和可靠性。
异常的类型
通常我们说的Java异常包括了Throwable
类的两个子类::Error
(错误) 和 Exception
(异常)
Error
Error
是异常处理的一个分类,它代表了一些严重的错误条件,通常是由虚拟机本身或者系统环境引起的,而不是程序的逻辑错误。
Error
类和它的子类通常表示程序无法恢复的错误,这意味着遇到这些错误时,程序通常会终止运行。
Error
错误是由虚拟机的控制的,在编写代码时无法抛出Error
类型的异常。
我们不需要在程序中捕获这些错误。如果捕获了Error
,程序也不会继续执行。
常见的Error
子类的例子
OutOfMemoryError
当程序试图分配的内存超过了虚拟机允许的最大内存时,将抛出OutOfMemoryError
。
public class OutOfMemoryExample {
public static void main(String[] args) {
// 尝试创建一个非常大的数组,可能会耗尽所有的可用内存
int[] largeArray = new int[1000000000];
}
}
如果虚拟机没有足够的内存来分配这个数组,将会抛出OutOfMemoryError
。
StackOverflowError
当程序的调用栈深度超过虚拟机限制时,将抛出StackOverflowError
。
public class StackOverflowExample {
public static void main(String[] args) {
stackOverflow();
}
public static void stackOverflow() {
stackOverflow(); // 递归调用可能导致栈溢出
}
}
在这个例子中,stackOverflow
方法递归调用自身,没有退出点,这将导致调用栈不断增长,最终抛出StackOverflowError
。
NoClassDefFoundError
当虚拟机因为某些原因无法找到类定义时,将抛出NoClassDefFoundError
。
public class NoClassDefFoundExample {
public static void main(String[] args) {
// 假设ClassNotFoundException类在运行时没有被加载到JVM中
new ClassNotFoundException("Test");
}
}
如果ClassNotFoundException
类没有被正确加载,将会在尝试创建对象时抛出NoClassDefFoundError
。
AbstractMethodError
当应用程序试图调用一个抽象方法时,如果该方法的具体实现没有被加载到JVM中,将抛出AbstractMethodError
。
查看代码
public interface MyInterface {
void abstractMethod();
}
public class MyClass implements MyInterface {
// 没有实现抽象方法
}
public class AbstractMethodExample {
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.abstractMethod(); // 尝试调用未实现的抽象方法
}
}
如果MyClass
没有实现abstractMethod
,而是在运行时尝试调用它,将抛出AbstractMethodError
。
IllegalAccessError
当程序试图访问一个不允许访问的类或成员时,将抛出IllegalAccessError
。
查看代码
public class IllegalAccessExample {
public static void main(String[] args) {
// 假设privateClass是一个私有类,无法在当前类中被访问
privateClass pc = new privateClass();
}
}
class privateClass {
privateClass() {
// 私有构造器
}
}
如果privateClass
是一个私有类,并且在非私有类中尝试创建它的实例,将抛出IllegalAccessError
。
我们通常不会捕获这些错误,因为它们通常表示程序无法继续运行的严重问题。
正确的做法是确保程序的设计不会导致这些错误的发生,或者在程序运行时提供足够的信息来诊断和解决这些问题。
Exception
Exception
类及其子类用于表示程序执行中发生的各种异常情况,这些异常情况可能是由程序自身的逻辑错误引起的,也可能是由外部环境引起的。
异常可以分为两大类:
- 运行时异常(RuntimeException):这些异常发生在程序运行时,通常是由程序逻辑错误引起的。运行时异常不需要显式地捕获或声明,除非程序需要在这些异常发生时进行特定的处理。例如,空指针异常(NullPointerException)、数组越界异常(ArrayIndexOutOfBoundsException)和类型转换异常(ClassCastException)等。
- 检查型异常(Checked Exception):这些异常必须在代码中显式地捕获或者通过方法签名声明抛出。它们通常是由外部因素引起的,如文件操作异常(FileNotFoundException)、输入输出异常(IOException)等。
运行时异常
空指针异常 (NullPointerException
)
当你尝试使用一个null
的对象引用进行操作时,就会抛出空指针异常。
public class NullPointerExceptionExample {
public static void main(String[] args) {
String text = null;
int length = text.length(); // 这里会抛出NullPointerException
}
}
数组越界异常 (ArrayIndexOutOfBoundsException
)
当你尝试访问数组的一个不存在的索引时,就会抛出数组越界异常。
public class ArrayIndexOutOfBoundsExceptionExample {
public static void main(String[] args) {
int[] array = {1, 2, 3};
int index = 3;
int value = array[index]; // 这里会抛出ArrayIndexOutOfBoundsException
}
}
类型转换异常 (ClassCastException
)
当你尝试将对象强制转换为不是实例的类时,就会抛出类型转换异常。
public class ClassCastExceptionExample {
public static void main(String[] args) {
Object obj = "Hello, World!";
String str = (String) obj; // 这里会抛出ClassCastException
}
}
检查型异常
文件未找到异常 (FileNotFoundException
)
当尝试打开或读取一个不存在的文件时,就会抛出文件未找到异常。
查看代码
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileNotFoundExceptionExample {
public static void main(String[] args) {
try {
FileReader reader = new FileReader("non_existent_file.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
输入输出异常 (IOException
)
这是在执行输入/输出操作时可能发生的通用检查型异常。
查看代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class IOExceptionExample {
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}
自定义异常
可以通过扩展Exception
类或其子类来创建自定义异常。
public class CustomException extends Exception {
public CustomException(String message) {
super(message);
}
}
在处理异常时,应该遵循最小化异常捕获范围的原则,只捕获和处理程序能够处理的异常。无法处理的异常应该向上抛出或声明抛出。
异常的处理
在Java中,异常的处理通常通过try
, catch
, finally
, 和 throw
语句来实现。
try 块
try
块用于包围可能会抛出异常的代码。如果在这个块中的代码抛出了异常,那么立即停止执行这个块的剩余代码,并搜索匹配的 catch
块。
try {
// 可能会抛出异常的代码
} catch (ExceptionType1 e) {
// 当 ExceptionType1 异常发生时执行的代码
} catch (ExceptionType2 e) {
// 当 ExceptionType2 异常发生时执行的代码
} finally {
// 无论是否发生异常,都会执行的代码
}
catch 块
catch
块用于捕获并处理在 try
块中抛出的异常。一个 catch
块可以捕获多个类型的异常,但是每个 catch
块只能处理一个类型的异常。
catch (ExceptionType e) {
// 处理 ExceptionType 异常的代码
}
finally 块
finally
块用于执行清理工作,无论是否发生异常,finally
块中的代码都会被执行。
即使在try
或catch
块中有return
语句,finally
块仍然会在方法返回之前执行。
如果操作系统或 JVM 在try
或catch
块执行期间发生故障,那么finally
块可能不会执行。
finally
块通常用于关闭资源,如文件、网络连接等。
finally {
// 清理代码,无论是否发生异常都会执行
}
throw 语句
throw
语句用于显式地抛出一个异常。这通常用于在程序中创建自定义的异常情况。
throw new ExceptionType("Exception message");
try-with-resources
Java 7引入了try-with-resources
语句,它可以自动管理资源,确保资源在使用后能够被正确关闭,即使在发生异常的情况下也是如此。这个语句适用于实现了AutoCloseable
或Closeable
接口的资源。
try (Resource res = new Resource()) {
// 使用资源
} catch (Exception e) {
// 处理异常
}