Java中的异常

发布于:2025-04-13 ⋅ 阅读:(21) ⋅ 点赞:(0)

前言
在编程的时候,难免会遇到一些特殊情况,这可能会导致中止,这时候就要想着在运行的过程中能不能将这个问题捕获到,方便后面解决,这时候就要引入异常这个概念了


一 异常的概念

Java中将在代码执行过程中不正常的程序称为异常
在这里插入图片描述
Throwable:是异常体系的顶层类,其派⽣出两个⼦类Error和Exception
Error(错误):指的是Java虚拟机⽆法解决的严重问题,⽐如StackOverflowError和OutOfMemoryError,通常是JVM虚拟机内部问题
Exception(异常):异常产⽣后程序员可以通过代码进⾏处理,使程序继续执⾏
我们通常把Exception称为异常,Error是错误

例如:
算数异常

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

除数不可以为0
运行结果中编译器会报出ArithmeticException算数异常
在这里插入图片描述
空指针异常

//空指针异常
public class Test {
    public static void main(String[] args) {
        int[] arr = null;
        System.out.println(arr.length);
    }
}

这里的arr数组是空的,不可以求其数组长度
NullPointerException空指针异常
在这里插入图片描述
数组下标访问异常

//数组下标越界访问异常
public class Test {
    public static void main(String[] args) {
        int[] arr = new int[3];
        System.out.println(arr[3]);

    }
}

这里的下标是[0,2],没有3下标,这时候会出现数组下标访问异常
ArrayIndexOutOfBoundsException数组下标越界访问异常
在这里插入图片描述

异常的分类

Java中主要分为编译时异常和运行时候异常
编译时异常(受检查异常)
字如其名,也就是在编译时候发生的异常,也叫受检查异常

在没有运行编译器也会用红色波浪线提示

例如:

在这里插入图片描述
这里子类重写Object父类的方法clone方法
这里说这个异常为进行捕获出来
如果没有捕获或者说明这里就会报错
下面时运行的结果,就算没有运行编译器也会用红色波浪线提示
在这里插入图片描述
像下面这样进行声明了就不会报错了

public class Person{
    private String name;
    public int age;

    @Override
    //这里进行异常的声明或者捕获
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

运行时异常(非受查异常)
代码运行的时候编译器就会报错,在编译时并不会报错
RunTimeException以及其⼦类对应的异常,都称为运⾏时异常NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException等等,这些异常只有在运行时候才会报错,编译时可以通过的,这就是与编译时异常的区别

异常的处理

throws异常的声明

throws就是在方法中声明可能会出现的异常,用来提醒编译者,但是这个只是声明,并不是将其处理

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

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

在这里插入图片描述
这里只是声明了异常并没有处理,相当于只是将这个异常逐渐传递下去,并没有得到真正的解决

就像这里主函数调用了含有异常声明的方法,主函数也要进行声明,相当于谁使用这个方法都要声明
如果不声明编译器就会报错
在这里插入图片描述

1.throws必须跟在⽅法的参数列表之后
2. 声明的异常必须是Exception或者Exception的⼦类
3. ⽅法内部如果抛出了多个异常,throws之后必须跟多个异常类型
4. 如果声明多个异常是同一个父类,可以直接声明父类一异常
5. 但是throws并么有处理异常,只是仅仅声明了

throw异常处理

throw new 异常类型();

public class Test {
    public static void fun(int[] arr,int index){
        if(arr==null){
            throw new NullPointerException("空指针异常");
        }
        if(index<0||index>=arr.length){
            throw new ArrayIndexOutOfBoundsException("数组下标越界异常");
        }
        System.out.println("后面的代码");
    }
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        fun(arr,3);
    }
}

这里一旦异常抛出,后面的代码就不会执行了,就像这里的3是超过数组下标,所以这里会抛出数组下标异常,后面的代码不会执行
运行结果如下
在这里插入图片描述

1.throw必须在方法内部
2.如果是RunTimeException或者其子类可以不用处理这些会在运行时由JVM来处理
3.异常一旦抛出,后面的代码就不会执行了

try - catch捕获异常

Java中为了捕获异常有了 try catch finally等关键字

try{
 // 将可能出现异常的代码放在这⾥ 
 //这里要捕获的异常类型与try中的异常一致
}catch(要捕获的异常类型 e){
 //异常处理完后,后面的代码会执行 
}catch(异常类型 e){
 // 对异常进⾏处理 
}finally{
 // finally中代码一定会执行
}

例如

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

        try{
            int[] arr = {1,2,3};
            System.out.println(arr[3]);
        }catch (NullPointerException e){
            e.printStackTrace();//处理异常
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }
        //异常被捕获以后后面的代码会正常执行
        System.out.println("后面的代码执行了");
    }
}

这里是下标越界的异常,被捕获了处理了,如果没有捕获,这里后面的代码就不会执行了

运行结果如下
在这里插入图片描述
例如下面如果没有捕获和处理异常,异常后面的代码不会执行
在这里插入图片描述
就像这里try中放的是出现的是数组下标访问异常,而我们这里捕获的是空指针异常,所以这里并不会处理异常,因此后面的代码就不执行了

