异常:
在程序执行过程中,出现非正常的情况,最终会导致JVM的非正常停止。
异常本身就是一个类,产生一个异常就将异常信息封装到对应的异常类。然后我们需要对该异常进行抛出或者抓取处理。
可以通过打印异常信息,来快速的找到程序的问题
异常体系:
在IDEA中选中一个类,Ctrl +H 查看该类的继承体系
Error演示:
RunTimeException演示:
我的一个疑惑:上边讲了Error是无法通过程序解决的。那么Error演示中的内存溢出问题里,我将数组的长度减少,这样不是就解决了这个问题吗。这样的想法是错误滴。程序的功能就是创建长度为1024 *1024**10000长度的数组,你不可以改变这个程序的功能的前提下,Error的内存溢出就是无法解决的了。
编译异常演示:
编译异常会爆红,要处理。否则编译不通过,会提示
翻译插件:
对于像我这样英语不好,且是初学java的同学来说,一些英文报错根本看不懂。解决方法有两种:一种是将IDEA汉化,这样报错会是中文。效果看上边编译异常演示中的图片。方法二下载Translation插件,这个是选中IDEA中的英文,然后可以翻译。
JVM默认处理异常的方式:
1.封装异常信息到该异常对应的异常类,打印异常信息到控制台。
2.程序终止。
产生异常时的处理过程(重点概念,要理解这个过程)
异常的手动处理:
在编写程序时,我们必须要考虑程序出现问题的情况(*异常的提前预判*)。
Java中异常处理的五个关键字:throw throws try catch finally
throw使用格式:throw new 异常类名称(参数);
这里的参数是用来自定义异常的报错信息的。来看这个异常类的源码,通过不同构造方法实现此功能。
throws:表示当前方法不处理方法中出现的异常,交给这个方法的调用者。如果方法中使用throw抛出了编译期异常,而没有捕获处理(后面讲),那么必须通过throws 进行声明,让调用者处理。有些我们不想处理的运行时异常,我们也可以使用throws 抛出给调用者。
手动捕获处理异常:try…catch…finally
try{
可能出现异常的代码
}catch (异常 A){ //当try中出现A类型异常,就使用catch捕获
处理异常的代码
}catch(异常 B){ //当try中出现B类型异常,就使用catch捕获
处理异常的代码
}
当多个同级异常的处理方法相同时,可以写在一个catch内。不同级时报错如下
多个catch时需要注意:
1)平级异常:抛出的异常类之间,没有继承关系,没有顺序。
2)父子关系的异常:越高级的父类,越写在下面。
finally代码块:
finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。
什么时候的代码必须最终执行?
当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等) ,我们都得在使用完之后,最终关闭打开的资源。
finally的语法: try…catch…finally:自身需要处理异常,最终还得关闭资源。
注意:finally不能单独使用。
比如在我们之后学习的IO流中,当打开了一个关联文件的资源,最后程序不管结果如何,都需要把这个资源关闭掉。
finally执行不到的情况
- 在执行try或catch块的代码时产生了无法被捕获的错误,比如线程死锁或虚拟机崩溃。
- 在try块中调用了System.exit()方法,导致Java虚拟机终止运行。
- 在try块中发生了死循环或者其他无限循环,导致程序无法正常离开try块。
- 当try块中的代码包含了无限循环时,且循环内没有让出线程的方式(例如使用Thread.sleep()),导致程序无法进入finally块。
public static void main(String[] args) {
try {
*read*("b.txt");
System.*out*.println("上边出异常,就跳到了catch,我不会被执行");
} catch (FileNotFoundException e) {
// 抓到的是编译期异常,抛出的是运行期异常
throw new RuntimeException();
}finally {
System.*out*.println("这里的代码,无论如何都会执行!");
}
System.*out*.println("手动捕获了异常,但是catch中又抛出了新的异常,新异常会到jvm,所以程序终止,这里执行不到");
}
public static void read(String fileName) throws FileNotFoundException {
if(!fileName.equals("a.txt")){ //equals 比较字符串内容是否相等
// 假设在判断的是文件是否存在,不存在抛出异常
throw new FileNotFoundException(); //这是一个编译时异常
}
}
执行结果:
两种输出结果是因为异常的报错输出和程序代码的执行不是一个线程。
public static void main(String[] args) {
try {
*read*("b.txt");
System.*out*.println("上边出异常,就跳到了catch,我不会被执行");
} catch (FileNotFoundException e) {
System.*out*.println("这里做手动异常处理,不交给jvm的默认异常处理");
}finally {
System.*out*.println("这里的代码,无论如何都会执行!");
}
System.*out*.println("手动处理了异常,程序不会终止,这句可以执行到");
}
异常在方法重写时的注意点:
子类重写父类方法,如果父类声明异常,那么子类只能声明与父类相同的异常或者父类方法声明异常的子类,或者不抛出异常。
如果是编译期异常,父类的方法没有异常声明,子类覆盖时也无法声明异常。
如果是运行期异常,父类的方法没有异常声明,子类覆盖时可以声明异常。
自定义异常类:
1)自定义编译期异常类:自定义异常类继承Exception。
2)自定义运行时异常类:自定义异常类继承 RuntimeException。
自定义为运行时异常,那么抛出该异常可以不处理,默认交给jvm。
自定义为编译时异常,那么抛出该异常必须手动处理,或者抛给上层调用者。上层都不处理,才交给jvm。
自定义异常类继承不到父类的构造方法,所以要有自己的构造方法。
Throwable类中定义了一些查看方法,继承它的子类可以使用:
public String getMessage() :获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。
public String toString():获取异常的类型和异常描述信息(很少用)。
public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
述信息,原因(提示给用户的时候,就提示错误原因。
public String toString():获取异常的类型和异常描述信息(很少用)。
public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。