你所要知道的异常知识都在这里了

举报
游离 发表于 2023/02/27 20:49:28 2023/02/27
【摘要】 异常@[toc] 认识异常首先要知道什么是异常(exception)在Java中,将程序执行过程中发生的不正常行为称为异常,异常是一种类型事实上,我们已经接触过一些异常了public static void main(String[] args) { System.out.println(10/0);}这是算数异常,具体一点,就是除零异常public static void main...

异常

@[toc]

认识异常

首先要知道什么是异常(exception)

在Java中,将程序执行过程中发生的不正常行为称为异常,异常是一种类型

事实上,我们已经接触过一些异常了

public static void main(String[] args) {
    System.out.println(10/0);
}

image-20220605101430042

这是算数异常,具体一点,就是除零异常

public static void main(String[] args) {
    int[] arr = null;
    System.out.println(arr.length);
}

image-20220605101611664

这是空指针异常

异常的分类

总的来说,异常可以分为两类,运行时 异常和编译异常

image-20220605102622051

以上的除零和空指针异常就是运行时异常,也就是说,在运行时才会报出错误的叫做运行时异常

在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception)
RunTimeException以及其子类对应的异常,都称为运行时异常。

比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。

在程序编译期间发生的异常,称为编译时异常,也称为受检查异常(Checked Exception)

image-20220605111055517

这就是编译异常,需要手动进行修改

image-20220605111325542

需要注意的是,一旦发生了异常,如果不处理,异常接下来的程序就不会运行

public static void main(String[] args) {
    int[] arr = null;
    System.out.println(arr.length);
    System.out.println("接下来的操作");//此时这一行代码就不会执行
}

异常的处理

我们需要通过异常的处理让程序出问题的时候提醒我们出问题的地方,更好的管理代码

防御性异常

LBYL: Look Before You Leap,也就是说,进行一步操作就判断这一步操作是否成功,要是不成功该如何进行补救,这就是防御性异常

boolean ret = false;
ret = 登陆游戏();
if (!ret) {
处理登陆游戏错误;
return;
}
ret = 开始匹配();
if (!ret) {
处理匹配错误;
return;
}
ret = 游戏确认();
if (!ret) {
处理游戏确认错误;
return;
}
ret = 选择英雄();
if (!ret) {
处理选择英雄错误;
return;

缺陷:正常流程和错误处理流程代码混在一起, 代码整体显的比较混乱。尽量不要采用

事后认错型

EAFP: It’s Easier to Ask Forgiveness than Permission 就是说先进行正常的流程操作,当流程全部结束之后,再进行判断错误,集中处理可能发生的错误

try {
登陆游戏();
开始匹配();
} catch (登陆游戏异常) {
处理登陆游戏异常;
} catch (开始匹配异常) {
处理开始匹配异常;
}

事后认错型的优点:正常流程和错误流程是分离开的, 我们更关注正常流程,代码更清晰,容易理解代码

==异常处理的核心思想就是事后认错型(EAFP)==

在Java中,异常的处理其实就是几个关键字的使用

throw、try、catch、finally、throws。

异常的抛出

在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者。
在Java中,可以借助throw关键字,抛出一个指定的异常对象(一般是自定义的异常),将错误信息告知给调用者。

image-20220605114205299

image-20220605114442363

要是抛出的是一个编译时异常,就一定要进行处理(alt+enter)

注意点:

1、throw必须写在方法的内部

2、抛出的对象必须是Exception 或者 Exception 的子类对象

3、如果抛出的是编译时异常,必须进行处理,否则无法通过编译

4、异常一旦抛出,后面的代码就不会被执行

当异常是编译时异常时,一定要进行处理,就要进行异常的声明,就会用到 throws

public static void func(int a) throws CloneNotSupportedException, FileAlreadyExistsException {
    if (a == 10) {
        //一般来说,抛出的是自定义的异常
        //throw  new RuntimeException("抛出一个异常");//这是一个运行时异常,不需要进行throws声明
        throw new CloneNotSupportedException("这是一个克隆异常");
    }
    if (a == 20) {
        throw new FileAlreadyExistsException("这是一个文件异常");
    }
}
public static void main(String[] args) throws CloneNotSupportedException ,FileAlreadyExistsException{
    func(10);
}

注意点:

1、throws必须跟在方法的参数列表的后面

2、声明的异常必须是 Exception 或者 Exception 的子类

3、方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开。

==如果抛出多个异常类型具有父子关系,直接声明父类即可,但是尽量不要,还是要写明具体的异常==

public static void func(int a) throws Exception { //Exception是所有的异常的父类
    if (a == 10) {
        //一般来说,抛出的是自定义的异常
        //throw  new RuntimeException("抛出一个异常");
        throw new CloneNotSupportedException("这是一个克隆异常");
    }
    if (a == 20) {
        throw new FileAlreadyExistsException("这是一个文件异常");
    }
}
public static void main(String[] args) throws Exception{
    func(10);
}
//不建议直接写父类(Exception),具体是什么异常就写什么异常

throw与throws的区别是什么?

throw是抛出一个异常

throws是声明一个异常

异常的捕获

上面使用throw和throws只是为了不报错,它有一个很大的弊端,一旦出现异常,之后的代码就不会执行了

一般来说,最常见的异常处理方式是try-catch捕获并处理

public static void main(String[] args) {
    try{
        int[] arr = null;
        System.out.println(arr.length);
    }catch(NullPointerException e){
        e.printStackTrace();//查看一下具体是哪一行异常
        System.out.println("捕捉到了空指针异常");
        //此时进行异常的处理
    }catch (ArithmeticException e) {
            e.printStackTrace();
            System.out.println("捕获了一个算数异常");
   }catch (Exception e) { //使用父类Exception,只能进行保底,顶多只能排除上面的异常,还要进一步查看异常的种类
            System.out.println("别的异常");
        }
    System.out.println("其他的代码");
}
//输出结果:
//java.lang.NullPointerException
//	at Test.main(Test.java:26)  第26行出现异常
//捕捉到了空指针异常
//其他的代码

注意:

1、就算使用了try-catch,catch里面也要捕捉对对应的异常,要是上面的是空指针异常,结果catch捕捉的是算数异常,那么也是没用的,还是会由JVM进行处理—直接结束程序,后面的代码也就不会执行

2、try-catch 可以捕获很多的异常,但是一行代码最多只能触发一个异常,所以不能==同时==捕获多个异常

3、如果异常中出现父子关系,一定是catch子类在类在前,父类在后,否则全部都被父类捕捉了

finally

在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,此时就要用到finally

public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    try {
        int[] arr = null;
        System.out.println(arr.length);
    } catch (NullPointerException e) {
        e.printStackTrace();
        System.out.println("捕捉到了空指针异常");
    } catch (ArithmeticException e) {
        e.printStackTrace();
        System.out.println("捕获了一个算数异常");
    } catch (Exception e) {
        System.out.println("别的异常");
    }finally{ 
        scanner.close(); //资源的关闭   
        System.out.println("进行资源的关闭");//finally一定会执行
    }
    System.out.println("其他的代码");
}