那这时候可以在其后面放其可能出现异常的父类进行捕获,这样就不会出现这样的问题了

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

        try{
            int[] arr = {1,2,3};
            System.out.println(arr[3]);
        }catch (NullPointerException e){
            e.printStackTrace();
        }catch (Exception e){//可以捕捉所有异常,因为其是异常的父类
            e.printStackTrace();
        }
        
        System.out.println("后面的代码执行了");
    }
}

这里的空指针异常和数组下标访问异常都是Exception的子类,可以在后面放父类异常,防止没有捕获到
运行结果如下
在这里插入图片描述
但是不建议只是用父类Exception来进行捕获,所以尽量将其放在最后进行捕获父类异常,用起来兜底,防止自己没有考虑周全

try如果捕获到了异常,其try中后面的代码就不会执行了
>

try - catch注意事项
1.try中的异常如果被捕获到了异常,try中异常之后的代码不会执行
2.catch捕获的异常与抛出类型的异常不匹配,异常就不会被捕获
3.异常之间也有父子关系,捕获子类异常在捕获父类异常之后

finally关键字

有些时候我们需要一些代码是必须要执行到,无论是存在否异常,一些程序都要执行,那因此就要引出finally关键字

语法格式:
try{
 // 可能会发⽣异常的代码 
}catch(异常类型 e){
 // 对捕获到的异常进⾏处理 
}finally{
 // 此处的语句⽆论是否发⽣异常,都会被执⾏到 
}
public class Test {
    public static void main(String[] args) {

        try{
            int[] arr = {1,2,3};
            System.out.println(arr[3]);
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }
        finally {
            System.out.println("finally中的代码被执行了");
        }
        System.out.println("后面的代码执行了");
    }
}

这里finally中的语句会执行,try-catch中的语句会执行
运行结果如下
在这里插入图片描述
既然都可以执行,那为什么还要finally语句呢

public class Test {
    public static int fun(){
        Scanner scanner = new Scanner(System.in);
        try {

            int sc = scanner.nextInt();
            return sc;
        }catch (InputMismatchException e){
            e.printStackTrace();
        }finally {
            System.out.println("finally中的代码");
        }
        System.out.println("最后面的代码");
        scanner.close();
        return 0;
    }
    public static void main(String[] args) {
        int n = fun();
        System.out.println(n);
    }

这里就算有return 语句,finally中代码一定会被执行,而try-catch-finally后面的代码不会执行了
在这里插入图片描述

1.先执行try中的代码
2.如果try中的代码有异常,那就在try中后面代码不会执行,在catch寻找是否有
与try中异常相匹配的
3.如果没有在catch中匹配到对应异常,如果没有异常将会向上传递
4.上层如果也没有处理异常,异常将会继续向上传递,一直到main函数为止
5.无论是否匹配到异常,finally中的代码都会被执行

四 自定义异常

虽然Java提供了很多异常,有时候一些异常不能满足我们的需求,这是我们就可以自定义异常来满足需求

这里我们模拟实现一个用户名和密码的登录

public class Test {
    private String userId = "三三";
    private String  password = "060610";

    public void login(String userId,String password) {

            if(!this.userId.equals(userId)){
                System.out.println("用户名错误");
            }
            if(!this.password.equals(password)){
                System.out.println("密码错误");
            }
        System.out.println("登录成功");
    }
    public static void main(String[] args) {
        Test test = new Test();
        test.login("三三","060610");
    }
}

这里密码和用户名都正确,这里会登录成功
运行结果如下
在这里插入图片描述
那我们可以在其用户名错误或者密码错误的时候抛出异常
当然可以,这时候我们就要自定义一个异常,然后继承Exception或者RunTimeException

下面是两个自定义异常类

//继承基类异常
//用户名异常
//这里继承的是受查异常
class UserIdException extends Exception{
    public UserIdException(String message){
        super(message);
    }
}
//密码异常
class PasswordException extends Exception{
        public PasswordException(String message){
            super(message);
        }
}

Test类中的代码使用上面的自定义异常

class UserIdException extends Exception{
    public UserIdException(String message){
        super(message);
    }
}
class PasswordException extends Exception{
        public PasswordException(String message){
            super(message);
        }
}
public class Test {
    private String userId = "三三";
    private String  password = "060610";

    public void login(String userId,String password) {
        try {
            if(!this.userId.equals(userId)){
                throw new UserIdException("用户名错误"+userId);
            }
            if(!this.password.equals(password)){
                throw new PasswordException("密码错误"+password);
            }
            //如果没有异常就会登录成功
            System.out.println("登录成功");
        }catch (UserIdException e){
            e.printStackTrace();
        }catch (PasswordException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.login("三三","060610");
    }
}

这里用户名和密码都正确,所以是登录成功
在这里插入图片描述
如果用户名不对,就会爆出用户名异常,登录就不成功
在这里插入图片描述

自定义异常都是继承Exception或者RunTimeException
继承Exception就是受查异常(编译时异常)
继承RunTimeException就是非受查异常(运行时异常)

到这里就结束了,预知后事如何,请听下回分解