认识异常
- 异常的背景
1.1初识异常
除以0、数组下标越界、访问null对象
Eg:
访问null对象
Public class Test{
Public int num = 10;
Public static void main(String[] args){
Test t = null;
System.out.println(t.num);
}
}
//执行结果
Exception in thread “main”=java.lang.NullpointerException
所谓异常,就是指程序在运行时出现错误时通知调用者的一种给机制。有些错误在编译过程中就会出错,这是“编译期”出错。而运行时错误指的是程序已经编译通过得到.class文件了,再由JVM执行过程中出现的错误。
1.2防御式编程
错误在代码中是客观存在的,因此我们要让程序出现问题的时候及时通知程序员,主要有两种主要的方式
LBYL:Look Before You Leap在操作之前就做充分的检查
EAFP:It’s Easier to Ask Forgiveness than Permission 先操作,遇到问题再处理(异常的核心思想)
对比:第一种方式,正常流程和错误处理流程代码混在一起,代码整体显得比较混乱。第二种方式正常流程和错误流程是分离开的,更容易理解代码
2. 异常的基本用法
2.1 捕获异常
语法:
Try{
有可能出现异常的语句;
}[catch(异常类型 异常对象){
}…]
[finally{
异常的出口
}]
try代码块中放的是可能出现异常的代码
catch代码块中放的是出现异常后的处理行为
finally代码块中的代码用于处理善后工作,会在最后执行。无论是否存在异常,finally中的代码一定都会执行到。例如,释放资源。
其中catch和finally都可以根据情况选择加或者不加
调用栈:
方法之间是存在相互调用关系的,这种调用关系我们可以用“调用栈”来描述。在JVM中有一块内存空间称为“虚拟机栈”,专门存储方法之间的调用关系。当代码中出现异常的时候,我们就可以使用e.printStackTrace();的方式查看出异常代码的调用栈。
2.2异常处理流程
程序先执行 try 中的代码
如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
如果找到匹配的异常类型, 就会执行 catch 中的代码
如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
如果上层调用者也没有处理的了异常, 就继续向上传递.
一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.
2.3抛出异常
除了Java内置的类会抛出一些异常之外,程序员也可以手动抛出某个异常,使用throw关键字完成这个操作
Public static void main(String[]args){
System.out.println(divide(10,0);
}
Public static int divide(int x,int y){
if(y==0){
throw new ArithmeticException(“抛出除0异常”);
}
return x/y;
}
//执行结果
Exception in thread “main” java.lang.ArithmeticException: 抛出除0异常
at java01_27.divide(java01_27.java:8)
at java01_27.main(java01_27.java:4)
2.4 异常说明
在处理异常的时候,通常希望知道这段代码中究竟会出现哪些可能的异常。就可以使用throw关键字,把可能抛出的异常显式的标注在方法定义的位置,从而提醒调用者要注意捕获这些异常。
public static int divide(int x, int y) throws ArithmeticException {
if (y == 0) {
throw new ArithmeticException("抛出除 0 异常");}
return x / y;
}
2.5关于finally的注意事项
Finally中的代码保证一定会执行到,这时就要注意:
Finally执行的时机是在方法返回之前(try或者catch中如果有return会在这个return之前执行finally)。但如果finally中也存在return语句,那么就会执行finally中的return,不会执行到try中原有的return。一般不建议在finally中写return(被编译器当作一个警告)
3.Java异常体系
java当中定义了许多异常类,并且定义了基类java.lang.Throwable作为所有异常的超类。Java语言设计者将异常划分为两类:Error和Exception。Throwable:有两个重要的子类:Exception(异常)和Error(错误),两者都包含了大量的异常处理类。
Error:指的是 Java 运行时内部错误和资源耗尽错误. 应用程序不抛出此类异常. 这种内部错误一旦出现,除了告知用户并使程序终止之外, 再无能无力. 这种情况很少出现.此类错误一般表示代码运行时JVM出现问题。通常有Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如说当jvm耗完可用内存时,将出现OutOfMemoryError。此类错误发生时,JVM将终止线程。
这些错误是不可查的,非代码性错误。因此,当此类错误发生时,应用不应该去处理此类错误。
2、Exception(异常):程序本身可以捕获并且可以处理的异常。是我们程序猿所使用的异常类的父类.
Exception这种异常又分为两类:运行时异常和编译异常。
1、运行时异常(非受查异常):RuntimeException类极其子类表示JVM在运行期间可能出现的错误。比如说试图使用空值对象的引用(NullPointerException)、数组下标越界(ArrayIndexOutBoundException)。此类异常属于不可查异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。
2、编译异常(受查异常):Exception中除RuntimeException极其子类之外的异常。如果程序中出现此类异常,比如说IOException,必须对该异常进行处理,否则编译不通过。在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类。
1、受查异常:编译器要求必须处理的异常。正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除RuntimeException及其子类外,其他的Exception异常都属于可查异常。编译器会检查此类异常,也就是说当编译器检查到应用中的某处可能会此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用throws语句抛出,否则编译不通过。
2非受查异常:编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException极其子类)和错误(Error)。
注意事项
自定义异常通常会继承自 Exception 或者 RuntimeException
继承自 Exception 的异常默认是受查异常
继承自 RuntimeException 的异常默认是非受查异常