1.异常:程序执行过程中的不正常情况。
2.异常在表现形式上分两种:
1)、运行时异常 语法没有错误,编译通过了,但是运行起来后程序在控制台报错,并停止运行。 2)、编译时异常 你们还以没有遇到过,他是语法没有错误,但是编译不通过。用鼠标指过去以后,会提示:有未处理的异常,后面跟异常的名字。
3.Trowable是Excepyion和Error的父类,
第二级: Exception,ErrorException -- Error是所有错误类的父类,异常Error -- 错误他们都是我们程序在语法正确的情况下,在运行期或编译期出现的问题。但是两者的严重层度和解决方案不一样。Exception是所有异常的父类 , Exception更多的是指可以用代码去解决的问题;Error往往不是代码级别的问题,往往是运行环境或硬件问题。 Exception和Error都是Throwable的子类,所以他们都可以抛出,当然也都可以catch。
第三层:Exception的子类RuntimeException --- 它和它的子类是运行时异常;--- 也就是在编译期不检查是否处理,没处理的话运行时报错。 非RuntimeException --- 它们和它们的子类是编译时异常;--- 要求编译期必须解决,否则编译不通过。
4、异常发生在哪个线程当中 --- 这个目前不需要,因为单线程;
4-1、发生了什么异常 --- 看异常的名字,对常见异常要逐渐形成经验性的解决方案;不熟悉的要学会查询(APIDOC文档,百度,Google)
4-2、发生在哪里? --- 从上往下找第一行你写的代码 为什么会有多个异常发生位置?这是因为异常的传播机制。在程序运行阶段,一旦发生了异常,那么JVM会自动产生一个对应类型的异常对象,把本次发生异常的基本信息封装进去。然后在当前代码处,查看是否有该异常的处理方案;如果没有,那么JVM会提前结束这个方法,然后带着异常对象返回方法调用处。接着,在方法调用处查看是否有该异常的处理方案,如果没有,那么再结束这个方法,返回它的调用处。如此执行下去,直到main方法如果也没有解决,那么终止main方法,打印异常信息在控制台。而main方法的终止,程序肯定也终止了。
4-3、这个异常的传播机制又告诉了我们额外的两个知识点:
1)、方法结束的方式不仅仅是遇到方法的结束"}" 或 “return”关键字;遇到未处理的异常,方法也会结束。而所有的结束,都是返回方法调用处!
2)、如果要求发生异常,程序不结束,那么我们其实是可以在异常的传播路径的任意位置进行处理的。当然,不同的位置,效果有差异。
5、异常的解决方案 :
1)、通过提前判断,把异常扼杀在摇篮中
2)、逻辑错误,修改代码
3)、try-catch 往往是学得内容不够,无法在运行期去判断或解决问题。
6、try-catch-finally含义
1)、try的含义就是试,所以在try块中就是书写有可能发生异常的代码块,我们试着让它去执行;
2)、try不能够单独存在的,后面必须跟上catch 或 finally;同样catch和finally也是不能单独存在的,必须配上try;
3)、catch的含义就是捕获,catch的圆括号中是表明我们要捕获哪种异常;花括号是用来确定捕获住以后,要做什么。 在catch的圆括号当中,就是一个异常变量的声明;然后一旦发生异常,执行进入catch块,就会拿这个变量去匹配(instanceof)JVM产生的异常对象。匹配上说明这个catch块就是处理这个异常的,没有匹配上,说明这个catch块不是处理它的。 注意:只要匹配上,那么这个异常就算是被抓住了,就不会继续往上传播,程序回到正确逻辑执行。
4)、如果在try的代码中,可能出现多种异常,那么后面是可以接多个catch块的,每个catch捕获一种。 一个try块内部可以有多种异常,但每次只会发生一种,然后停止执行try剩余代码,然后从上往下依次匹配每个catch块,谁匹配住了进入谁内部,然后执行完catch,跳到最后面去。 如果同时有多个catch块,那么这多个catch捕获的exception如果有继承关系,那么必须是先捕获子类,再捕获父类。如果捕获的多个exception没有继承关系,那么谁先谁后就没有关系了。
5)、finally块是用来书写不管是否发生异常,都必须要执行的代码。往往是:资源的回收,清理动作。
⚠️:1、到底书写一个Exception就捕获所有异常,还是分别捕获各种Exception子类,依赖于具体要处理的问题域,新语法可以一个catch捕获多种异常。
try{ ......
}catch(异常类型1|异常类型2 变量名){
}
7、finally、final、finalize的区别:
final 是一个关键字 修饰变量不变,那就是常量;修饰方法不变,那就是不能被重写;修饰类不变,就是不能被继承。 finally 是一个关键字,用来在try-catch后面表示不管是否发生异常都必须要执行的代码块。 finalize 不是关键字,他是方法名;它是Object的一个方法。这个方法是专门用来销毁对象的,由GC调用。
8、finally不管是否发生异常都会被执行,那么能不能不让它执行?比如:在它之前加return?结论:加return也能被执行,代码级别只有System.exit(0)可以阻止finally的执行。那么,它在return之前还是在return之后被执行呢?其实return语句虽然在代码中只是一条语句,但是在底层执行的时候不是一个动作。finally是在return语句已经做好返回准备了以后,被执行,然后再发生真正返回动作。--- 换句话,在finally当中是不能够修改在他之前的return后面的返回值的。
9、try-catch-finally的掌握,除了语法的掌握,更重要的一点是处理完异常以后,要让程序能够回到正确逻辑下继续运行,而不是程序不死就可以了。
编译时异常引出:在现在大家的开发中,异常往往是由JVM在运行时给我们产生的。实际开发中,我们能不能够自己产生一个异常呢? 通过测试我们可以看到,虽然我们能够在代码中自己产生一个异常对象,但是这个异常对象只是安安静静地呆在内存当中,并没有异常传播或者终止程序等效果。 如果想要这些效果,那么我们需要在异常对象的前面加一个throw关键字,这个时候这个异常对象才能够在运行时被纳入到异常的传播机制当中。 但光加throw是不够的,因为这个时候在编译期间,已经通过硬代码明确看到这里会出异常。所以,编译器就要求先提出解决方案,然后再执行。 两种解决方案:1、try-catch;2、throws
对于编译时异常来说更多的是用于底层代码设计人员使用的一种手段,所以大家现在并没有看到它的实际使用。它的存在主要是一个异常到底该谁处理的职责问题。因为底层代码中有一些初始条件来自于外部的调用者(传参),这个数据的正确性不是由底层方法的实现者来控制的。因此如果由这个数据的正确性出现了问题,那么就不应该由底层这个使用者来进行处理,必须通知调用者去检查,从而才设计了抛出这么一个机制的存在。 抛出机制并没有处理异常,仅仅是在编译期强制调用者通过语法去检查明确自己的传入的数据对不对。
目前学习的重点仅限于语法:
1、throw位置:throw是写在方法内部的后面接的内容:throw后面接的是一个异常对象代表的含义:运行期,throw语句被执行的时候,是会真正发生抛出一个异常对象。
2、throws位置:throws是写在方法声明(签名)的最后后面接的内容:throws后面接的是一个或多个异常类的类名代表的含义:编译期,用来警告方法的调用者,本方法有可能会发生某些类型的异常,你要做处理。
3、两者的关联在语法上,如果throw的是一个运行时异常,那么不强制要求写throws;但是如果是一个编译时异常,那么必须写throws。
throws的出现,对我们之前学到知识点是有补充的:1、一个完整的方法声明访问修饰符 可选修饰符 返回类型 方法名(形参列表) throws 异常类型1,异常类型2 2、对方法重写的要求变化了方法名不能变;参数列表不能变;返回类型不能变;重写后的方法访问修饰符不能比重写前小;重写后的方法throws的异常不能比重写前多;这里的多不是指个数,而是范围。
常见异常:1.ArrayIndexOutOfBoundsException数组越界异常
2.ArithmeticException算术异常
3.ClassCastException类型转换异常
4.NullPointerException空指针异常
5.FileNotFoundException找不到文件异常
6.RuntimeException运行时异常
catch执行顺序:在多个catch执行时越具体的放前面,Exception放最后面,Exception是异常的父类 栈(StackOverFlowException),堆(HeapOverFlowException)
try{ //可能会发生的异常
}
catch{
//捕捉异常 //可以多个catch块 //catch块不能随意排列,因为Exception是所有异常的父类,所以Exception排列在最后,子类异常依次排列在Exception前面,这样可以提高安全性
}finally{
//无论是否有异常,最终都会执行 //唯一例外的是,在catch块中exit退出了,finally不执行
}