初始Java——异常机制

1认识异常

异常类型:

  1. 运行时异常(非受查异常)
  2. 编译时异常(受查异常)

编译时是指javac,也就是写出来直接会在编译器上报错的异常

运行时是指编译i通过得到class文件后,由JVM执行过程中出现的错误

防御式编程

错误在代码中是客观存在的,因此我们要让程序出现问题的时候及时通知,主要有两种方式

LBYL:Look Before You Leap在操作之前就做充分地检查

EAFP:It's Easier to Ask Forgiveness than Permission事后获取原谅比事前获取许可更容易,也就是先操作,遇到问题再处理

 

异常的用法

异常处理通过:

try{

可能发生异常的代码

}catch(异常类型 异常对象 || 可以是一种也可以是多种){

捕获异常类型,并处理异常

}

catch可以有很多个,但是作为父类的exception不能放到前边,不然直接符合条件捕获跳出了

 

为什么要使用try?

直接运行可能发生异常的代码,如若发生异常,则会终止程序运行

使用try则捕获到异常的同时,代码还在继续运行,

原理:

直接运行,直接将异常交给JVM,一旦异常整个程序直接退出来

 

关于异常的处理方式

一场的种类有很多,我们要根据不同的业务场景来决定

对于比较严重的问题(例如和算钱相关的场景),应该让程序直接崩溃,防止造成严重的后果

对于不太严重的问题(大多数场景),可以记录错误日志,并通过监控报警程序及时通知

对于可能会恢复的问题(和网络相关的场景),可以尝试进行重试

在我们当前的代码中采取的是经过简化的第二种方式,我们记录的错误日志时出现异常的方法调用信息,能很快速的让我们找到出现异常的位置,以后实际工作中我们会采取更瓦贝的方式来记录异常信息

 

关于“调用栈”

方法之间是存在相互调用关系的,这种调用关系我们可以用“调用栈”来描述,在JVM中有一块内存空间称为“虚拟机栈”专门存储方法之间的调用关系,当代码出现异常的时候我们就可以使用e.printStackTrace();的方式查看出现异常代码的调用栈

打印异常信息:

e.printStackTrace();

 

注意:

catch进行类型匹配的时候,不光会匹配相同类型的异常对象,也会捕捉目标异常类型的子类对象

因此,当catch(Exception e){}的情况下,也就是出现任何Exception的子类异常都可以直接抛出

 

finally的用法

作用:释放已打开的资源

语法:

try{

 

}catch(){

 

}finally{

 

}

注意:无论是否存在异常,finally中的代码一定都会执行到,保证最终一定会执行到已打开的close方法

 

finally的注意事项

由于finally其方法返回之前一定会执行finally,所以如若finally中也存在return语句,则会影响到原有的return语句

所以,尽量不要在finally中写return诸如此类的语句,以免造成影响

 

使用try负责回收资源

try(Scanner sc = new Scanner(System.in)){

    int num = sc.nextInt();

    System.out.println(num);

}catch (Exception e){

    e.printStackTrace();

}

将Scanner对象在try的()中创建,就能保证在try执行完毕后自动调用Scanner的close方法

 

 

方法异常处理

如果本方法中没有适合的处理异常的方式,就会沿着调用栈向上传递

public class test20210426 {

    public static void main(String[] args) {

        try{

            func();

        }catch (ArrayIndexOutOfBoundsException e){

            e.printStackTrace();

        }

    }

 

    public static void func(){

        int[] arr = {1,2,3};

        System.out.println(arr[100]);

    }

}

image.png

在本代码中,func方法有错误,但是没有处理异常,则会支到调用该方法的位置处理异常,所谓的沿着调用栈向上传递

 

如果向上一直传递都没有合适的方法处理异常,最终就会交给JVM处理,程序就会异常终止(和没使用try catch 是一样的)

 

异常处理流程

  • 程序先执行try中的代码
  • 如果try中的代码出现异常,就会结束try中的代码,看和catch中的异常类型是否匹配
  • 如果找到匹配的异常类型,就会执行catch中的代码
  • 如果没有找到匹配的异常类型,就会将异常向上传递到上层调用者
  • 无论是否找到匹配的异常类型,finally中的代码都会被执行到(在该方法结束之前执行)
  • 如果上层调用这也没有处理的了异常,就继续向上传递
  • 一直到main方法也没有合适的代码处理异常,就会交给JVM来进行处理,此时程序就会异常终止

 

抛出异常(手动)

除了Java内置的类会抛出一些异常之外,也可以选择手动抛出某个一场,使用throw关键字完成这个操作

public static int divide(int x, int y){

if(y == 0){

throw new ArithmeticException("抛出除0异常");

}

}

在代码中,我们可以根据实际情况来抛出需要的异常,在构造遗产对象同时可以指定一些描述性信息

2异常说明

在处理异常的时候,通常希望知道这段嗲马中究竟会出现那些可能的异常

所以可以使用throws关键字,吧可能抛出的异常显式的标注在方法定义的位置,从而提醒调用者要注意捕获这些异常

public static int divede(int x , int y ) throws ArithmeticException{//声明异常

    if(y == 0){

        throw new ArithmeticException("抛出异常");

    }

    return x/y;

}

作用:防止代码过长找不到需要抛的异常

 

 

3Java异常体系

Java内置了丰富的异常体系,用来表示不同情况下的异常

 

  • 顶层的Throwable派生出两个钟哟啊的子类,ErrorException
  • 其中Error指的是Java运行时内部错误和资源耗尽错误,应用程序不抛出次类异常,这种内部错误一旦出现,除了告知用户,并且使程序终止之外,在无能为力这种情况很少出现
  • Exception是我们程序员所使用的异常类的父类
  • 其中Exception有一个子类成为RuntimeException,这里面又派生出很多我们常见的异常类NullPointerException等等

 

 

4自定义异常类

import java.util.Scanner;

public class TestException {
    private static String userName = "admin";
    private static String password = "123456";

    public static void main(String[] args) {
        try {
            login("asd","123");
        }
        catch(UserError userError){
            userError.printStackTrace();
        }catch(PasswordError passwordError){
            passwordError.printStackTrace();
        }
    }
    public static void login(String userName, String password)throws UserError,PasswordError{
        if(!TestException.userName.equals(userName)){
            throw new UserError("用户名错误!");
        }
        if(! TestException.password.equals(password)){
            throw new PasswordError("密码错误!");
        }
        System.out.println("登录成功!");
    }
}


class UserError extends Exception{
    public UserError(String message){
        super(message);
    }
}
class PasswordError extends Exception{
    public PasswordError(String message){
        super(message);
    }
}

注意:

  • 自定义一场通常会继承自Exception或者RuntimeException
  • 继承自Exception的异常默认是受查异常
  • 继承自RuntimeException的异常默认是非受查异常

 

posted @ 2021-08-09 17:02    阅读(50)  评论(0)    收藏  举报