异常总结:
本文总结自传智博客毕老师的java基础视频也包含了作者自己对异常的一些理解
1.为什么要定义异常?
2. 如何从OO角度理解异常?a. 程序的设计者考虑到了程序使用时会存在的问题, JVM也为其开发者考虑了一些可能出现的问题
b. 这些问题是需要被描述,定义以及指示给程序的调用者的, 而且我们希望将潜在的错误在编译时期就充分考虑
c. 潜在的问题如果发生,我们希望知道它是什么, 也需要有一些手段保证其真正发生了有相应的办法可以“处理”,记录
b. 所以,异常作为对程序问题的描述是很有必要被系统的定义的
3.异常可以分成几种,体系结构是什么样的?a. 就像疾病之于人类一样, 这些不正常情况,或者说问题也是一个具体事物,java也用类的形式进行描述,封装对象
b. 而且java对这些程序的“疾病”进行了非常丰富详尽的体系划分和问题描述
c. 随着新的问题的出现,用户也可以根据实际需求继承相应的类来定义自己的异常
d. 将正常的流程代码和问题处理代码相分离,方便于阅读
a. Throwable = Error + Exception(编译时被检测的异常+编译时不被监测的异常RuntimeException)
b. 将异常的父类共性特征抽取定义超类Throwable
c. 有的异常可以处理,有的异常不容易处理;其中可以处理的定义成Exception,不容易处理的定义成Error
d. 对于Error一般不编写针对性的代码处理,而对于Exception一般可以编写代码对异常情况进行处理
4.如何自定义一个异常?
一个需求: 自定义除法除数负数异常(除数为负数,出现异常)
class NegativeDivisorException extends Exception{ // --> a.自定义一个负数异常
private int negativeDivisor;// --> d.
public NegativeDivisorException(String message,int negativeDivisor){
super(message); // --> d. 通过supor调用父类
this.negativeDivisor=negativeDivisor;
}
public int getValue(){ // --> d. 定义一个方法获取异常负数
return negativeDivisor;
}
}
public class NegativeDivisorExceptionDemo { // --> b. myDivision方法声明抛出异常
public static int myDivision(int number,int divisor) throws NegativeDivisorException{
if(divisor<0) // --> b. 需求处抛出
throw new NegativeDivisorException("Divisor cannot be negative",divisor);
return number/divisor;
}
public static void main(String[] args) {
try{ // --> c. 调用时try-catch
myDivision(9,-3);
}catch(NegativeDivisorException e){ // --> c.
System.out.println(e.getMessage());
System.out.println("Divisor: "+e.getValue());
}
}
}
a. 定义一个XxxException类继承Exception
b. 在需求处throw XxxException(); 并且要在此需求的方法上声明这个方法存在的危险(throws XxxException)
c. 调用含有这个新异常的方法时, 就需要进行异常处理了
d. 自定义异常中可以根据需要自定义一些成员变量和方法,也会继承父类中的一些方法,
e. 异常需要被抛出,只有Throwable体系中的成员才具备可抛性,才能使用throw, throws这两个关键字eg. 父类Exception中已经把异常信息的操作完成了,子类只需要在构造时,将异常信息通过super语句传递给父类
6. 异常的处理方式有哪些?
a. try-catch 处理
try{
存在异常隐患的代码,异常发生,异常之后的代码就都不执行了,直接跳入catch
}catch(XxxException e){ //catch住的异常
异常发生时,继续执行的代码,执行至} 出catch程序继续向下执行
}
b. throws 声明 把异常抛给(继续抛给)后面的调用者处理(也是提示使用者调用代码可能存在的问题)
public void foo throws XxxException{ //声明继续抛出
obj.fee();
}
void fee() throws XxxException{ //声明抛出异常
throw new XxxException();
}
c. catch是用于处理异常的,如果没有catch异常就没有被处理,那么就必须生命出去(RuntimeException除外)
7. 所有的异常都需要try-catch处理或throws声明吗?
a. 不是,Runtime异常不需要
b. 如果函数内容抛出Runtime异常,函数上可以不用声明,编译一样通过;
c. 如果在函数上声明了Runtime异常, 调用者不处理, 编译也不会报错
d. 之所以不用声明,申明了不错了也不报错,是因为Runtime异常类的错误会导致后面连锁性的问题,此时,就不应该让程序继续运行而直接停掉程序,只有当手工更改代码使错误消失,程序才能正常运行,所以这类异常被触发了,就不希望程序员try-catch处理
e. 修改,完善程序, 让Runtime异常不存在是很重要的
f. 除了RuntimeException, 其他的异常都是编译时被监测的异常,必须try-catch或throws
g. 在自定义异常时,需要根据d判断是否需要继承RuntimeException
a. String getMessage(); 获取异常信息
b. String toString(); 异常信息的字符串表现形式, 异常名称+异常信息
c. void printStackTrace(); 自动打印输出异常名称,异常信息,异常出现的位置,JVM默认的异常处理机制
9. 为什么要定义finally?
a. finally 中的代码是一定会被执行的,无论异常是否发生
b. 根据a,finally通常是被用做关闭资源的(资源开启--异常出现--catch中结束了程序(eg.抛出了异常) -- 用finally才能关闭资源)
c. finally和try/ try-catch放在一起使用
d. finally在return之前执行,但finally中的代码无法改变返回的结果
e. finally只有一种情况不会被执行 System.exit(0);
10. 应用\处理异常时有那些是需要注意的?
a. 可以有 try-catch; try-catch-finally; try-finally 三种try的搭配模式
c. 调用到抛出异常的功能时,抛出几个就处理几个
c.1 一个try可以对应多个catch
c.2 多个catch,父类的catch放在最下边
b. 异常在子父类中的体现
b.1 子类覆盖父类方法时, 如果父类抛出异常, 那么子类方法要么不抛异常,如果要抛出异常只能是父类异常,或父类异常的子类异常(子类不能比父类问题还多——多态引用优先,保证多态引用时不出问题)
b.2 如果子类覆盖方法真的会存在一个父类被覆盖方法不存在的异常时,就必须内部try-catch而不能throws
b.3 如果父类方法抛出多个异常,那么子类在覆盖方法时,只能抛出父类异常的子集
b.4 如果父类或者接口中没有抛出异常,那么子类在覆盖方法的时候也不可以抛异常,只能try-catch处理
d. catch内需要有针对性的处理方式, 不要简单的定义printStackTrace,输出语句
d.1 当捕获到的异常,本功能处理不了时,可以继续在catch中抛出
try{
throw new AException();
}catch(AException e){
throw e; // --> d.1
}
d.2 如果该异常处理不了,但并不属于该功能出现的异常,可以将异常装换后,在抛出和该功能相关的异常
d.3 或者异常可以处理,但需要将异常产生的和本功能相关的问题提供出去,也可以将捕获异常处理后,转换新的异常
try{
throw new AException();
}catch(AException e){
对AException 先处理在通知调用者BException (比如汇款的例子) --> d.3
throw new BException(); //-->d.2 d.3
}
参考资料: 毕老师Java基础视频 Day9-10 异常相关内容