[Java]异常-

目录:

一:异常的基本介绍

二:异常的分类

三:异常的抛出

四:异常的捕获

1.throws

2.关键字try-catch组合使用处理异常

五:finally的使用

六:自定义的异常类

一:异常的基本介绍:

1.定义:程序执行过程中遇到的不正常的行为,即阻碍程序的执行的行为称作异常。异常可以通过代码解决。

2.本质:异常本身是一个类。我们所说的发生异常,实际上是一个异常类被执行了。

异常体系图: 

 从上图可知:

Throwable:是异常体系中的顶层类,派生出Error和Exception两个重要的子类。

Error:指的是Java虚拟机无法解决的严重问题。比如:JVM的内部错误,资源耗尽(内存溢出和栈溢出)。

Exception(异常):②

3.异常关键字:异常类的使用涉及五个关键字:throw, throws, try, catch, finally

二:异常的分类

1.编译时异常:也叫受查异常(Checked Exception),在程序编译期间发生的异常。若这个异常不解决,那么程序无法运行。比如:

当你对一个类进行拷贝的时候,如图,类的对象Person调用clone方法时,调用者 (指的是包含含有异常的代码的方法,在main方法中调用,此时main方法是调用者) 需要对代码中含有的异常进行解决,如果没有解决,那么程序无法运行,这就是在编译过程中发生的异常(即编译时异常)。

2.运行时异常:也叫非受查异常(Unchecked Exception),在程序运行期间发生的异常。这个异常能通过编译,但在程序运行的时候就会报错,从而中止程序运行。比如:

①:ArithmeticException (算术异常)

 注:正常来说0是不能作分母当被除数的,但此时编译器没有进行报错,当运行程序时就会报错,报错了一个异常ArithmeticException并打印到控制台上,这是算术异常,是运行时异常的子类之一。

②:ArrayIndexOutOfBoundsException (数组越界访问异常)

注:这里定义了一个内存空间为3的数组,下标选择范围为0~2,此时访问数组的3下标,越界访问了数组的空间,但在进行编译时编译器没有报错,却在运行时报了一个异常ArrayIndexOutOfBounds的错误出来,这是数组越界访问异常,是运行时异常的子类之一。

③:NullPointerException (空指针异常)

注:这里定义了一个引用类型为空指针的数组,说明这个数组未引用任何值,所以是无法使用这个引用来访问编译器的库方法,但此时数组访问了系统的求数组长度的库方法,而系统并未报错,却在运行时报了NullPointerException异常错误,这是空指针异常,是运行时异常的子类之一。

总结:编译时异常必须要先解决掉问题,程序才能执行。运行时异常可以通过编译,但会在运行过程中出问题,需要运行中解决或者在编译时解决都可。

三:异常的抛出-throw

throw关键字的运用是手动抛出一个异常。

语法:throw new xxxException("异常产生的原因")

运用场景:当我们需要检测程序中的可能会出现的错误时,在可能发生错误的地方抛出一个异常,告知调用者。比如:参数检测

我需要利用这个testException方法来检测我的数组中有没有10这个数字,若有则报异常。

第一个数据是正确的,没有异常发生。

 第二个数据里面含有10这个数字,触发了异常,一旦触发了异常,且异常没有被解决,那么程序会立马中止并报错。

这里的n == 10 是告知调用者异常的原因是数组数据中含有10这个数字。


注:利用throw关键字在代码可能会发生错误的地方抛出一个异常,能有效的检查代码,同时将错误信息返还给调用者。

四:异常的捕获

异常的捕获就是处理代码中出现的异常。有两个可以解决异常的方法。用thorws关键字解决或try-catch解决

1.throws

1.1 语法:修饰符  方法名 (参数列表) throws 异常类型1,异常类型2… {

}

1.2 该关键字是声明并抛出代码中可能存在的异常,通过该方法解决的异常最后都是抛给JVM进行处理。(throws只是暂时的能解决编译时异常,无法解决运行时异常,所以一般是在出现编译时异常时使用)比如:

此时main方法这个调用者中的代码出现了一个编译时异常:CloneNotSupportedException

如果我们用throws抛出了这个异常,在main方法中存在的异常能得以暂时解决,即:这个CloneNotSupportedException是被main方法交给了上一层的调用者解决,最终被JVM进行处理。在main方法中没有对其实际处理掉。

1.3 补充:1.若代码中存在多个编译时异常,可以同时声明多个异常。

2.若抛出的异常具有父子关系,则直接声明父类即可。

3.声明的异常必须是Exception或Exception的子类。

4.将光标放在需要抛出的异常方法上,Alt+ Enter 快速处理

2.关键字 try-catch 组合使用处理异常

2.1 语法:

try {
//可能存在异常的代码
}catch (要捕获的异常类型 e) {
//try中抛出异常,若被catch捕获的异常类型对上,或是其子类,就会被捕获到e中去(e可以是其它的字母)
//然后对异常进行处理,处理完后跳出try-catch结构,继续执行后序代码
}catch (异常类型 e) {
//对异常进行处理
}

