相关文档:https://www.nowcoder.com/questionTerminal/a4a96e226c7b4dafae8250db4ff88af5?toCommentId=17805
异常的分类:
解释:
所有异常类型都是 Throwable 类的子类,它包含Exception类和Error类;Exception又包括checked exception(已检查异常)和unchecked exception(未检查异常);
Error、RuntimeException及其子类被称为 未检查异常,其它的被称为 已检查异常;
未检查异常也叫作运行时异常,已检查异常也叫作编译时异常;
unchecked exception:Java编译器不要求对未检查异常一定捕获或抛出,可以不做处理;此类异常通常是在逻辑上有错误,可以通过修改代码避免;在eclipse中(保存即编译)编译后此类异常发生处会报错;
checked exception:Java编译器要求对检查异常必须捕获或抛出;代码逻辑没有错误,但程序运行时会因为IO等错误导致异常,你在编写程序阶段是预料不到的;如果不处理这些异常,程序将来肯定会出错;所以编译器会提示你要去捕获并处理这种可能发生的异常,不处理就不能通过编译;
未检查异常不需要我们程序员关心(Error是我们解决不了的;RuntimeException在程序中到处都是,由程序员水平造成的,水平高这种异常就少);我们只需要可以关心对已检查异常的处理;
已检查异常举例:
未检查异常举例:
NullPointerException - 空指针引用异常
ClassCastException - 类型强制转换异常。
IllegalArgumentException - 传递非法参数异常。
ArithmeticException - 算术运算异常
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
IndexOutOfBoundsException - 下标越界异常
NegativeArraySizeException - 创建一个大小为负数的数组错误异常
NumberFormatException - 数字格式异常
SecurityException - 安全异常
UnsupportedOperationException - 不支持的操作异常
FileNotFoundException - 文件未找到异常
对已检查异常进行处理的方式:继续抛出(throws)、捕获(try-catch-finally);
对未检查异常进行处理的方式:继续抛出、捕获、不处理;
Java异常处理机制(throws的实现机制):层层上抛,如果异常没法处理,就会抛给本方法的调用者,一直往上抛,直到被处理;(没被处理)或者最终抛给main函数调用者(JVM),程序停止,打印异常栈信息;
五个关键字:
try:试一试,把可能产生异常的代码包住;
catch:捕获异常,针对两种抛出异常进行捕获:
第一种:JVM产生一个异常对象并抛出,希望得到处理;
第二种:通过 throw new **Exception(); 由程序员自己抛出的异常;
finally:在以下三种情况中都会执行,用于释放资源,肯定会执行:
无异常:try全部 + finally + 正常结尾;
有异常被catch:try开始到出现异常的地方 + 某个catch + finally + 正常结尾;
有异常没被catch住:try开始到出现异常的地方 + finally + 停止;
throw:在方法内部使用,用于主动抛出异常;
throws:声明本方法可能会抛出异常,希望调用者小心;
如果是uncheckedException,(RuntimeException及其子类)调用者可以处理也可以不处理;
如果是checkedException,调用者必须处理;(如何处理:加try-catch;给所在的方法加上throws说明,将异常抛到调用栈的上一层);
一个try可以有多个catch,如果是并列关系,谁上谁下均可;如果有包含关系:先小后大(异常类型的书写顺序必须是子类在前,父类在后);
声明已检查异常:
在遇到以下4中情况时,应该抛出异常:
1.调用一个抛出已检查异常的方法;
2.程序运行过程中发现错误,并且利用 throw 语句抛出一个已检查异常;
3.程序出现错误,会抛出未检查异常时;
4.Java虚拟机和运行时库出现的内部错误;
不要声明Java的内部错误(从 Error 继承的错误);因为任何程序代码都可能会抛出这种错误,而我们对其没有任何控制能力;
也不要声明未检查异常(从 RuntimeException 那继承的异常),这些异常完全在我们控制之内,是因为我们编写程序时出现的错误代码,对于这种异常我们应该致力于修改、完善我们的代码;
一个方法必须声明所有可能抛出的已检查异常;而未检查异常要么不可控制(Error),要么就应该避免(RuntimeException);如果方法没有声明所有可能发生的已检查异常,编译器就会给出一个错误信息;
如果子类覆盖了父类中的一个方法,则子类中该方法声明的已检查异常不能比超类方法中声明的已检查异常更通用(子类方法中可以抛出更特定的异常,或者根本不抛出任何异常);如果超类方法没有抛出任何已检查异常,子类也不能抛出任何已检查异常;
如果类中一个方法声明将会抛出一个异常,而这个异常是某个特定类的实例时,则这个方法就有可能抛出一个这个类的异常,或者这个类的任意一个子类的异常;
如何抛出一个已经存在的异常类:
方法1:
throw new EOFException();
方法2:
EOFException e = new EOFException();
throw e;
如何抛出一个未存在的异常类(自定义异常):
1.定义一个派生于Exception的类,或者派生于Exception子类的类;注意:定义的类应该包含两个构造器,一个是默认的构造器,一个是带有详细描述信息的构造器(超类的Throwable的toString方法将会打印出这些详细信息,这在调试中非常有用);
2.此时就可以实现抛出一个已经存在的异常类;
捕获异常:
如果调用一个抛出已检查异常的方法,就必须对它进行处理,或者将它继续进行传递;
通常,应该捕获那些知道如何处理的异常,而将那些不知道怎么处理的异常继续进行传递;
如果编写一个覆盖超类的方法,而这个方法又没有抛出异常,那么这个方法就必须捕获方法代码中出现的每一个已检查异常;不允许在子类的throw说明符中出现超过超类方法所列出的异常类型范围;
在catch子句中可以抛出一个异常,这样做的目的是改变异常的类型;有一种更好的处理方法(包装技术),可以将原始异常设置为新异常的“原因”:
catch(SQLException e)
{
Throwable se = new ServletException(“database error”);
se.initCause(e);
throw se;
}
当捕获到异常时,就可以通过 Throwable e = se.getCause(); 重新得到原始异常信息;
如果在代码中发生了一个已检查异常,而不允许抛出它,那么我们就可以使用包装技术;我们可以捕获这个已检查异常,并将它包装成一个运行时异常;
finally 子句:
推荐独立使用try/catch 和 try/finally 语句块:
try
{
try
{
}
finally
{
// 只有一个职责:就是确保关闭资源
}
}
catch
{
// 只有一个职责:就是确保报告出现的错误
}
try 语句块中如果有 return 语句,在返回之前,finally 子句中的内容将会被执行;如果 finally 中也有 return 语句,finally中的return语句返回值会覆盖try块中return语句的返回值;
finally中抛出异常的更好的解决方法:使用带资源的try语句:
前提是这个资源是属于一个实现了AutoCloseable接口(或是其子接口:Closeable)的类;
不论这个带资源的语句块如何退出(正常退出或抛出异常),都会将资源进行关闭,原来的异常会重新抛出;
使用带资源的try语句,其本身还可以有catch子句和一个finally子句,这些子句会在关闭资源后执行,但不建议加;
使用异常机制的技巧:
1.异常处理不能代替简单的测试:
因为捕获异常的时间大大超过简单测试的时间;
使用异常的基本规则:只在异常情况下使用异常机制;
2.不要过分地细化异常:
不要将每一条语句分装在一个独立的try语句块中,应该讲整个任务包装在一个try语句块中;
3.利用异常层次结构:
不要只抛出RuntimeException异常;应该寻找更加适当的子类或创建自己的异常类;
不要只捕获Throwable异常,否则会使程序代码更难读,更难维护;
考虑已检查异常与未检查异常的区别;不要因为逻辑错误抛出已检查异常;
将一种异常转换成另一种更加适合的异常时不要犹豫;
4.不要压制异常:
不太理解,应该是:只要可能抛出异常的地方,就进行捕获;
5.在检测错误时,“苛刻”要比放任更好:
在检测错误时,不要担心抛出异常;
6.不要羞于传递异常:
并不是要捕获抛出的全部异常,在适合的场景应该讲异常往外抛而不是捕获;
5和6可以归纳为:“早抛出,晚捕获”;
处理异常经验:
1、处理运行时异常时,通过逻辑去合理规避同时辅助try-catch处理;
2、在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常;
3、对于不确定的代码,也可以加上try-catch,处理潜在的异常;
4、尽量去处理异常,切忌只是简单的调用printStackTrace()去打印输出;
5、具体如何处理异常,要根据不同的业务需求和异常类型去决定;
6、尽量添加finally语句块去释放占用的资源;