异常处理机制
异常并非语法错误,若语法错误,编译不通过,不会产生字节码文件,不能运行。异常处理衡量一门语言是否成熟的标准之一。异常处理机制可以让程序有更好的容错性,代码更健壮
C语言没有异常处理机制,所以经常使用特定返回值来表示异常情况,然后使用if语句来判断正常和非正常关系, if-else就是异常处理的一种
没有异常处理机制存在的缺点:
1. 使用方法的返回值来表示异常的情况有限,无法穷举所有的异常情况。
2. 异常流程和正常代码混合在一起,增大了程序的复杂性,可读性不好。
3. 随着系统规模的扩大,程序的可维护性极低(耦合度太高)。
Java异常体系
1. 把不同类型的异常情况描述成不同的异常类
2. 分离异常流程代码和正确流程代码
3. 灵活处理 如何当前方法处理不了,交给调用者处理
非正常情况:
1. error:表示错误,一般指JVM相关的不可修复的代码,如系统崩溃,内存溢出,JVM错误,由JVM抛出,不需要处理
几乎所有的子类都以Error作为类的后缀
2. Exception:表示异常,指程序出现不正常的情况,该问题可以修复(处理异常),几乎所有的子类都是以Exception作为类型的后缀, XXXException
抛出异常
1.使用throw在方法内部,抛出一个异常对象
2.使用throws在方法的声明上,抛出一个异常对象
throw:当一个方法出现未知异常的时候,抛出一个具体的异常类型,和return一样会结束当前方法
1. 用在方法体内,跟的是异常对象名
2.只能抛出一个异常对象名
3.表示抛出异常,由方法体内的语句处理
4.执行throw则一定抛出了某种异常
throws:用来声明一个方法可能产生的所有异常,当前方法不处理异常,而是将异常往上传,谁调用我我就抛给谁。如果每一个方法都放弃异常处理都直接通过throws声明抛出,最后异常会抛到main方法,如果main方法也不处理,就会继续抛出给JVM,底层的处理机制就是打印异常的跟踪栈信息
1.用在方法声明后面,跟的是异常类名,可以跟多个异常类名,用逗号隔开。
2.throws表示出现异常的一种可能性,并不一定会发生这些异常
3.表示抛出异常,由该方法的调用者来处理
//在本例中,异常由方法体内的语句处理
public static void main(String[] args) { int num1,num2; num1 = 10; num2 = 0; System.out.println(divide(num1,num2)); } private static int divide(int num1,int num2) { System.out.println("begin........"); if(num2 == 0) { throw new ArithmeticException("除数不能为0"); } try { int ret = num1/num2; System.out.println("结果" + ret); } catch (ArithmeticException e) { e.printStackTrace(); } System.out.println("end..........."); return 0; }
//除数为0,得到这样的结果:
/*
begin........
Exception in thread "main" java.lang.ArithmeticException: 除数不能为0
at com.tedu.day08.ThrowDemo.divide(ThrowDemo.java:55)
at com.tedu.day08.ThrowDemo.main(ThrowDemo.java:47)
*/ }
捕获和处理异常:
try catch
try catch finally
try finally
以上的异常处理语法结构中
注意:
1. 只有try块是必须的,也就是说如果没有try块,则不可能有后面的catch块和finally块;
2. catch块和finally块都是可选的,但catch块和finally块至少出现其中之一,也可以同时出现;
3. 可以有多个catch块,捕获父类异常的catch块必须位于捕获子类异常的后面;
4. 不能只有try块,既没有catch块,也没有finally块;
5. 多个catch块必须位于try块之后,finally块必须位于所有catch块之后。
public static void main(String[] args) { //使用try-catch捕获异常 /* * 使用try—catch捕获单个异常 (或多个异常) * try{ * 编写可能会出现异常的代码 * }catch(异常类型 e){ * 处理异常的代码1 * //记录日志 打印异常信息 继续抛出异常 * }catch(异常类型){ * 异常处理2 * } * * 1.一个catch语句,只能捕获一种类型的异常,如果捕获多个,就必须使用多个catch语句 * 2.代码在一瞬间只能出现一种类型的异常,只需要一个catch捕获,不可能同时出现多个异常 * try{ * * } * catch(Exception e){//必须放到最后 * } * * try—catch必须连用 * ------------------------------------------- * 如何获取异常信息,使用Throwable类的方法 * 1.getMessage() 获取异常的描述信息,原因提示给用户的时候 就提示错误原因 * * 2.toString() 获取异常的类型和异常描述信息 * * 3.printStackTrace() 打印异常的跟踪栈信息 并且输出都控制台,包含异常的类型,异常的原因还包括了异常出现的位置,在开发和调试阶段都 * 得使用printStackTrace() * * 注意: * 现在catch语句中,必须写e.printStackTrace() * 查看异常位置 信息 */ try { int ret = 10/0; System.out.println("结果" + ret); }catch(Exception e) { System.out.println("异常消息:" + e.getMessage()); System.out.println("异常消息: " + e.toString()); //打印信息到控制台 e.printStackTrace(); } System.out.println("---Throwable类,如何捕获异常信息"); } }
再看看finally的用法
finally
/* * finally 表示一个代码块 * 特点:无论是否有异常,最终都会执行finally代码块 (有特殊情况,JVM退出) * 目的:释放资源 * --------------------------------------------- * 比如当我们的try语句中开了一个物理资源(数据库,网络) * 都得在使用完之后,最终关闭打开的资源 * ---------------------------------------------- * finally * 1.try - finally * 会有catch捕获异常,根据场景,抛出异常,不需要自己处理 * * 2.try - catch - finally * 自身需要处理异常 最终还需要关闭资源 * 注意:finally不能单独使用 * ---------------------------------------------- * 当只有在try 或者 catch 中调用退出JVM的方法 才不会执行finally (System.exit(0)); * 否则finally一定会执行 * ---------------------------------------------------------------------------- * * final修饰符,定义常量 * finally代码块(特点如上) * finalize方法,垃圾回收机制 * * */ public class FinallyDemo { public static void main(String[] args) { // text1(); System.out.println(text2()); // System.out.println(text3()); // // System.out.println(text4()); } private static int text4() {//返回1 int a = 1; try { return a; } finally { return a++; } } private static int text3() {//返回2 int a = 1; try { return a; } finally { return ++a; } } //如果finally语句里面带有return语句 //永远返回的是finally里面的值 //需要避免该情况 private static int text2() { try { System.out.println("aa"); return 1; } finally { System.out.println("bb"); return 100;//finally返回值会把try里面的返回值覆盖 这里返回100 } } private static void text1() { System.out.println("begin....."); try { int ret = 0/0; System.out.println("结果" + ret); } catch (Exception e) { System.out.println("异常"); } finally { System.out.println("关闭资源"); } System.out.println("end......"); }
}
/*
使用finally回收资源
有时候,程序在try块里面打开了一些物力资源(比如数据库连接,网络连接好磁盘文件等),这些物理资源都必须显式回收。因为java的垃圾回收机制不会回收任何的物理资源
垃圾回收机制只回收堆内存中对象所占用的内存。
问题:那么在哪边回收这些物理资源呢?
答:在finally块中,因为如果try块的某条语句引起一场,该语句后的其他语句通常不会被执行,那将导致位于该语句后的资源回收语句得不到执行;如果在catch块里进行资源回收
但catch块完全有可能得不到执行,这也将导致不能及时回收这些物理资源。所以我们不管try块中的代码是否出现异常,也不管哪个catch块会被执行,finally块总会被执行。
那么:java异常处理的完整语法结构如下:
try
{
//业务实现逻辑
...
}
catch(SubException e)
{
//异常处理块1
...
}
catch(SubException2 e)
{
//异常处理块2
...
}
...
finally
{
//资源回收块
...
}
参考:
https://blog.csdn.net/weixin_38011265/article/details/79149313