1 什么是异常
如果某个方法无法按照正常的途径完成任务,则需要提供另外一种途径退出方法。在这种情况下会抛出一个封装了错误信息的对象。此时,这个方法立即退出不返回任何值。此外,调用这个方法的其他代码也无法继续执行,异常处理机制将代码执行交给异常处理器。
Throwable 是所有异常或错误的父类,其子类有 Error 和 Exception,Exception 的子类有 RuntimeException 和 IOException 。Exception
能被程序本身处理(try-catch
), Error
是无法处理的(只能尽量避免)。
Exception 可以分为运行时异常和编译时异常。
- 运行时异常(RuntimeException):都是非受查异常(此外,还包括Error),在运行过程中才出现,Java编译器不会检查。程序可以选择处理或者不处理。
常见的几个运行时异常:
- NullPointerException(空指针异常)
- ArithmeticException(运算异常)
- ClassCastException(类转换异常)
- ArrayIndexOutOfBoundsException(数组越界异常)
- StringIndexOutOfBoundsException(字符串下标越界异常)
- 编译时异常:除了RuntimeException及其子类以外,其它Exception类及其子类都是受查异常。程序必须进行处理的异常,如果不处理,编译就不能通过。
常见的几个编译时异常:
- FileNotFoundException
- ClassNotFoundException
- SQLException
- NoSuchFieldException
- NoSuchMethodException
2 Java异常类层次结构图
3 Exception和Error的区别
- Error,指Java运行时系统的内部错误和资源耗尽错误。例如,Java 虚拟机运行错误(
Virtual MachineError
)、虚拟机内存不够错误(OutOfMemoryError
)、类定义错误(NoClassDefFoundError
)等 。应用程序不会抛出该类对象。一旦发生这些异常,JVM一般选择线程终止。 Exception,指程序在运行过程中出现了各种意外事件,这些事件可以被Java异常处理机制处理。
4 受查异常和非受查异常
受查异常:除了
RuntimeException
及其子类以外,其他的Exception
类及其子类都属于受检查异常 。常见的受检查异常有: IO 相关的异常、ClassNotFoundException
、SQLException
…- 受查异常必须被
catch
/throw
处理,否则不能通过编译。
- 受查异常必须被
非受查异常:
RuntimeException
及其子类都统称为非受检查异常,例如:NullPointerException
、NumberFormatException
(字符串转换为数字)、ArrayIndexOutOfBoundsException
(数组越界)、ClassCastException
(类型转换错误)、ArithmeticException
(算术错误)等。throws 用于函数外,后面紧跟异常类,可以有多个异常类;throw 用在函数内,只能抛出一种异常类。
- throws 用于声明可能发生的异常,且该异常不一定发生;throw 则是抛出异常,一旦执行则必然发生异常。
二者遇到异常都仅仅抛出,而不是进行处理,真正的处理由函数的上层调用处理。
6 try-catch-finally
try
块: 用于捕获异常。后面可以接零个或多个catch
块,如果没有catch
块,则必须跟一个finally
块。catch
块: 用于处理 try 捕获到的异常,参数为可能的异常类型。finally
块: 无论是否捕获或处理异常,finally
块里的语句都会被执行。当在try
块或catch
块中遇到return
语句时,finally
语句块将在方法返回之前被执行。
注意:
- 当 try 语句块和 finally 语句块中都有 return 语句时,在方法返回之前,finally 语句块的内容将被执行,并且 finally 语句块的返回值将会覆盖原始的返回值。
- finally语句块中无法对try语句块中的return值进行修改。 ```java package org.example;
public class TryCatchTest { public static void main(String[] args) { System.out.println(r(1)); System.out.println(r2(1)); } public static int r(int i) { try { return i; } finally { return i + 1; } } public static int r2(int i) { try { return i; } finally { i++; } } }
输出结果:
```java
2
1
原因:当try语句块中调用了return,则会把需要return的值进行保存,然后再执行finally语句块。执行完finally语句块后,再返回来获取保存的return值进行return。
- 当finally语句块中也有return时,则直接就从finally中return出去了,不会返回去获取之前保存的return进行return。
- 所以:在finally中修改try中return值是不会生效的。因为此时return的值已经被保存了。
在下面三种情况,finally语句块不会被执行:
- 在
try
或finally
块中用了System.exit(int)
退出程序。但是,如果System.exit(int)
在异常语句之后,finally
还是会被执行。 - 程序所在的线程死亡。
- 关闭 CPU。
7 try-with-resources
- 适用范围(资源的定义): 任何实现
java.lang.AutoCloseable
或者java.io.Closeable
的对象 - 关闭资源和 finally 块的执行顺序: 在
try-with-resources
语句中,任何 catch 或 finally 块在声明的资源关闭后运行
《Effecitve Java》中明确指出:
面对必须要关闭的资源,我们总是应该优先使用
try-with-resources
而不是try-finally
。随之产生的代码更简短,更清晰,产生的异常对我们也更有用。try-with-resources
语句让我们更容易编写必须要关闭的资源的代码,若采用try-finally
则几乎做不到这点。
Java 中类似于InputStream
、OutputStream
、Scanner
、PrintWriter
等的资源都需要我们手动调用close()
方法来关闭,一般情况下我们都是通过try-catch-finally
语句来实现这个需求。
在JDK7之后,可以使用try-with-resources来实现自动关闭。多个资源之间用 ; 分隔开。
package org.example;
import java.io.*;
import java.util.Scanner;
public class TryResourcesTest {
public static void main(String[] args) {
tryCatch();
System.out.println("=========");
tryResources();
tryResources2();
}
public static void tryCatch() {
Scanner sc = null;
try {
sc = new Scanner(new File("G:\\javaCode\\demo_code\\demo\\src\\org\\example\\tryResources.txt"));
while (sc.hasNext()) {
System.out.println(sc.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (sc != null) {
sc.close();
}
}
}
public static void tryResources() {
try (Scanner sc = new Scanner(new File("G:\\javaCode\\demo_code\\demo\\src\\org\\example\\tryResources.txt"));) {
while (sc.hasNext()) {
System.out.println(sc.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// 多个资源之间用 ; 隔开
public static void tryResources2() {
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(new File("G:\\javaCode\\demo_code\\demo\\src\\org\\example\\tryResources.txt")));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(new File("G:\\javaCode\\demo_code\\demo\\src\\org\\example\\tryResources2.txt")))
) {
int next;
while ((next = bis.read()) != -1 ) {
bos.write(next);
}
System.out.println("输出完毕!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}