Throwable类
在我们利用编译工具进行Java程序编写时,通常会遇到各种错误提示我们需要抛出异常,也会遇到编译失败的情况提示我们哪里出现了错误,那么这些异常和错误的提示是来自哪里呢?
在Java的java.lang包中,有一个类叫Throwable,而我们遇到的异常和错误均是Throwable类的子类。
那么什么是异常什么是错误呢?
- 异常Exception:(错了,但问题不大)异常一般情况程序是可以自行处理的,利用catch来捕获或抛出。但并不是所有的异常都需要处理,有时候一些异常是无关紧要的,此时可以不对该类异常进行处理。为了区分这种情况,异常也可以分为两类**:受检查异常(Checked Exception)和不受检查异常(Unchecked Exception)**
- 错误Error:(错了,问题很严重无法编译)错误一般是程序无法自行处理的。出现错误后,一般JVM都会终止线程。
受检查异常与不受检查异常
- 受检查异常:该类异常如果遇到就必须对其进行捕获或者抛出,否则无法编译。一般情况除了RuntimeException及其子类都是受检查异常。
- 不受检查异常:该类在编译过程中即使不对其进行处理仍然能够编译,一般RuntimeException及其子类都是不受检查异常。常见的有:NullPointerException (空指针错误)、ArrayIndexOutOfBoundsException (数组越界错误)、ClassCastException (类型转换错误)、SecurityException (安全错误⽐如权限不够)、NumberFormatException (字符串转换为数字格式错误)等。
那么如何查看异常的相关信息呢?
这里Throwable类提供了几个方法来查看异常信息:
- String getMessage() : 返回异常发⽣时的简要描述
- String toString() : 返回异常发⽣时的详细信息
- String getLocalizedMessage() : 返回异常对象的本地化信息。(这里需要使⽤ Throwable 的⼦类覆盖这个⽅法,如果没有覆盖那么返回的信息与 getMessage() 返回的结果相同)
- void printStackTrace() : 在控制台上打印 Throwable 对象封装的异常信息
那么如何处理这些异常呢?
这里就不得不提到常用的几个处理异常的块:try、catch、finally
- try:可以捕获异常
- catch:可以处理try捕获到的异常
- finally:在该块内的程序,无论是否捕获或者是否处理异常都能执行
当我们利用try捕获异常后,是可以选择接零个或多个catch块的,即可以选择不对异常进行处理也可以进行多个处理。但是在不进行处理(没有catch块时)必须跟一个finally块。finally块一般都是要执行的,**即使在try和catch块中出现return语句也要在返回之前把finally块内的程序执行。**但也正是因为这种机制,所以finally块内不能使用return语句,因为当finally块内有return时,try和catch中的return返回值会被暂时存储在一个本地变量内,此时执行finally内的return后,本地变量内的返回值则变为finally内的return返回值。
前面也说了finally块一般是都要执行的,那么有没有特殊情况呢?有!
- 在执行finally 块之前虚拟机被终⽌运⾏的话,finally 中的代码就不会被执⾏
- 程序所在的线程死亡
- 关闭 CPU
在使用try、catch、finally处理异常时还有一个问题,即很多情况下我们需要对资源进行管理,但是利用try、catch、finally管理会很复杂,代码较乱。
try-with-resourse
try-with-resourse是什么呢?
try-with-resourse是一种可以代替try、catch、finally的更好的管理资源的异常处理机制。例如:InputStream 、 OutputStream 、 Scanner 、PrintWriter 等的资源都需要调⽤ close() ⽅法来⼿动关闭
例如:
/**
* try、catch、finally
*/
public static void test1() {
String filePath = "D:\\test1\\test1.txt";
FileOutputStream fileOutputStream = null;
FileInputStream fileInputStream = null;
try {
fileOutputStream = new FileOutputStream(new File(filePath));
fileInputStream = new FileInputStream(new File(filePath));
// 写
fileOutputStream.write("ab1024".getBytes(Charset.defaultCharset()));
// 读取
int data = fileInputStream.read();
while (data != -1) {
System.out.print((char) data);
data = fileInputStream.read();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭写流
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭读流
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
此时可以看出代码量是很大的,很复杂不易管理,需要在finally块中对多个数据流资源进行关闭处理。
因此可以使用try-with-resourse来改进:
/**
* 自动关闭资源 try-with-resource
*/
public static void test2() throws Exception {
String filePath = "D:\\test1\\test1.txt";
// java7中的try-with-resource,java9中有所改善
try (FileOutputStream fileOutputStream = new FileOutputStream(new File(filePath));
FileInputStream fileInputStream = new FileInputStream(new File(filePath))) {
// 写
fileOutputStream.write("ab1024".getBytes(Charset.defaultCharset()));
// 读取
int data = fileInputStream.read();
while (data != -1) {
System.out.print((char) data);
data = fileInputStream.read();
}
}
}
此时代码得到了明显的优化,在这里可以通过分号声明多个资源,例如上面的 fileOutputStream 与 fileInputStream
注意
最后,处理异常不要时不要把异常声明为静态变量,由于静态变量是存储在栈中的,这样我们下次对新的异常抛出时,可能是从栈中提取,异常会出现错乱现象。当然我们也不能随意抛出异常,一个异常一定要有意义,而且需要具体,⽐如字符串转换为数字格式错误的时候应该抛出NumberFormatException ⽽不是其⽗类 IllegalArgumentException 。(错误需要更具体才能针对性解决) 如果打印了异常日志那么就不需要抛出了。