也可以在try后面加一个(资源),finally就会自动回收掉资源

public static void main(String[] args) {
    try (Scanner scanner = new Scanner(System.in)) {
        int[] arr = null;
        System.out.println(arr.length);
    } catch (NullPointerException e) {
        e.printStackTrace();
        System.out.println("捕捉到了空指针异常");
    } catch (ArithmeticException e) {
        e.printStackTrace();
        System.out.println("捕获了一个算数异常");
    } catch (Exception e) {
        System.out.println("别的异常");
    } finally {
        System.out.println("进行资源的关闭");//一定会执行
    }
    System.out.println("其他的代码");
}

finally中的语句是一定会被执行的

public static int func(int a) {
    try {
        if (a == 0) {
            throw new ArithmeticException();//抛出一个算数异常
        }
        return 2;//就算a=0,上面就异常了,根本就不会执行这一行了
    } catch (ArithmeticException e) {
        System.out.println("算术异常");
    }finally {
        //不建议在finally里面加return
        return 30;
    }
}
public static void main(String[] args) {
    System.out.println(func(0));
}
//输出结果:
//算术异常
//30

异常的处理流程

image-20220605131901519

没有对应的操作进行异常的处理,就会由JVM处理,直接终止程序

总结一下:

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

自定义异常

要自定义异常就要先明确是什么类型的异常,要是运行时异常就继承RuntimeException,编译异常就继承Exception

//自定义一个运行时异常
public class MyException extends RuntimeException {   	
    //两个构造方法
    public MyException() {  //不含参数
    }

    public MyException(String message) { //含有一个参数
        super(message);
    }
}
import MyException.MyException;

import java.util.Scanner;

public class Test2 {
    public static void function(int a) {
        if (a == 0) {
            throw new MyException("这是我的自定义异常");//抛出自己的自定义的异常 1
        }
    }
    public static void main(String[] args) {
        try {
            function(0);//2
        } catch (MyException myException) {
            myException.printStackTrace();
            System.out.println("捕获一个异常");
        }
    }
}

image-20220605133936517

//自定义一个编译异常,继承的是Exception
public class MyException extends Exception {   //运行时异常
    //两个构造方法
    public MyException() {  //不含参数
    }

    public MyException(String message) { //含有一个参数
        super(message);
    }
}
import MyException.MyException;

import java.util.Scanner;

public class Test2 {
    public static void function(int a) throws MyException {  //alt+enter进行异常的声明
        if (a == 0) {
            throw new MyException("这是我的自定义异常");
        }
    }
    public static void main(String[] args) {
        try {
            function(0);
        } catch (MyException myException) {
            myException.printStackTrace();
            System.out.println("捕获一个异常");
        }
    }

实现用户登录的功能

要先实现用户登录,首先要知道登录的必要因素:用户名和密码

其次,还要判断用户名和密码是否正确,要是不正确,就要自定义异常,进行报错

class UserNameException extends RuntimeException {  //继承于运行时异常的用户名异常
    public UserNameException() {
    }

    public UserNameException(String message) {
        super(message);
    }
}

class PasswordException extends RuntimeException {
    public PasswordException() {
    }

    public PasswordException(String message) {
        super(message);
    }
}


public class Test3 {
    private static final String userName = "admin";//要想在下面的loginInfo中使用,就要加上static
    private static final String password = "123456";

    public static void loginInfo(String uName,String pWOrd) throws UserNameException, PasswordException {     //throws表示异常的声明
        if(!uName.equals(userName)){
            throw new UserNameException("用户名错误");
        }
        if (!pWOrd.equals(password)) {
            throw new PasswordException("密码错误");
        }
        System.out.println("密码正确");
    }
    public static void main(String[] args) {
        try {
            loginInfo("admin", "123456");
        } catch (UserNameException e) {
            e.printStackTrace();//打印出异常发生的位置
        } catch (PasswordException e) {
            e.printStackTrace();
        }
    }
}

通过这一个用户登录的例子,相信大家会对于自定义异常的理解会更加深刻。

以上就是Java中关于异常的知识点,如有错误,还请大家指正,我们一起加油!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。