异常是 Java 程序中经常遇到的问题,一 个异常就是一个 BUG,就要花很多时间来定位异常问题。下面是Java异常类的组织结构。Throwable、IOException、ClassNotFoundException、CloneNotSupportedException及其子异常类表示是程序需要显示捕捉或者抛出的。
异常类
Throwable
Throwable是Java异常的顶级类,所有的异常都继承于这个类。Error,Exception是异常类的两个大分类。
Error
是程序无法处理的错误,表示运行应用程序中较严重问题。大多数的错误与代码编写者执行的操作无关,而是表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。
Exception
是程序本身可以处理的异常。也就是你常见的空指针异常(NullPointerException),数组超出范围异常(IndexOutOfBoundsException)等等。
通常,Java的异常(包括Exception和Error)分为检查异常(checked exceptions)和非检查的异常(unchecked exceptions)。
异常的分别
检查异常
除了RuntimeException与其子类,以及错误(Error),其他的都是检查异常(绝对的大家族)。
检查异常(checked exceptions)
检查异常是编译器要求必须处置的异常。某段代码,编译器要求必须要对这段代码try…catch,或者throws exception,这就是检查异常,也就是说,代码还没运行编译器就会检查你的代码,会不会出现异常,要求对可能出现的异常必须做出相应的处理。
对检查异常的几种处理方式
- 继续抛出,消极的方法,一直可以抛到java虚拟机来处理,就是通过throws exception抛出
- 用try…catch捕获
非检查异常(unchecked exceptions)
RuntimeException与其子类,以及错误(Error)。
非检查异常是编译器不要求强制处置的异常。虽然可能出现错误,但不会在编译的时候检查。但一般不处理,因为很难判断会出什么问题,而且有些异常也无法运行时处理,比如空指针,需要手动的去查找。而且,捕捉异常并处理的代价远远大于直接抛出。
对未检查的异常的几种处理方式
- 捕获
- 继续抛出
- 不处理
运行时异常
对Exception异常进行划分,它可分为运行时异常和非运行时异常。
运行时异常(RuntimeException)
RuntimeException 的异常子类不需要强制性处理,可以选择性处理。
运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过,运行时异常可处理或者不处理。运行时异常一般常出来定义系统的自定义异常,业务根据自定义异常做出不同的处理。
常见的运行时异常如NullPointException、ArrayIndexOutOfBoundsException等。
非运行时异常
非运行时异常是程序必须进行处理的异常,捕获或者抛出,如果不处理程序就不能编译通过,同时 RuntimeException 是 Exception 子类。如常见的IOException、ClassNotFoundException等。
十大深恶痛绝的 Java 异常
1、NullPointerException
空指针异常,操作一个 null 对象的方法或属性时会抛出这个异常。具体看这篇文章:Java 避免空指针的 5 个案例。
2、OutOfMemoryError
内存异常异常,这不是程序能控制的,是指要分配的对象的内存超出了当前最大的堆内存,需要调整堆内存大小(-Xmx)以及优化程序。
3、IOException
IO,即:input, output输入输出,我们在读写磁盘文件、网络内容的时候经常会生的一种异常,这种异常是受检查异常,需要进行手工捕获。
如文件读写会抛出 IOException:
public int read() throws IOException{}
public void write(int b) throws IOException{}
4、FileNotFoundException
文件找不到异常,如果文件不存在就会抛出这种异常。
如定义输入输出文件流,文件不存在会报错:
public FileInputStream(File file) throws FileNotFoundException{}
public FileOutputStream(File file) throws FileNotFoundException{}
FileNotFoundException 其实是 IOException 的子类,同样是受检查异常,需要进行手工捕获。
5、ClassNotFoundException
类找不到异常,Java开发中经常遇到,是不是很绝望?这是在加载类的时候抛出来的,即在类路径下不能加载指定的类。
看一个示例:
public static <T> Class<T> getExistingClass(ClassLoader classLoader, String className) {
try {
return (Class<T>) Class.forName(className, true, classLoader);
}
catch (ClassNotFoundException e) {
return null;
}
}
6、ClassCastException
类转换异常,将一个不是该类的实例转换成这个类就会抛出这个异常。
如将一个数字强制转换成字符串就会报这个异常:
Object x = new Integer(0);
System.out.println((String)x);
7、NoSuchMethodException
没有这个方法异常,一般发生在反射调用方法的时候,如:
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "."
+ name + argumentTypesToString(parameterTypes));
}
return method;
}
8、IndexOutOfBoundsException
索引越界异常,当操作一个字符串或者数组的时候经常遇到的异常。
9、ArithmeticException
算术异常,发生在数字的算术运算时的异常,如一个数字除以 0 就会报这个错。
double n = 3 / 0;
这个异常虽然是运行时异常,可以手工捕获抛出自定义的异常,如:
public static Timestamp from(Instant instant) {
try {
Timestamp stamp = new Timestamp(instant.getEpochSecond() * MILLIS_PER_SECOND);
stamp.nanos = instant.getNano();
return stamp;
} catch (ArithmeticException ex) {
throw new IllegalArgumentException(ex);
}
}
10、SQLException
SQL异常,发生在操作数据库时的异常。
如下面的获取连接:
public Connection getConnection() throws SQLException {
if (getUser() == null) {
return DriverManager.getConnection(url);
} else {
return DriverManager.getConnection(url, getUser(), getPassword());
}
}
又或者是获取下一条记录的时候:
boolean next() throws SQLException{}
它是受检查异常,需要进行手工捕获。