画师:竹取工坊
大佬们好!我是Mem0rin!现在正在准备自学转码。
如果我的文章对你有帮助的话,欢迎关注我的主页Mem0rin,欢迎互三,一起进步!
文章目录
一、 异常的体系结构
异常指的是程序中的不正确行为,比如之前遇到的数组越界,空指针异常等。
通常说的异常是Java中的 Exception ,与之并列的是 Error ,指的是JVM无法处理的严重问题,比如JVM内部问题、资源耗尽等,代表是 StackOverflowError 和 OutOfMemoryError。
Exception 和 Error 都是异常体系的顶层类 Throwable 的子类,Exception 又分为编译时异常(受查异常)和运行时异常(非受查异常),受查异常强制要求编译器进行处理,否则无法编译。
再往下分类出更加具体的各种异常,比如上面提到的数组越界异常。
详见下图:

二、异常的处理
throw 关键词
throw 用于人为抛出异常,例如:
java
throw new ArrayIndexOutOfBoundsException("人为抛出数组越界异常");
得到的结果为:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 人为抛出数组越界异常
at TestDemo.main(TestDemo.java:11)
throws 关键词
throws 关键词用于声明该方法可能会产生异常,需要进行处理,相当于是把处理异常的部分交给了方法的调用者。
throws 关键词在定义方法的时候使用,如果需要声明多个异常则用逗号隔开,比如以下实例:
java
public static void ExceptionProducer() throws ArithmeticException, AbstractMethodError {
throw new ArithmeticException("1111");
}
try-catch-finally
产生异常,抛出异常,现在终于到了处理异常的环节了。
try 、catch、final分别对应着处理异常的"运行"、"处理"和"收尾"三个阶段。
例如以下实例:
java
try {
System.out.println("抛出异常");
throw new ArrayIndexOutOfBoundsException("人为异常");
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("已处理数组越界异常");
} finally {
System.out.println("after");
}
结果为:
抛出异常
已处理数组越界异常
after
接下来我们一个一个来讲:
try
try 用于运行可能会出现异常的代码,并且将产生的异常抛出。
需要注意的是,try 内部运行的代码只会到产生异常的部分,如果已经产生了异常,那后面的代码都不会运行,比如以下实例:
java
try {
System.out.println("抛出异常");
throw new ArrayIndexOutOfBoundsException("人为异常");
System.out.println("111");
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("已处理数组越界异常");
} finally {
System.out.println("after");
}
编译器会警告说 System.out.println("111"); 是无法执行的语句。
这对于 throw 关键词也是一样的,在下面的实例中也会被警告说这是无法执行的语句
java
throw new ArrayIndexOutOfBoundsException("人为抛出数组越界异常");
throw new ArithmeticException("111");
catch
catch 用于捕获 try 产生的异常,形式是 catch(异常类型 e) {语句},如果 catch 中的异常类型和抛出的异常类型一样,则会捕获异常类型并运行对应语句。
异常的捕获存在父子类的关系,也就是说抛出的子类的异常可以被父类的 catch 捕获。同时因为一个异常只会被一个 catch 语句捕获,因此如果在捕获父类异常的时候想要单独捕获子类的异常的时候,需要把子类的异常写在前面,就像以下示例:
java
try {
System.out.println("抛出异常");
throw new ArrayIndexOutOfBoundsException("人为异常");
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("已处理数组越界异常");
} catch(Exception e) {
System.out.println("111");
} finally {
System.out.println("after");
}
如果中间两个 catch 写反了就会显示:已捕获到异常错误java.lang.ArrayIndexOutOfBoundsException。
如果 try 运行在不同的情形下可能会产生了多个异常,可以用多个 catch 进行捕获,并进行处理,例如以下实例:
java
int[] array = {1};
try {
//array = null;
int a = array[110];
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("已处理数组越界异常");
} catch(NullPointerException e) {
System.out.println("已处理空指针异常");
} finally {
System.out.println("after");
}
现在抛出的结果是数组越界异常,如果去掉注释就是空指针异常。
如果这两个异常的处理方式相同,还可以采取并列的方式:
java
int[] array = {1};
try {
array = null;
int a = array[110];
} catch(ArrayIndexOutOfBoundsException | NullPointerException e) {
System.out.println("已处理异常");
} finally {
System.out.println("after");
}
finally
用于处理异常的收尾阶段,具有必然运行 的特点,可以用于运行无论程序是否发生异常都需要执行的代码,比如回收资源。
java
public static int getData(){
Scanner sc = null;
try{
sc = new Scanner(System.in);
int data = sc.nextInt();
return data;
}catch (InputMismatchException e){
e.printStackTrace();
}finally {
System.out.println("finally中代码");
}
System.out.println("try-catch-finally之后代码");
if(null != sc){
sc.close();
}
return 0;
}
public static void main(String[] args) {
int data = getData();
System.out.println(data);
}
(懒得调缩进了将就着看一下()
我们会发现finally中代码和try-catch-finally后的代码是有运行的差别的,finally之后的代码可能无法执行,如果资源回收在try-catch-finally后执行的话可能会被跳过。
因此finally的存在是有必要的。
三、自定义异常
在实际使用中,现有的异常可能不完全符合我们的需求,这个时候我们就可以自定义一个异常类型。实例如下:
java
public class NameException extends Exception{
public NameException(String message) {
super(message);
}
}
想要抛出该异常可以:
java
if (name != userName) {
throw new NameException("用户名错误");
}
继承自 Exception 的异常默认为受查异常。
继承自 RunException 的异常默认为运行时异常。
