自学内容网 自学内容网

Java——异常


在这里插入图片描述

一、什么是异常

在我们日常生活中,我们可能会生病,会头疼,会肚子疼,打球的时候也可能会受伤。再程序中也是一样,运行时难免会出现一些奇奇怪怪的问题。
在Java中,将程序执行过程中发生的不正常行为称为异常。

例如:
算数异常

        //算数异常
        System.out.println(10 / 0);

Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:4)

数组越界异常

//数组越界异常
        int[] arr = {1,2,3,4};
        System.out.println(arr[4]);

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
at Test.main(Test.java:8)

空指针异常

//空指针异常
        String str = null;
        System.out.println(str.length());

Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:12)

在Java中不同类型的异常,都有与其对应的类来进行描述。

二、异常的体系结构

感觉异常还是挺多的,那在Java中是怎么分类管理的呢?构建一个异常的体系结构。
在这里插入图片描述
从图中可以看到:
Throwable:是顶层类,其有两个子类Error Exception.
Error 是指Java虚拟机无法解决的严重问题,如StackOverflowError栈溢出,OutOfMemoryError堆溢出。
Exception: 程序员可以通过代码进行处理,让程序继续执行。

异常分类

异常可能在编译时发生,叫编译时异常,也叫受检查异常。
如:java.lang.CloneNotSupportedException

public class Dog {

    public String name;
    public String age;
    
    public Dog clone() {
        return (Dog) super.clone();
    }
    
}

在这里插入图片描述
在这里插入图片描述

在运行时发生,叫运行时异常,也叫非受检查异常。
RunTimeException以及其子类对应的异常,都称为运行时异常。
比如:
NullPointerException、
ArrayIndexOutOfBoundsException、ArithmeticException

注意:
编译时出现的语法性错误,不能叫做异常。
在这里插入图片描述
运行时异常时经过编译生成字节码Class文件,再有Java执行过程中出现的错误。

三、异常的处理过程

事前防御型:在操作之前就做充分的检查。代码比较混乱,正常操作与处理错误的操作混合。

//以和平精英为例
        Boolean login = loginGame();
        if(login==false) {
            处理登录失败的错误;
            return;
        }
        login = matchTeammates();
        if(login==false) {
            处理匹配队友失败的错误;
            return;
        }
        login = loadingScreen();
        if(login==false) {
            处理加载地图失败的错误;
            return;
        }
        ...

事后认错型:先操作,遇到问题再处理解决。正常处理流程和错误处理流程分开,代码清晰易理解。


        try{
            loginGame();
            matchTeammates();
            loadingScreen();
            
        }catch (loginGameException e){
            处理登录失败的错误;
        }catch (matchTeammatesException e){
            处理匹配队友失败的错误;
        }catch (loadingScreenException e){
            处理加载地图失败的错误;
        }
        ....

1.异常的抛出

throw ,抛出一个指定的异常对象,将错误的信息传递给调用者。

throw new xxxException("异常产生的原因")
public class Test {

    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        getArrayElement(arr,1);
    }
    
    public static int getArrayElement(int[] arr,int index) {
        if(arr==null) {
            throw new NullPointerException("数组空指针异常");
        }
        if(index<0 || index>arr.length-1) {
            throw new IndexOutOfBoundsException("数组下标越界");
        }
        return arr[index];
    }
}

注意y一下几点:

  • throw必须写在方法体的内部
  • 抛出的对象必须是Exception / Exception 的子类
  • 如果抛出的是运行时异常 RunTimeException / RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
  • 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
  • 异常一旦抛出,其后的代码就不会执行

2.异常的捕获

  • throws异常声明
  • try - catch 捕获处理

3.throws异常声明

用于当前的方法不处理异常,提醒调用者来处理异常。


修饰符 返回值类型 方法名(参数列表)throws 异常类型1,异常类型2 ... {

...
}

以上面的小狗类为例子:

public class Dog {

    public String name;
    public String age;
    public Dog clone() throws CloneNotSupportedException{
        return (Dog) super.clone();
    } 
    //throws抛给调用者,此时为Test

}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Dog dog = new Dog();
        dog.clone();
