目录
4.异常的祖宗是Throwable,Object是Throwable的父类
1.什么是异常
程序在执行过程当中出现的不正常事件,在遇到异常后jvm会把异常信息打印到控制台上
2.异常以什么形式存在
1.java中的异常以类的形式存在,
2.当程序执行到某处发现异常时会new相应的异常对象
3.异常在什么阶段发生
程序分为编写编译和运行三个阶段,所有的异常都发生在运行阶段
4.异常的祖宗是Throwable,Object是Throwable的父类
5.异常的分类
1.Throwable有两个直接子类:Error,Exception
2.Error和Exception都是异常类
6.Error
Error不可处理,一旦发生,只能尽快终止程序
7.Exception
Exception中有两种:运行时异常(RuntimeException)和编译异常(除了RuntimeException)
1.RuntimeException
运行时异常的子类也是运行时异常
运行时异常可处理可不处理
2.编译异常
编译异常是需要进行预处理的
Exception以及除了RuntimeException的子类都是编译异常,Error也是运行时异常
编译异常也是发生在运行阶段,因为编译异常是需要进行预处理的,不进行预处理就无法编译编译好以后在运行阶段再检测是否是编译异常类对应的异常,就是说如果存在编译异常那么其实就过不了编译,更别说产生异常对象了
3.对比运行时异常和编译异常
运行时异常可处理可不处理编译异常需要进行预处理
运行时异常发生概率低,编译异常发生概率高
8.异常处理方式
1.抛出异常:
语法:在方法名和形参列表后添加 throws 异常类名
把异常抛给调用这个函数的函数
2.捕捉异常
把捕捉到的异常处理一下
1.语法:
try{代码;}catch(异常引用){代码}
2.含义
try大括号里面存放的是可能出现编译异常的代码,如果在这里确实异常了就会创建一个对象让catch里面的异常引用指向这个异常,然后执行catch大括号里面的代码,并且从出现异常的那一行代码起往后的代码就不执行了
3.catch可以使用多态的特性(如果不知道是什么子类异常可以捕捉他的捕捉父类异常),但是只能是异常,最多也就是到Throwable,不能一直父类到Object,因为异常的祖宗是Throwable,就算Throwable的父类是Object也不能在catch的小括号里面填Object,除此之外,最好还是捕捉最精确的异常,少用父类,除非真不知道
3.异常处理代码
public class Throw {
public static void a() throws FileNotFoundException {
System.out.println("a开始");
b();
System.out.println("a结束");
}
public static void b() throws FileNotFoundException{
System.out.println("b开始");
c();
System.out.println("b结束");
}
public static void c() throws FileNotFoundException {
System.out.println("c开始");
new FileInputStream("D:/java.txt");
System.out.println("c结束");
}
}
public class Run {
public static void main(String[] args){
//由于不建议再main中抛异常,所以使用try,catch捕捉异常
try {
Throw.a();//最好不要再抛异常了,因为这里是main方法,是程序的入口,再抛出异常就抛给了JVM,而JVM检测到你跑过来的异常就会终止程序
}catch(FileNotFoundException e){
System.out.println(e.toString());
}
System.out.println("捕捉异常执行完毕");
}
}
不异常的运行结果;
a开始
b开始
c开始
c结束
b结束
a结束捕捉异常执行完毕
异常的运行结果:
a开始
b开始
c开始
java.io.FileNotFoundException: D:\1.txt (系统找不到指定的文件。)捕捉异常执行完毕
从上述可知,a调用b,b调用c,所以执行过程是,主函数调用a函数先打印”a开始“,然后调用b函数,打印”b开始“,又调用c函数打印"c开始"然后在查找文件,没有找到的时候就会抛出异常直到传递给主函数当中捕捉到了异常,于是开始执行catch里面的代码,catch执行完之后还会继续执行try{}catch(){}块以外的代码。全部执行完之后主函数也就执行完了
如果上述内容改作在主函数当中抛出异常给JVM那么就不会执行了,因为他会直接终止程序
代码如下
public class Throw {
public static void a() throws FileNotFoundException {
System.out.println("a开始");
b();
System.out.println("a结束");
}
public static void b() throws FileNotFoundException{
System.out.println("b开始");
c();
System.out.println("b结束");
}
public static void c() throws FileNotFoundException {
System.out.println("c开始");
new FileInputStream("D:/1.txt");
System.out.println("c结束");
}
}
public class Run {
public static void main(String[] args) throws FileNotFoundException{
Throw.a();
System.out.println("捕捉异常执行完毕");
}
}
运行成功时结果和trycatch的一样
运行不成功的结果如下(会直接终止整个程序):
a开始
b开始
c开始
Exception in thread "main" java.io.FileNotFoundException: D:\1.txt (系统找不到指定的文件。)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:216)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:111)
at com.software.exception.Throw.c(Throw.java:20)
at com.software.exception.Throw.b(Throw.java:15)
at com.software.exception.Throw.a(Throw.java:10)
at com.software.exception.Run.main(Run.java:7)
4.多异常的抛出和捕捉
1.抛出:
把throws 异常1,异常2,异常3.......
2.捕捉:
每个catch捕捉一个异常,多个异常的时候就写成try{}catch(){}catch(){}......
需要注意,在捕捉异常的时候如果捕捉的多个异常有父子关系,那么就先捕捉子类异常后捕捉父类异常。
在JDK1.8以后的语法当中有新的写法(但是最好别这么用,因为虽然对于计算机而言异常信息明确,但是对于人而言,这个异常信息不明确,不知道到底捕捉到的哪个异常)就是catch(异常类名一 | 异常类名二 | 异常类名三 变量名),但是有要求,就是小括号这里面这几个异常类型不可以有父子关系
9.异常的两个重要方法
1.getMessage:获取异常信息
对于toString方法而言会返回异常类名:异常信息
对于getMessage而言会返回异常信息
也就是说获取异常信息不包括类的全路径
2.printStackTrace:打印堆栈信息
在这里打印的信息可以看到到底是在哪个方法里出了问题。方便程序员修改代码,会体现出来一层一层的方法调用信息,实际应用当中最好catch里面执行打印堆栈信息
10.finally
1.含义
finally的含义是不论这段代码有没有异常finally的内容都会执行
2.语法(必须搭配有try,不可单独使用)
try{}catch(){}finally{} 也可以直接try{}finally{}
3.过程
如果在try里面有一个漏掉的运行时异常没有处理,那么当程序执行到这一步的时候就中断了,后续一些必要的操作有的无法完成,所以此时就用到了finally关键字来执行一些必要操作,当程序检测到下一行代码有一个未处理的异常的时候就会先转去执行finally里面的代码,然后在执行未处理异常这一行代码并且报错
4.例子
下列这段代码就会产生一个运行时异常并且无法完成必要的“关闭文件”的操作
public class Run{
public static void main(String[] args){
FileInputStream fis = null;
try{
fis = new FileInputStream("D:/java.txt");
fis.read();
System.out.println("adw");
int result = 100/0;//在这里会出现运行时异常,如果不做处理,那么后续的close操作就无法完成,所以要把这个运行时异常也捕捉一下然后处理掉,或者finally
fis.close();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
}
}
运行结果是
adw
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.software.demo11.Run.main(Run.java:14)
所以要进行处理就要开始进行finally了
public class Run{
public static void main(String[] args){
FileInputStream fis = null;
try{
fis = new FileInputStream("D:/java.txt");
fis.read();
System.out.println("adw");
int result = 100/0;//在这里会出现运行时异常,如果不做处理,那么后续的close操作就无法完成,所以要把这个运行时异常也捕捉一下然后处理掉
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
finally {
System.out.println("ad");
if(fis!=null) {
try {
fis.close();
System.out.println("文件已关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
运行结果是:
adw
ad
文件已关闭
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.software.demo11.Run.main(Run.java:14)
5.finally经典题
public class Run{
public static void main(String[] args){
System.out.println("返回值是:"+Run.show());
}
public static int show(){
int i=0;
try{
i=10;
return i;
}finally{
i++;
System.out.println("i="+i);
}
}
}
运行结果:
i=11
返回值是:10
return在最后执行,但是finally一定会执行,所以编译后就是又额外定义了一个变量存下来返回值,然后又执行了i++,最后把刚才存下来的返回值给返回掉
11.自定义异常
1.自定义异常都需要些什么
1.让自己定义的异常类继承一个本来就有的异常类
2.无参的构造方法,调用父类无参构造方法
3.有参的构造方法,调用父类有参构造方法
4.对于toString,getMessage,printStackTrace这三个方法可以重写也可以不重写,建议别重写
2.作用
使得异常信息更准确,和自己规定的一样
3.throw关键字
这里有一个throw关键字在方法体当中,用来创建一个对象把对象抛给throws
4.代码示例
public class Mine extends Exception{
public Mine(){};
public Mine(String message){
super(message);
}
}
public class Run {
public static void main(String[] args){
//接下来是关于throw关键字和自定义异常的部分
//throw关键字是在发生某些事件的时候用来创建一个异常对象然后交付给throws用来向上抛出的
try {
show(5.0,0.0);//由于除数是0,那么此时本身其实是有运行时异常的,但是必须都是int才报错,所以在这里需要人为规定一个异常,设置如果监测到除数是个0就把他抛出来
} catch (Mine e) {
e.printStackTrace();
}
System.out.println("adw");
}
public static void show(double j,double k) throws Mine{
if(k==0){
throw new Mine("浮点数运算除数也不为0");
}//这部分内容就是判断到了有异常了就抛出去,是我人为未规定异常
double i = j/k;
System.out.println(i);
}
}
12.关于异常的五个关键字:
try catch finally throws throw
13.如果一个方法在声明的时候有throws关键字
父类方法有异常,在子类当中重写的时候这个方法要么不再抛异常,要么子类抛的异常不能更大(父类),
重写方法的时候子类的异常不能比父类的异常更多
总结:
1.父类有异常,子类可以没有异常
2.父类有异常,子类异常不能是父类异常的父类
3.子类编译异常数不能比父类的多
以上情况说的都是编译时异常
14.什么时候抛出,什么时候捕捉
需要调用方处理异常的时候使用throws和throw
需要自己的时候使用try catch