目录
一. 异常概述
1. 什么是异常
异常:是指程序在运行过程中出现的不正常情况
注意:代码写错了(编译期间检查出来的错误),不属于异常的范围
2. java中默认的异常处理机制
java为每个可能出现的异常情况封装了一个类,当出现异常时,会抛出该类的对象,并终止jvm(虚拟机)的运行,当然我们程序员可以预测可能会出现异常,并利用异常处理机制对异常进行捕获,从而使程序能够正常执行完毕,这也是下面要讲的
二. 异常处理
java中提供了一些办法,让程序员对可能出现的异常进行提前的处理,避免后续程序无法执行
1. 方式一
1.1 try关键字
try{
//try中经常写一些可能会出现异常的代码(这个要靠经验)
}
try{}块主要用来检测异常,其中可以写一些可能出现异常的代码(要靠经验),当检测到异常时try块中后续的代码将不会执行,直接跳到catch(){}语句块中捕获该异常
1.2 catch关键字
把try中检测到的异常与catch中的异常类型进行匹配,保证程序可以进行下去,catch块必须紧跟在try块后面,如果匹配成功,继续执行catch块中的内容,如果匹配不成功,则会由jvm将异常信息打印到控制台程序结束,当然,一个try后可跟多个catch来捕获不同类型的异常,但异常的类型必须有子类到父类的顺序写,否则会编译出错
try{
//可能出现异常的代码
}catch(异常类型1){
//对该异常的处理
}catch(异常类型2){
//对该异常的处理
}catch(异常类型3){
//对该异常的处理
}
//后续代码
1.3 finally关键字
finally{}块中的代码不管是发生异常还是没发生异常都会被执行,finally块可写可不写
try{
//可能出现异常的代码
}catch(异常类型1){
//对该异常的处理
}catch(异常类型2){
//对该异常的处理
}catch(异常类型3){
//对该异常的处理
}finally{
//必须要执行的代码
}
//后续代码
下面举三个案例来说明什么是必须会被执行的代码
1.3.1 finally案例一
场景一:异常没有被成功捕获(即catch没有成功匹配到异常),那么jvm会在控制台打印出异常信息,程序被终止,但在这之前,会执行finally中的语句
public class Demo5 {
//场景1 异常没有被捕获到,后面的代码无法执行,但是finally中的代码是可以执行的
public static void main(String[] args) throws IOException {
try{
int num = Integer.parseInt("10a");
}catch (ArrayIndexOutOfBoundsException e){
e.printStackTrace();
}finally{
System.out.println("qqqqqqqqqqqqqqqq");
}
System.out.println("aaaaaaaaaaaaaaa");*/
}
}
可以看到这里虽然发生了数字格式化异常,但是在程序终止前,还是先执行了finally块中的代码
1.3.2 finally案例二
当我们正在读取硬盘上的文件时,如果因为各种原因而发生异常,而我们又必须要执行关闭流的操作,就可以将关闭流的操作写到finally中
//场景2 确保在出现异常的情况下 依然最终把流对象关闭掉
public class Demo5 {
FileInputStream in = null;
try{
in = new FileInputStream("H:/demo.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件找不到异常");
}finally{
if(in!=null){
in.close();
}
}
System.out.println("aaaaaaaaaaaaaa");
}
}
注意:
1.如果将关闭流的操作写到try中,那么很可能在关闭流之前就发生异常,从而导致程序转而执行catch语句而没有关闭流
2.如果将关闭流的操作写到catch()语句中,那么如果程序没有发生异常,则无法执行catch语句,从而无法关闭流
3.如果在try和catch中都写关闭流的操作,虽然可以解决问题,但会造成代码冗余
所以最终解决的方案就是将关闭流的操作写到finally语句块中
1.3.3 finally案例三
当我们在有返回值的函数中写了finally语句块,无论是try中还是catch进行return,也必须在return之前,先执行finally代码块
public class Demo6 {
//场景3 无论是try中还是catch进行return,也必须在return之前,先执行finally代码块
public static void main(String[] args) throws IOException {
test(10,0);
System.out.println("aaaaaaaaaaaaaaaaaaa");
}
public static int test(int a,int b){
try{
int c = a / b;
return c;
}catch (ArithmeticException e){
e.printStackTrace();
System.out.println("算术异常");
return -1;
}finally{
System.out.println("关闭流");//关闭流操作
}
}
}
2. 方式二
2.1 throws关键字
throws用于在一个方法内部可能会出现异常,但是在这个方法内不想用try{}catch{}语句块处理,那么就用throws将该异常抛出,在调用这个方法的地方处理或再抛出,但是最多只能抛到main方法,如果再抛就会抛给jvm,jvm就会终止程序,达不到处理异常想让程序正常往下执行的效果,所以我们一般规定,最多抛到main方法就要用try{}catch{}去处理异常
public class Demo7 {
//main方法
public static void main(String[] args){
//最好在main方法中处理了
try {
methodA();
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("该路径不存在此文件");
}
System.out.println("main");
}
//方法二 methodA()
public static void methodA () throws FileNotFoundException {
methodB();
System.out.println("methodA");
}
/*
用methodB方法模拟一个底层方法
throws 异常类型 声明表示此方法中可能会出现给定的异常,并且该方法不处理异常
谁调用谁处理
*/
//方法三 methodB()可能出现异常
public static void methodB () throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream("H:/demo.txt");
System.out.println("methodB");
}
}
注意:
1. throws后面可以抛出多个异常
2. 任何方法都可以使用throws抛出,包括抽象方法
3.如果抛出的是运行期异常,那么不会有任何提示,需要自己看所调用的方法结构
2.2 throw关键字
throw关键字用于方法内部,当不满足条件时,自己主动的抛出一个异常对象,并通过构造方法将异常原因传给Throwable类的message成员变量,终止该方法的运行
public class Demo10 {
public static void main(String[] args) {
try {
char c = level(101);
} catch (ScoreException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println("qqqqqqqqqqqqqqqq");
}
/*
throws:用在一个方法名后,表示此方法可能存在异常,并且不处理
throw:用在一个方法内部,表示不满足某条件时,抛出一个异常对象,并将出错信息通过构造方法传入,终止当前方法
*/
public static char level(int score) throws ScoreException
{
if(score<0||score>100)
{
throw new ScoreException("非法的分数");//当不满足某条件时,程序中主动的抛出异常对象,终止此方法继续向下进行,通过异常的构造方法传入异常原因
}
if(score>=90&&score<=100)
{
return 'A';
}else{
return 'B';
}
}
}
三. 编译期异常和运行时异常
编译期异常:直接或间接继承Exception类的异常,该类的异常会在编写代码时由编译器显示报错,指出要对其进行处理,eg:FileNotFoundException等
运行期异常:继承了RunTimeException类的异常,该类的异常不会在编写代码时提示,只有当实际运行中导致了该异常,终止了程序,才会在控制台打印,eg:ArrayIndexOutOfBoundsException等
四. 异常体系结构
五. 自定义异常
当java提供的异常类不满足我们的业务需求时,我们需要自己定义一个异常类,来达到让程序在发生不正常现象时可以向用户给一定的提示性信息,使程序有一定的健壮性
一个类要想是异常类,那么只需要继承Exception或RunTimeException,继承编译期异常(Exception),那么该自定义的异常类会在编译时由编译器提示要处理异常,而继承运行时异常则只在运行时发生异常才会输出到控制台,并终止程序运行,需要程序员自己判断并做出相应的处理,这两种异常可以根据自己的需要,继承其中一个即可,继承后一定要重写带String的有参构造,可以将异常信息通过构造方法,传递给父类
public class ScoreException extends Exception{
//根据自己的业务需求自己定义异常类
/*
分数自定义异常
当分数不合法时抛出此类的对象
例如百分制分数,分数小于0大于100时抛出
*/
public ScoreException(String message) {
super(message);
}
}
public class Demo10 {
public static void main(String[] args) {
try {
char c = level(101);
} catch (ScoreException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println("qqqqqqqqqqqqqqqq");
}
public static char level(int score) throws ScoreException
{
if(score<0||score>100)
{
throw new ScoreException("非法的分数");
}
if(score>=90&&score<=100)
{
return 'A';
}else{
return 'B';
}
}
}