一、什么是异常
异常: 不正常的情况
概念: 异常指的是在程序的运行过程中发生的不正常的事件
异常体系(异常分类)
Throwable (超类)
error (严重)
Exception(异常)
RuntimeException (运行时异常)
其它(编译时异常)
严重问题: Error 我们不处理。 这种错误太严重了, OutOfMemoryError 计算中觉见的错误,内存溢出
问题: Exception
编译期问题: 不是RuntimeException 类及它子类的异常,
运行期问题: 是RuntimeException 类和它的子类的异步
jvm 默认如何处理异常
如果程序出现异常,我们没有处理,它会打印异常信息到控制台, 并且终止程序
Throwable类的常见方法
-
e.printStackTrace() 打印异常信息到控制台( 出现类什么类型的异常+信息+位置)
-
e.getMessage() 异常信息,如果想打印需要自己打印
-
e.toString() 出现类什么类型的异常+信息
注: System.exit(0); 退出程序
异常处理
1.自己处理
注:
-
在使用catch 对异常进行处理时,catch声明的异常类型应该和实际抛出的异常类型,相同,或者是继承关系,才能捕获。
-
方法中的异常,可以在方法里进行处理,也可以在调用方法时,进行处理
单个异常处理
语法:
try{
可能出现异常的代码块
}catch(异常类型 异常名){针对这个异常进行处理
}finilly{都会执行,释放资源相关代码
}注: try里面的代码越少越好
多个异常处理
try{
可能出现异常的代码块
}catch(异常类型 异常名){
针对这个异常进行处理
}catch(异常类型 异常名){
针对这个异常进行处理
}catch(异常类型 异常名){
针对这个异常进行处理
}
finilly{
都会执行,释放资源相关代码
}
2.抛出异常
throws
通过throws关键字将异常交给调用者来处理, 可以抛出多个异常,多个异常之间用逗号
throws作用:
在定义一个方法的时候可以使用throws关键字声明,使用throws关键字声明的方法表示此方法不处理异常,而交给方法的调用者进行处理
注:
-
调用的方法如果 throws 抛出了异常,调用方法时,要进行处理(1. try .. catch .. 2. 用throws 向外抛出)
-
重写一个方法时,throws 抛出的异常,父类不能小于子类的范围(子不能比坏)
throw
抛出异常, 可以确定出现异常
语法:
throw new 异常类型("异常信息");
if(num2 == 0){ throw new ArithmeticException("除数不能为0"); } System.out.println(num/num2);
throw 与 throws的区别
-
throws用在方法名后面,跟的是异常类名,throw是用在方法体中,跟的异常对象
-
throws可以跟多个异常类名,用逗号隔开,throw只能抛出一个异常对象
-
throws表示抛出异常,由该方法的调用者来处理, throw表示抛出异常,由方法体内的语句处理
-
throws表示出现异常的一种可能性,并不一定发生这些异常,throw则是抛出了具体的异常,真是的产生了一个Exception对象
finally
放在finally中的代码,不管程序是否发生异常都会执行
只有程序执行了try块中的代码,finally就都会执行前提是没有执行System.exit ()
try块中写return 也没有用, finally 中的代码还是要执行
组合形式:
try{} catch(){}
try{} catch(){} catch(){} catch(){} ...
try{} finally{}
try{} catch(){} finally{}
try{} catch(){} catch(){} catch(){} ...finally{}
return 与 finally
在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。
在转去之前,try中先把要返回的结果存放到不同于x的局部变量中去,
执行完finally之后,在从中取出返回结果,
因此,即使finally中对变量x进行了改变,但是不会影响返回结果。它应该使用栈保存返回值。
public class Example07 {
public static void main(String[] args) {
System.out.println(test());
}
public static int test(){
int x = 1;
try{
x++; // 2
return x; // return 2
}finally {
++x; //3
System.out.println(x); //3
}
}
}
最后返回的结果是 2;
final,finally,finalize的区别?
final:最终的 可以修饰类(类不能被继承) 成员变量(变量就不能重新赋值) 成员方法(不能被重写)
finally: 是异常处理的一部分,放在里面的代码不管是否发生异常都会执行
finalize: Object类中的一个方法,用于垃圾回收器回收对象的时候调用
自定义异常
需求:
-
用户名不能为空
-
用户名的年龄需要在0-150之间
class MyException extends Exception{
private String message;
public MyException(){
}
public MyException(String message){
this.message = message;
}
@Override
public String getMessage() {
return message;
}
}
class Student2{
private int age;
public void setAge(int age) throws MyException{
if(age>=0 && age <= 150){
this.age = age;
}
else{
throw new MyException("年龄必须在0-150之间");
}
}
}
public class Example08 {
public static void main(String[] args) throws MyException {
// String username = "";
// if(username.length() ==0){
// throw new MyException("用户名不能为空");
// }
int age =-1;
Student2 stu = new Student2();
stu.setAge(age);
}
}
关于异常的题目
第一题:编译期异常和运行期异常的区别?
运行时异常包括 RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。 Java 编译器不会检查运行时异常。
受检异常是Exception 中除 RuntimeException 及其子类之外的异常。 Java 编译器会检查受检异常。
RuntimeException异常和受检异常之间的区别:是否强制要求调用者必须处理此异常,如果强制要求调用者必须进行处理,那么就使用受检异常,否则就选择非受检异常(RuntimeException)。一般来讲,如果没有特殊的要求,我们建议使用RuntimeException异常。
第二题:throw和throws的区别
Java 中的异常处理除了包括捕获异常和处理异常之外,还包括声明异常和拋出异常,可以通过 throws 关键字在方法上声明该方法要拋出的异常,或者在方法内部通过 throw 拋出异常对象。
throws 关键字和 throw 关键字在使用上的几点区别如下:
throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出。
throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否则也要在方法签名中用 throws 关键字声明相应的异常。
第三题:final,finally,finalize的区别?
final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,Java 中允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
第四题:Error 和 Exception 区别是什么?
Error 类型的错误通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等,编译器不会对这类错误进行检测,JAVA 应用程序也不应对这类错误进行捕获,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复;
Exception 类的错误是可以在应用程序中进行捕获并处理的,通常遇到这种错误,应对其进行处理,使应用程序可以继续正常运行。
第五题:JVM 是如何处理异常的?
在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给 JVM 的过程称为抛出异常。可能有一系列的方法调用,最终才进入抛出异常的方法,这一系列方法调用的有序列表叫做调用栈。
JVM 会顺着调用栈去查找看是否有可以处理异常的代码,如果有,则调用异常处理代码。当 JVM 发现可以处理异常的代码时,会把发生的异常传递给它。如果 JVM 没有找到可以处理该异常的代码块,JVM 就会将该异常转交给默认的异常处理器(默认处理器为 JVM 的一部分),默认异常处理器打印出异常信息并终止应用程序。
第六题:try-catch-finally 中哪个部分可以省略?
catch 可以省略
原因:
更为严格的说法其实是:try只适合处理运行时异常,try+catch适合处理运行时异常+普通异常。也就是说,如果你只用try去处理普通异常却不加以catch处理,编译是通不过的,因为编译器硬性规定,普通异常如果选择捕获,则必须用catch显示声明以便进一步处理。而运行时异常在编译时没有如此规定,所以catch可以省略,你加上catch编译器也觉得无可厚非。
理论上,编译器看任何代码都不顺眼,都觉得可能有潜在的问题,所以你即使对所有代码加上try,代码在运行期时也只不过是在正常运行的基础上加一层皮。但是你一旦对一段代码加上try,就等于显示地承诺编译器,对这段代码可能抛出的异常进行捕获而非向上抛出处理。如果是普通异常,编译器要求必须用catch捕获以便进一步处理;如果运行时异常,捕获然后丢弃并且+finally扫尾处理,或者加上catch捕获以便进一步处理。
至于加上finally,则是在不管有没捕获异常,都要进行的“扫尾”处理。
第七题:try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
会执行,在 return 前执行。
注意:在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,然后如果在 finally 中修改了返回值,就会返回修改后的值。显然,在 finally 中返回或者修改返回值会对程序造成很大的困扰,C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情,Java 中也可以通过提升编译器的语法检查级别来产生警告或错误。
第八题:常见的 RuntimeException 有哪些?
ClassCastException(类转换异常)
IndexOutOfBoundsException(数组越界)
NullPointerException(空指针)
ArrayStoreException(数据存储异常,操作数组时类型不一致)
还有IO操作的BufferOverflowException异常