2.2 throws没有对异常进行真正的处理,try-catch是在代码运行时就会处理掉其中的异常

比如:此时的异常ArithmeticException时在main方法中就被处理了,没有交给上一层调用者进行处理,并且能执行后序代码。

2.3 可以存在多个catch接收异常类型,但不会抛出多个异常进行同时处理

比如:

 public static void main(String[] args) {
        try {
            int[] array = new int[2];
            System.out.println(array[3]);

            System.out.println(10 / 0);

        }catch (ArithmeticException e) {
            e.printStackTrace();//这个能将你解决的异常打印到控制台上
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }
        System.out.println("程序正常运行!");
    }

结果:

数组越界访问异常先发生,异常发生之后,它之后的代码就不会再执行了,会直接跳转到catch中去捕获并处理异常

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

            int[] array = new int[2];
            System.out.println(array[3]);

        }catch (ArithmeticException e) {
            e.printStackTrace();//这个能将你解决的异常打印到控制台上
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }
        System.out.println("程序正常运行!");
    }

结果:

这里是算术异常先发生,发生之后,处于它之后的代码不会执行,就不会触发数组越界访问异常

总结:尽管代码中可能会同时含有多个异常,但编译器只会处理优先碰到的异常,其余的存在异常的代码就不会执行。

补充:e.printStackTrace 只会显示运行时异常和自己手动抛出的异常,不会显示编译器本身带有的编译时异常。

2.4 若catch捕获的异常类型存在父子关系,那么子类在前,父类在后。

比如:

此时下图中的异常被ArithmeticException 捕获到

此时下图中的ArrayIndexOutOfBoundsException  被父类RuntimeException 捕获到

如上图:子类异常只有一个算术异常:ArithmeticException,其它的异常则有父类兜底。

五: finally关键字的使用

1.语法:①形成try-catch-fianlly连用  ②形成try-finally连用

2.特点:只要存在finally语句,那么这个语句中的代码一定会实现

此时代码中存在数组越界访问异常没有处理,finally中的语句执行了

此时代码中的数组越界访问异常处理了,finally中的代码执行了

总结:不管try-catch中的异常有没有处理,finally语句一定会执行

3.finally语句的作用:因为finally语句中的代码一定会被执行,所以会用来进行对一些资源清理的扫尾工作。

4.finally的特例:当try-catch中出现有return时,finally语句依旧会被执行

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

            int[] array = new int[2];
            System.out.println(array[3]);
        }catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("捕获到了异常!");
            return;
        } finally {
            System.out.println("finally中的代码一定会执行!");
        }
    }

​

代码运行结果:

总结:finally语句执行的时机是在方法返回之前,即return之前。(如果try或catch中有return语句,那么finally会在return执行之前先执行

5.关于finally的一道面试题

try{
return 10;
}finally{
return 20;
}
A:10 B:20 C:30 D:编译失败

答案是B。因为在try中的return执行时,会先执行finally语句,此时里面的return语句执行,返回20,程序结束。即当try和finally中都存在return语句时,会执行finally中的return。

六:自定义的异常类

为什么要自定义一些异常类呢?因为编译器给予我们的异常类有时是不符合我们的需求的,这就需要自定义异常类来符合我们的实际情况。

1.异常类的结构:

创建一个异常类首先了解它的结构:

//以算术异常结构为例
public class Arithmetic extends RuntimeException {

    public ArithmeticException () {
        super();
    }

public ArithmeticException(String s) {
        super(s);

  }

}

//第一步:用public class修饰你要自定义的异常,用extends来继承父类异常
//注:继承RuntimeException 说明你要自定义的异常是运行时异常
//    继承Exception 说明你要自定义的异常是编译时异常

//第二步:构造两个构造方法,一个没有参数,一个拥有String类型的参数。

2. 现在来实现一个用户登录异常

2.1 先构造用户账号异常和用户密码异常

//用户账号异常
public class UserException extends RuntimeException {
    public UserException() {
        super();
    }

    public UserException(String s) {
        super(s);
    }
}

//用户密码异常
public class PasswordException extends RuntimeException {
    public PasswordException() {
        super();
    }

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

2.2 构造测试自定义异常类的代码

public static void main(String[] args)  {
        String userName = "zhangsan";
        String userPassword = "123456";

        System.out.println("请输入用户名:");
        Scanner scanner = new Scanner(System.in);
        String user = scanner.nextLine();
        System.out.println("请输入密码:");
        String password = scanner.nextLine();

        if (!user.equals(userName)) {
            throw new UserException("用户名错误!");

// 这个正常是可以用System.out.println("用户名错误!") 来代替的
//不过此时是以异常的形式展现出来,有利于调用者清楚明白代码错误发生的地方
        }

        if (!password.equals(userPassword)) {
            throw new PassWordException("用户密码错误!");
        }
        System.out.println("登录成功!");



}

测试结果:

这就是自定义异常类的作用,自定义异常类可以维护符合我们实际情况的异常结构。

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值