//此时Test也不处理,抛给JVM来处理。
//一旦交给JVM处理,程序就终止了!
    }
}

注意事项:

  • throws必须出现方法的参数列表后
  • 声明的异常必须是Exception,及其子类,不能是Error
  • 如果需要抛出多个异常,则异常之间用逗号隔开,如果抛出的异常有父子关系,则直接声明父类即可!
  • 使用throws,调用者必须对该异常进行处理,或继续使用throws

4.try - catch 捕获处理异常

由上可知throws并没有对异常进行有效的处理,此时就需要用到try - catch了!

try {
  //可能会出现异常的代码
  
}catch(异常类型1 e) {
// 对try中的代码块出现的异常类型1进行捕获
//没出现异常就直接跳过了
}catch(异常类型2 e) {
// 对try中的代码块出现的异常类型2进行捕获
//没出现异常就直接跳过了
}

    public static void main(String[] args) {
        int[] arr = {1,2,3,4};
        System.out.println("异常处理开始-------");

        try{
            System.out.println(arr[21]);
            arr=null;
            System.out.println(arr.length);
        }catch (NullPointerException e) {
            e.printStackTrace();
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }
        System.out.println("异常处理结束-------");

    }

在这里插入图片描述
为啥这个异常信息在最后输出?因为和System.out.println()输出的方式不一样。
注意事项:

  • try里只要哪一行抛出异常,其后面的代码则不再执行
  • 出现的异常会与catch中的异常类型进行一 一匹配,一致则捕获,否则继续往上抛,直到JVM
  • 如果异常之间具有父子关系,子一定要在父前,因为父亲可以接受子类的出现的异常,那么子类就没有了意义。
  • 建议不同的异常,分开写,不必直接写Exception
  • 只会抛出一个异常,不会抛出多个异常。

5.finally

再写程序时,有些特定的代码,无论是否发生异常,都需执行。如 打开的文件资源,组后都需关闭,对资源进行回收。也是因为出现异常,有些代码不能执行到。

try {

}catch (异常类型 e) {

}finally {
//无论发生异常与否,都会执行到。
}

那么既然finally 和 try-catch-finally后的代码都会执行到,那么为啥还要有finally?

返回数据,如果正常输入,则s 无法关闭。

public class Test {
    public static void main(String[] args) {

        printFunc();
    }

    private static int printFunc() {
        Scanner s = new Scanner(System.in);

        try {
            int a = s.nextInt();
            return a;
        }catch (InputMismatchException e) {
            e.printStackTrace();
        }
     
        s.close();
        return 0;
    }
    
 }

所以有finally

public class Test {
    public static void main(String[] args) {

        printFunc();
    }

    private static int printFunc() {
        Scanner s = new Scanner(System.in);

        try {
            int a = s.nextInt();
            return a;
        }catch (InputMismatchException e) {
            e.printStackTrace();
        }finally {
            s.close();
        }

        return 0;
    }

}

6.异常处理流程总结

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

四、自定义异常

虽然Java中异常类有许多了,但是并不能完全表示实际开发中所遇到的一些异常,此时就需要我们自己定义一些异常了。

  • 自定义异常通常会继承Exception(默认为受检查的异常)或RuntimeException(为非受检查的异常)

实现一个用户登录功能,登录用户名或密码错误采用自定义异常类处理。

public class Test {
    public static void main(String[] args) {

        try {
            login("zxh","123456");

        } catch (UserNameException e) {
            throw new RuntimeException(e);
        } catch (PasswordException e) {
            throw new RuntimeException(e);
        }

    }

    public static void login(String name, String pwd) throws PasswordException, UserNameException {
        if(!name.equals("zxh")) {
            throw new UserNameException("用户名错误!");
        }
        if(!pwd.equals("123456")) {
            throw new PasswordException("密码错误!");
        }
        System.out.println("登录成功");

    }
}
public class PasswordException extends Exception{

//因为写了hrow new PasswordException("密码错误!"); 传递了参数。
    public PasswordException(String message) {
        super(message);
    }
}


public class UserNameException extends Exception{
//传递了参数
    public UserNameException(String message) {
        super(message);
    }

}


原文地址:https://blog.csdn.net/m0_74100417/article/details/140370702

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!