1.异常概述与异常体系结构
1.1.异常概述
a.异常的概念:
- 1.我们在开发的时候,都不太可能把代码写的没有缺陷,在实际与运行的时候,都会遇到一些问题,我们将程序执行中发生的不正常情况称为“异常”(
异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行
),比如客户输入的数据格式错误、读取文件是否存在、网络是否始终保持通畅等
b.异常处理:
- 1.对于这些错误,一般有两种方式处理
- 一是遇到错误啥也不干终止程序
- 再就是在开发程序的时,考虑到错误检测,进行错误消息的提示以及错误处理
c.异常发生时机
- 1.捕获错误最理想的是在编译期间,但是有的错误是只能在运行期才会发生。如除数是0、数组角标越,所以异常就分两类:
- 编译时异常
- 运行时异常
运行时异常
- 1.是指编译器不要求强制处置的异常。 一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。
java.lang.RuntimeException
类及它的子类都是运行时异常。 - 2.对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
编译时异常
- 1.是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常
- 2.对于这类异常,如果程序不处理,可能会带来意想不到的结
1.2.异常的体系结构:
a.粗略的分类:
Java程序在执行过程中所发生的异常事件可分为两类:
a1.Error
:
- 1.Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:
StackOverflowError-栈溢出和OOM-堆溢出
。无法编写针对性的代码进行处理
public class ErrorTest {
public static void main(String[] args) {
//1.栈溢出java.lang.StackOverflowError
main(args);
//2.堆溢出:java.lang.OutOfMemoryError
Integer[] arr = new Integer[1024 * 1024 * 1024];
}
}
a2.Exception
:
- 1.其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:
- 空指针访问
- 试图读取不存在的文件
- 网络连接中断
- 数组角标越界
注意:我们平常将Error和Exception都称作广义上的异常,但是由于我们不处理Error,
所以我们平时在开发中提到的异常一般指的是Exception
,我们说的异常处理指的就是狭义上的异常:Exception,所以此博客主要讲解的异常处理针对的是Exception
b.异常的具体分类:
- 1.异常的顶级父类是
java.lang.Throwable
,其下有两个子类java.lang.Error
java.lang.Exception
2.Exception下的异常:
2.2.常见异常:
a.有哪些常见异常:
b.常见异常举例:
- 1.运行时异常->空指针异常:
- 2.运行时异常->数组角标异常:
- 3.运行时异常-> 类型转换异常
- 3.运行时异常-> 数值类型转换异常
3.异常处理机制:
3.1.概述:
- 1.在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要
检测分母为0,数据为空,输入的不是数据而是字符
等,这样过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。 - 2.Java采用的异常处理机制,
是将异常处理的程序代码集中在一起,与正常的程序代码分开
,使得程序简洁、优雅,并易于维护。其实异常处理并非真正意义上将异常代码改正,修改代码操作还是需要开发人员自己去做! - 3.关于异常对象的产生:
系统自动生成的异常对象
和手动生成一个异常对象,并抛出(throw)
- 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——
自动抛出
- 由开发人员手动创建:
Exception exception = new ClassCastException();
——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样
- 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——
3.2.异常的处理模型:抓抛模型:
- 1.
过程一(抛)
:程序在正常执行过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出。一旦抛出对象以后,其后的代码就不再执行
- 2.
过程二(抓)
:可以理解为异常处理的方式:①try-catch-finally
和 ②throws
4.抓异常处理机制分类
4.1.机制1:try-catch-finally
a.使用格式:
try{
...... //可能产生异常的代码
}
catch(ExceptionName1 e){
...... //当产生ExceptionName1型异常时的处置措施
}
catch(ExceptionName2 e){
...... //当产生ExceptionName2型异常时的处置措施
}
......
finally{
...... //无论是否发生异常,都无条件执行的语句
}
注意:finally为可选结构,是否使用取决于自己
b.代码展示与说明:
public class ExceptionTest {
public static void main(String[] args) {
String str = "123";
str = "abc";
int num = 0;
try {
num = Integer.parseInt(str); //可能出现异常的代码
System.out.println("hello------1");
}catch (NullPointerException e){
System.out.println("出现空指针异常了,不要急");
}catch (NumberFormatException e){
//System.out.println("出现数值转换异常了,不要急");
//System.out.println(e.getMessage());
e.printStackTrace();
}catch (Exception e){
System.out.println("出现异常了,不要急");
}
System.out.println(num);
System.out.println("hello------2");
}
}
c.try-catch-finally说明:
- 1.finally是可选的
- 2.使用try将可能出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,然后根据此对象的类型,去catch中进行匹配,每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
- 3.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理,
一旦处理完成就跳出当前的try-catch结构
(在没写finally情况下),继续执行其后的代码 - 4.catch中的异常类型如果没有子父类关系,则先声明谁都无所谓,catch中的异常类型如果满足子父类关系,则
要求子类一定声明在父类的上面
,否则报错 - 5.常用的异常对象处理的方式:
String getMessage()
:获取异常信息,返回字符串printStackTrace()
: 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void
- 6.在
try中声明的变量
,在出了try结构
后,不能再被调用
- 7.如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数。比 如 : 可 以 用 ArithmeticException 类 作 为 参 数 的 地 方 , 就 可 以 用RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。但不能是与ArithmeticException类无关的异常,如NullPointerException(catch中的语句将不会执行)
- 8.try-catch-finally结构可以嵌套
d.finally
- 1.捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
- 2.不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行。
- 3.finally语句和catch语句是任选的
try-catch-finally处理的是编译时异常,将程序再编译时不报错,但是对于运行时异常可能还会报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
所谓的异常处理一般都是考虑处理编译时异常进行处理
4.2.机制2:throws + 异常类型:
a.说明:
- 1.throws + 异常类型是写在方法的声明处。指明方法执行时,可能会出现的异常类型
- 2.一旦方法执行时,出现异常,仍然会在异常处生成一个异常类对象,此对象满足throws后异常类型时,就会被抛出,异常代码后后续的代码就不再执行了。
- 3.try-catch-finally是真正的把异常给处理掉了,但是trrow的方式只是将异常抛给了方法的调用者,并没有将异常给真正的处理掉
4.3.方法重写的规则:
- 1.子类重写的方法抛出的异常不能大于父类被重写的方法抛出的异常类型
4.4.开发中如何选择使用哪种方式处理异常
- 1.如果父类中被重新给的方法中没有throws方式处理异常,则子类重写方法不能使用throws,意味者如果子类重写方法有异常,那么就只能使用try-catch-finally来处理
- 2.执行的某个方法中,如果调用了其他多个方法,且这几个方法是有递进的相互关系来执行的,那么就建议在这几个被调用的方法中使用throws方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理
5.抛异常的方式:
5.1.异常对象的产生:
- 1.系统自动生成的异常对象;
- 2.手动的生成一个异常对象,并抛出throw;
5.2.手动生成异常对象举例:
面试题:
Throw和Throws有什么不一样
- 在Java中异常处理可以看成两个阶段,可以分为抛出异常和抓取异常,其中抛出异常阶段就可以自定义一个异常对象,并使用Throw抛出这个异常对象,是写在用于方法体内的位置;Throws是抓取异常的阶段,是异常处理的一种方式,还有异常处理的一种方式是使用try-catch-finally;throw和Throws可以同时出现,互相没有什么影响
5.3.用户自定义异常类:
a.如何自定义异常类:
- 1.自定义异常类必需要继承于现有的异常结构:RuntimeException、Exception
- 2.提供全局常量:serialVersionUID
- 3.提供重载的构造器
b.代码实现:
- 1.自定义的异常类:
- 2.抛异常测试: