Java异常体系层次
Java中所有的异常都有一个共同的父类Throwable,它有两个重要的子类Error和Exception
Error
Error是程序无法处理的错误,编译器不做检查,通常是JVM运行时发生的异常情况
常见的Error
- NoClassDefFoundError 类未定义错误
- StackOverflowError 栈溢出错误
- OutOfMemoryError 内存溢出错误
NoClassDefFoundError的成因可能是
- 类加载的class或者jar包不存在
- 类文件存在,但是存在不同的域中
- 大小写问题,javac编译的时候不区分大小写,很有可能编译出来的class文件就与想要的不一样
StackOverflowError 的成因可能是深递归造成的Java虚拟机栈被耗尽而抛出的错误
Exception
Exception分为RuntimeException以及非RuntimeException,又被称为UnCheckedException 和 CheckedException 即非受检异常和受检异常。
顾名思义,RuntimeException 即Java运行时抛出的异常,编译器无法检查出来,所以被称为非受检异常,发现后可以处理。
受检异常通常是Exception的直接子类,而非受检异常是RuntimeException的子类。
常见的RuntimeException
- NullpointException 空指针异常
- ClassCastException 类型强制转换异常
- IllegalArgumentException 传递非法参数异常
- IndexOutOfBoundsExcetption 数组下标越界异常
- NumberFormatException 数字格式错误异常
NumberFormatException 通常发生在String类型转换成int类型的时候发生的异常,可能由于字符串中存在字母,字符,空格等无法转换成数字的就会抛出该异常。
常见的CheckedException
- ClassNotFoundException 找不到指定的Class的异常
- IOException IO异常
ClassNotFoundException 发生的场景可以在反射以及ClassLorder类加载的情况下。
比如Class.forName()这个方法的类路径错误,而找不到那个类。
抛出异常的三种方式
- throw
- throws
- 系统自动抛出
throw
throw是在方法的内部声明,明确声明抛出哪一个异常,即在某种情况下必定抛出异常。
public static void testThrow(){
int a = 1;
if(a == 1){
throw new NullPointerException();
}else {
a = 2;
}
}
public static void main(String[] args) {
testThrow();
}
运行结果
Exception in thread "main" java.lang.NullPointerException
at com.cakemonster.javabasic.throwable.Throw.testThrow(Throw.java:15)
at com.cakemonster.javabasic.throwable.Throw.main(Throw.java:22)
即在某种情况下一定会抛出异常。
throws
throws 在方法上声明,不确定是否在何时抛出异常,但是会在某种可能情况下抛出异常。
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyClassLoader m = new MyClassLoader("C:\\Users\\蜡笔小新\\Desktop\\", "myClassLoader");
Class c = m.loadClass("Wali");
System.out.println(c.getClassLoader());
System.out.println(c.getClassLoader().getParent());
System.out.println(c.getClassLoader().getParent().getParent());
System.out.println(c.getClassLoader().getParent().getParent().getParent());
c.newInstance();
}
该方法在找不到该class文件的时候就会抛出ClassNotFoundException,但是又不确定是否会找到,就是可能会抛出的异常。
Java异常机制
- 异常类型规定了什么异常被抛出
- 异常堆栈跟踪标明了异常在哪里被抛出
- 异常信息表明为什么会抛出异常
即异常机制解决了 what,where,why的三个问题
try-catch-finally
try、catch 和 finally 代码块共同形成了一张捕获异常的网。首先,try 语句限定可能抛出异常的代码。在该例子中,异常直接放在 catch 代码块或异常处理函数 中。在所有尝试和捕获都完成后,会继续执行 finally 代码块,无论是否发生了异常。捕获到异常时,可以尝试优雅地进行恢复,也可以退出程序(或方法)。
关于衔接catch块
分为两种情况,一种是没有catch块,一种是一个或多个catch块
- 没有catch块的时候,就一定要跟上一个finally块。
- 可以拥有多个 catch代码块,但必须采用某种特定方式来搭建它们。如果所有异常都是其他异常的子类,那么子类会按照 catch 代码块的顺序放在父类前面。
@Test
public void exceptionTest() {
Logger l = Logger.getLogger(Employee.class.getName());
File file = new File("file.txt");
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(file));
String line = bufferedReader.readLine();
while (line != null) {
// Read the file
}
} catch (FileNotFoundException e) {
l.severe(e.getMessage());
} catch (IOException e) {
l.severe(e.getMessage());
} catch (Exception e) {
l.severe(e.getMessage());
} finally {
// Close the reader
}
}
在这个示例中,FileNotFoundException 是 IOException 的子类,所以它必须放在 IOException catch 代码块的前面。IOException 是 Exception 的子类,所以它必须放在 Exception catch 代码块的前面。
关于finally的一些问题
- 不管有没有抛出异常,finally中的语句一定会执行
- 当try-catch中有return语句的时候,finally还是会执行
- finally在try或者catch的return语句之后执行,就是说当try-catch块执行完return语句之后,并不会马上返回,而是会继续去执行finally中的代码,当执行完finally之后,才会return。
- finally中最好不要有return语句,这样的话返回出去的就不会是try-catch中的return,而会是finally中的return的内容。
总结,finally块中的代码会在方法返回之前执行,但是在return语句之后执行。
详细可以看这个博客
https://blog.csdn.net/aaoxue/article/details/8535754
在以下4种特殊情况下,finally块不会被执行:
- 在finally语句块第一行发生了异常。 因为在其他行,finally块还是会得到执行
- 在前面的代码中用了System.exit(int)已退出程序。 exit是带参函数 ;若该语句在异常语句之后,-finally会执行
- 程序所在的线程死亡。
- 关闭CPU。
Throwable的一些常用方法
- getMessage() 返回异常发生时的详细信息
- toString() 返回异常发生时的简要信息
- getLocalizedMessage() 返回异常对象的本地化信息,使用Throwable的子类覆盖这个方法,可以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同
- printStackTrace() 在控制台上打印Throwable对象封装的异常信息