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++; } } }
输出结果:```java21
原因:当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();}}}
