java中的异常也是一些类,报错的信息就是异常类的对象信息,是人为编写出来的。
异常在Api文档中为Throwable类,异常又分为Exception 异常和Error 错误两种子类,其中Error 和 Exception类下有多种子类来表示错误类型。
特性:父类大范围模糊定义,子类具体定义。
其它子类错误或异常可查阅Api文档;
1.1 异常简介
简介:异常是指程序在执行/编译 时出现的非正常情况,不处理会导致jvm非正常终止,异常分为普通异常和严重错误,普通异常又分为编译时的异常和运行时异常,编译时异常是指编写代码时出现的语法和逻辑错误,语法错误时编译无法通过,无法生成字节码文件,程序是不能执行的,此时就可以编写针对性的代码捕获绕过异常,或者向上抛出让调用者使用针对性的代码捕获绕过异常,逻辑错误只会导致你的结果不正确
1.2 严重错误
1.首先要注意,异常抛出错误类型分为Error类型和Exception类型的错误,其中Error类型的错误为严重错误,比如堆栈内存溢出,资源耗尽等情况, Error类型后缀的错误无法进行捕获绕过处理,需要下去改代码!。
例如:
tip://注意,Error类型中有一个特殊的子类为ThreadDeath线程死亡,它也是Error类型。
2.其他的一般性问题(也就是后缀为Exception类型的错误)可以编写针对性的代码能让jvm继续运行,其实就是编写特定代码,绕过此处错误
Exception非常常见的错误:
1.空指针访问
2.试图读取不存在的文件
3.网络连接中断
4.数组下标越界,String下标越界,集合类下标越界
5.输入输出异常
1.3 Exception的编译时异常和运行时异常
Tip:虽然
Error
是在运行时抛出的,但它即不是运行时异常,也不是编译时异常,Error是Throwable类单独的一个子类,非Exception类
运行时异常是指继承自Exception
类的子类 RuntimeException
类下的异常对象,通常是由于程序逻辑错误导致的,编译器编译时不会强制要求处理它们。
编译时异常是指除RuntimeException
类以外的所有继承自Exception类型
的异常,需要注意的是,RuntimeException类的父类也是Exception类型。所有的Exception类型包括RuntimeException的异常都可以编写有针对性的代码让程序继续执行。
1.编译时异常:在代码编译阶段,如果代码中存在某些特殊功能的代码:例如上传文件,解析日期等等,此时编译器会直接报错。
因为这些特殊功能的代码必须先编写针对性的代码进行捕获处理,然后编译器才会允许执行程序(你不写不让你执行,你大概率不能改源码,因为你要用这些特殊功能就必须进行捕获处理)。
2.运行时异常:即代码运行起来才抛出的异常,通常是由于代码编写不当引起的,运行时抛出的异常在代码编译阶段不会有任何问题,可以先编写针对性的代码进行捕获处理,或者不进行捕获处理也可以直接执行程序(你不写可以执行),如果它抛出了异常,我们再进行捕获处理或者改源码。
典型的运行时异常有:
1.NullPointerException
2.ArrayUbdexOutOfBoundsException
3.ClassCastException类型转换异常
4.NumberFormatException 数字格式异常
5.InputMismatchException 输入不匹配异常
6.ArithmeticException 算数异常
典型的编译时异常有:
1.ClassNotFoundException 类找不到异常
2.FileNotFoundException 找不到文件异常
3.IOException 数据传输异常
1.4异常处理
机制:
捕获:try-catch-finally
抛出:
1.自动抛出
2.手动向上抛出:throws 异常类型
3.throw new 异常类型(自定义异常,翻源码可以观察到自动抛出就是用此方法)
1.4.1 处理方案
处理方案一:抓抛模型 try-catch-finally
1.抛出:异常的抛出分为手动向上抛出异常和自动抛出异常,自动抛出异常指的是程序在执行过程中,一旦出现异常,会自动在出现异常的代码处自动生成对应异常类的对象,并将此对象抛出至调用栈中寻找合适的异常处理代码,如果调用栈内没有合适的异常处理代码,就会传播至调用栈的顶层,一旦异常从调用栈的顶层传播给jvm从Jvm中抛出,就会导致
线程
终止,如果是Error类型的错误,Jvm会终止全部java线程。手动向上抛出异常指的是:在处理异常的过程中使用 throws 异常类型 向上抛出异常给调用者, 让调用者来处理此异常。2.抓(捕获):对于被抛出的对象(自动/手动)进行捕获处理,异常处理过后代码便可以继续执行。
使用场景:在项目中,代码较多,跑一次需要10分钟以上!这时如果出现了空指针异常,重新启动项目一个一个改会显得非常蠢,如果改了依旧抛出异常怎么办?
通过抛出的异常上下定位后找到了出现问题的大概位置,使用try抓到问题,再使用catch创建该异常对象进行捕获防止抛出导致程序停止(可以有多个catch创建多个异常对象),catch可以没有任何内容,但一定要做注释!等所有错误全部找到后再修改源码,加快修复Bug的效率。
//注意如果catch内也抛出了异常,那么除finally以外所有下方的代码都不会执行。
1.3 Exception的编译时异常和运行时异常
Tip:虽然
Error
是在运行时抛出的,但它即不是运行时异常,也不是编译时异常,Error是Throwable类单独的一个子类,非Exception类
运行时异常是指继承自Exception
类的子类 RuntimeException
类下的异常对象,通常是由于程序逻辑错误导致的,编译器编译时不会强制要求处理它们。
编译时异常是指除RuntimeException
类以外的所有继承自Exception类型
的异常,需要注意的是,RuntimeException类的父类也是Exception类型。所有的Exception类型包括RuntimeException的异常都可以编写有针对性的代码让程序继续执行。
1.编译时异常:在代码编译阶段,如果代码中存在某些特殊功能的代码:例如上传文件,解析日期等等,此时编译器会直接报错。
因为这些特殊功能的代码必须先编写针对性的代码进行捕获处理,然后编译器才会允许执行程序(你不写不让你执行,你大概率不能改源码,因为你要用这些特殊功能就必须进行捕获处理)。
2.运行时异常:即代码运行起来才抛出的异常,通常是由于代码编写不当引起的,运行时抛出的异常在代码编译阶段不会有任何问题,可以先编写针对性的代码进行捕获处理,或者不进行捕获处理也可以直接执行程序(你不写可以执行),如果它抛出了异常,我们再进行捕获处理或者改源码。
典型的运行时异常有:
1.NullPointerException
2.ArrayUbdexOutOfBoundsException
3.ClassCastException类型转换异常
4.NumberFormatException 数字格式异常
5.InputMismatchException 输入不匹配异常
6.ArithmeticException 算数异常
典型的编译时异常有:
1.ClassNotFoundException 类找不到异常
2.FileNotFoundException 找不到文件异常
3.IOException 数据传输异常
1.4异常处理
机制:
捕获:try-catch-finally
抛出:
1.自动抛出
2.手动向上抛出:throws 异常类型
3.throw new 异常类型(自定义异常,翻源码可以观察到自动抛出就是用此方法)
1.4.1 处理方案
处理方案一:抓抛模型 try-catch-finally
1.抛出:异常的抛出分为手动向上抛出异常和自动抛出异常,自动抛出异常指的是程序在执行过程中,一旦出现异常,会自动在出现异常的代码处自动生成对应异常类的对象,并将此对象抛出至调用栈中寻找合适的异常处理代码,如果调用栈内没有合适的异常处理代码,就会传播至调用栈的顶层,一旦异常从调用栈的顶层传播给jvm从Jvm中抛出,就会导致
线程
终止,如果是Error类型的错误,Jvm会终止全部java线程。手动向上抛出异常指的是:在处理异常的过程中使用 throws 异常类型 向上抛出异常给调用者, 让调用者来处理此异常。2.抓(捕获):对于被抛出的对象(自动/手动)进行捕获处理,异常处理过后代码便可以继续执行。
使用场景:在项目中,代码较多,跑一次需要10分钟以上!这时如果出现了空指针异常,重新启动项目一个一个改会显得非常蠢,如果改了依旧抛出异常怎么办?
通过抛出的异常上下定位后找到了出现问题的大概位置,使用try抓到问题,再使用catch创建该异常对象进行捕获防止抛出导致程序停止(可以有多个catch创建多个异常对象),catch可以没有任何内容,但一定要做注释!等所有错误全部找到后再修改源码,加快修复Bug的效率。
//注意如果catch内也抛出了异常,那么除finally以外所有下方的代码都不会执行。
printStackTrace() 打印异常的详细信息
getMessage() 获取发生异常的原因
Tip:catch也可以处理编译时异常
3.finally的作用:即使在catch内也出现了异常导致程序终止,或者在catch中有return语句,在finally内被声明的代码依旧会执行,如果finally也出现了异常,那么finally抛出异常的下方代码也全部都不会执行,虽然try、catch、finally中都可以再次嵌套try-catch-finally,但尽量减少这个行为,提高代码的可读性。
//finally语句和catch语句是可选的,但finally不能单独使用。
finally的用途:
1.catch出现了异常,使用finally继续执行代码进行测试
2.一些输入流,输出流,数据库连接和socket的连接等资源在使用完毕以后必须显式地进行关闭操作,因为这些资源仍旧存在指针指向本地文件,所以java的垃圾回收器不会自动回收这些资源,会导致内存泄漏(内存随着不断地运行越来越大)。//重点
只要打开了这些输入输出流,就必须在某一时刻.close关闭它们,如果无法正常地执行到.close(),需要用到try或catch进行捕获,这时就可以使用try或 try,catch 搭配 finally 强行关闭! (注意尽量不要写在try catch内,因为它们一旦发生异常,.close可能无法执行到,最好分开写。)
比如:如果try内可能会出现异常,那么就把.close写到捕获方法外面,或直接写到finally中,不要写在catch和try内,因为如果try没有异常那么catch的.close就失效,try出现了异常时try的close在异常下面close就失效,catch后就会继续执行。
如果try内可能会出现异常,catch内也可能出现异常,将.close写入finally作为最后的保障即可,如果finally也有可能出现异常,此时就需要再次嵌套抛抓模型按照上方规则写入即可。
//catch出现异常的前提是try出现异常,小技巧是try内定义的变量只能在try内可见,可以把变量声明放在try外面,地址赋值放在try内。
//只有输入流这种需要被显式关闭的资源才需要手动.close
//还需要注意下图的小细节:
fis在try上方已被定义成null:
//此处细节是可以为fis.close()增加一个非空判断,防止它再次出现空指针
处理方案2:throws 异常类型1,异常类型2.....
此方法即将异常向上抛出给调用者,让调用者进行处理,可以逐级向上抛出,如下图:
Tip:注意,非常不建议使用图中的向上逐级抛出给主方法的行为!
1.只进行了抛出,没有进行处理
2.图中逐级向上抛出给了主方法,而主方法会抛出给虚拟机!
能正常执行是因为主方法将编译时异常抛给虚拟机了(先让你过),一旦出现异常Jvm就会终止线程,此操作相当于将编译时异常转换成了运行时异常,throws一般用在数据库操作中,推荐在主方法内配合try catch直接解决掉它.
方法重写的要求:查阅面向对象
1.4.2 异常处理的选择方式
1.资源的关闭问题必须考虑try-catch
2.方法的重写如果父类没有throws则子类必须考虑try-catch
3.如果有n个方法是依次递进关系(a方法计算后才会传给b),此时需要使用throws配合try-catch处理异常
1.5 自定义异常
机制:
throw new 异常类型();可以定义一个异常对象,可以加上判断语句来让它达成某些条件时弹出异常,自定义异常也应当考虑如何使用try-catch-finally和throws解决它。
1.6 自定义异常类
自定义异常类是指定义一个全新的异常类型,创建的类需要继承Throwable的子类,你定义的这个全新的异常类型甚至可以是错误!
要求:
1.定义的类必须继承自现有的异常体系
2.可以参考其它异常的构造方法,照搬就行,有构造方法就可以输入提示信息了。但识别类的独有序列版本号serialVersionUID注意不要重复。(I/O流有用)
使用方法:参考自定义异常:throw new 自定义异常类型()
用途:当项目需要这些额外的诉求时就可以创建,最直接的目的就是通过自有的异常类的名字直接判断出出现此异常的原因。
附录
//图中判断一遍后未输入新数据导致结果一直为false,第一次可以输入的原因是System.in会读取用户的输入流,只有next()能清除输入流的原因是nextInt()只有返回下一个标记的功能,具体查看nextInt()和next()源码区别。
一种解决方案是使用next()清除输入流,另一种解决方案是new Scanner转移到循环内每次创建时都让它新建一个输入流。
知识补充:
子类重写的异常方法抛出的类型可以与父类被重写的异常方法抛出的类型相同,或者子类异常方法抛出的类型可以是父类异常方法抛出类型的子类。但是如果父类没有抛出,那子类也不能抛出!
例如:父类抛出的异常类型是RuntimeException那么子类可以与它相同,但子类抛出异常类型也可以是RuntimeException的子类,但是子类的抛出类型不能是RuntimeException的父类,比如Exception。