Java之异常

目录

异常的概念

异常层次

异常事件类型

常见的异常类

异常处理机制 

Java异常处理过程——捕获异常

Java异常处理过程——声明异常throws 

Java异常处理过程——throw 

自定义异常类


异常的概念

异常是例外,是一个程序在执行期间发生的事件,它中断正在执行程序的正常指令流。软件开发过程中,很多情况都会导致异常的产生,例如对负数开平方根、对字符串做算术运算、操作数超出范围、数组下标越界等。

也就是程序没有按照我们预想的结果运行,在运行过程中发生了错误

在JAVA中,一切都是对象,所以异常也是对象,所以异常也是有类的

异常层次

异常事件类型

异常事件可以分为受检异常非受检异常

  • Error(错误) 程序的内部错误,一般来说,出现了这种错误,程序就无法正常的运行下去,只能退出程序 程序员是无法处理的,也不定义Error的子类 比如OutOfMemory堆内存溢出错误 StackOverError是栈溢出错误
  • RuntimeException(运行时异常):编译的阶段不会报错,运行时才会报错,比如数组越界,空指针异常和类型转换异常,Java编译器会自动按照异常产生的原因引发相应类型的异常,程序中可以选择捕获处理也可以不处理,虽然Java编译器不会检查运行时异常,但是也可以去进行捕获和抛出处理。
  • 编译时异常(受检异常)Exception子类中除RuntimeException及其子类之外的异常,该异常必须手动在代码中添加捕获语句来处理该异常。编译时异常也称为受检异常
  • Error和RuntimeException都是非受检异常,编译阶段可以不显示进行异常处理(try...catch/throws)

常见的异常类

异常处理机制 

什么叫异常处理机制?
前面定义过:异常是程序由于各种原因导致无法正常运行的一个事件,这个事件是由一个异常对象来代表的,这个对象的产生取决于产生异常的类型,可能由应用程序本身产生,也可能由Java虚拟机产生。异常事件是 Throwable 类或其子类的实例(异常对象),异常处理机制就是当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理,异常处理机制并不能真正的修复程序的bug,只能说是防止程序崩溃,要修复程序的bug时需要我们在catch语句中对异常进行一些处理。

定义:Java是采用面向对象的方式来处理异常的。处理过程:

  • (1)抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给运行时系统(JRE)。
  • (2)捕获异常:JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。
  • 限制:Exception 中除 RuntimeException 及其子类之外的异常一般需要进行异常处理
     

Java异常处理过程——捕获异常

基本语法

try {
   // 可能会产生异常的代码,比如除以0,数组越界,空指针等
	}
    catch (ExceptionType1 e){
    // 对ExceptionType1的处理
    }
    catch (ExceptionType2 e){
    // 对ExceptionType2的处理
    }
	...
    [finally]{
    //异常的出口,最终都会来执行这个代码块
    }
  • try语句中存放的是可能发生异常的语句。当异常抛出时,异常处理机制负责搜寻参数与异常类型相匹配的第一个处理程序,然后进入catch语句中执行,此时认为异常得到了处理。如果程序块里面的内容很多,前面的代码抛出了异常,则后面的正常程序将不会执行,系统直接catch捕获异常并且处理
  • catch语句可以有多个,用来匹配多个异常,捕获异常的顺序与catch语句的顺序有关,当捕获到对应的异常对象时,剩下的catch语句不再进行匹配,因此在安排catch语句的顺序时,首先应该捕获最特殊的异常,然后一般化。catch的类型是Java语言定义的或者程序员自己定义的,表示抛出异常的类型。异常的变量名表示抛出异常的对象的引用,如果catch捕获并匹配了该异常,那么就可以直接用这个异常变量名来指向所匹配的异常,并且在catch语句中直接引用。部分系统生成的异常在Java运行时自动抛出,也可通过throws关键字声明该方法要抛出的异常,然后在方法内抛出异常对象。
  • final语句为异常提供一个统一的出口,一般情况下程序始终都要执行final语句,finally在程序中可选。finally一般是用来关闭已打开的文件和释放其他系统资源。try-catch-final可以嵌套。
     

我们拿一个数组越界来看捕获异常的使用

使用try...catch..处理异常

 首先我们发现了当在try代码块中发现了异常,就转去执行对应catch代码块的代码,不会执行try发生异常之后的代码

 异常也是类,只有发生的异常有相对应的异常对象,才能捕获这个异常,并处理

我们发现,catch处理的异常是类转换异常,但是发生的数组越界,所以并不会去处理这个发生的异常,所以会导致程序结束,所以我们可以用多个catch来对应多种异常类型,对应不同的处理

 匹配多个异常的catch语句

public class Example6_1 {
        public static void main(String[] args) {
            try {
                int i =Integer.parseInt(args[0]);
                int a = 10/i;
                System.out.println("a="+a);
            }
            catch (ArrayIndexOutOfBoundsException e){
                System.out.println("没有输入数字串");
            }
            catch (ArithmeticException e){
                System.out.println("分母不能为0");
            }
            catch (NumberFormatException e){
                System.out.println("输入格式不正确");
            }
            catch (Exception e){
                System.out.println("异常"+e);
            }
        }
}

多重捕获

public class Example6_1 {
    public static void main(String[] args) {
        try {
            int i = Integer.parseInt(args[0]);
            int a = 10 / i;
            System.out.println("a=" + a);
        } catch (ArrayIndexOutOfBoundsException | NumberFormatException e) {
            System.out.println("没有输入数字串或输入格式不正确");
        } catch (ArithmeticException e) {
            System.out.println("分母不能为0");
        } catch (Exception e) {
            System.out.println("运行异常" + e);
        } finally {
            System.out.println("finally block");
        }
    }
}

异常的向上转型使用 

因为所有的异常类(不包括Error)都是 Exception的子类,所以所有的异常都可以由它的父类来接收,但是不太推荐这种方法

  •  若此时清楚try中可能发生的异常,那就用catch去捕获明确的相应异常类型
  • 当我们不清楚try中可能会发生异常,那我们就可以使用Exception这个父类来接收所有的可能产生子类异常对象
  • 通常,为捕获到所有可能出现的异常,可以在处理异常的末尾,加上Exception 类,这样即可以使所有异常都被捕捉到,也可以防止想捕获具体异常时被它提前捕获。

关于错误的堆栈信息

finally代码块 

 说明不论怎么样,都会在最后去执行finally代码块的内容(finally的return30覆盖了catch的return 20),所以不要在finally中写return语句

当try中抛出异常且catch中有return语句,finally中没有return语句,java先执行catch中非return语句,再执行finally语句,最后执行return语句。若try中没有抛出异常,则程序不会执行catch体里面的语句,java先执行try中非return语句,再执行finally语句,最后再执行try中的return语句。
 

  • 所以final一般用来关闭已经打开的文件或者资源的释放
  • 1finally语句块中发生了异常 2前面的代码中执行了System.exit()退出程序 3程序中所在的线程死亡 4关闭CPU 只有发生这四种种情况才不会执行finally
  • 如果finally中定义的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块中之前保存的值;如果finally中定义的数据是是引用类型,则finally中的语句会起作用,try中return语句的值就是在finally中改变后该属性的值。

异常的处理过程

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

 使用场景

针对需要处理的异常,采用捕获的方式去处理异常。

Java异常处理过程——声明异常throws 

定义:用在方法声明上,表示该方法可能会产生的异常类型,但是在本方法中不处理,若异常产生把异常抛给调用者,可以是多个异常类型

语法

 static void pop() throws Exception1,Exception2,Exception3{
        //方法体
    }

例子

重点

  • 非检查异常(运行时异常)可以不使用throws关键字来声明要抛出的异常,编译也可顺利通过,但在运行时会被系统抛出。
  • 受检异常(编译时异常),必须使用try-catch/throws处理,否则会导致编译错误。
  • 仅当抛出了异常,该方法的调用者才必须处理或重新抛出该异常。当方法的调用者无力处理该异常时,应该继续抛出而不是直接调用方法。
  • 调用方法必须遵循一个原则:若覆盖一个方法,则不能声明与覆盖方法不同的异常,声明的任何异常必须是被覆盖方法所声明异常的同类或子类。如下例子:
     

Java异常处理过程——throw 

定义:用在方法内部,表示人为产生异常对象并抛出

结构语法

  • 关键词是throw,抛出的是一个异常对象,所以需要new一个对象,而且一个throw只能抛出一个异常对象
  • throw在方法体中,程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里到外寻找含有与其匹配的catch子句的try块。
  • new 的异常类名称必须是Throwable或其子类
throw new 异常类名称("描述信息")

例子

throw后面不能带其他代码

throws与throw的区别

  • throw 在方法体内使用,throws 函数名后或者参数列表后方法体前
  • 意义 : throw 强调动作,而throws 表示一种倾向、可能但不一定实际发生
  • throws 后面跟的是异常类,可以一个,可以多个,多个用逗号隔开。throw 后跟的是异常对象。
     

使用场景

  • 针对不知道如何处理的异常,采用抛出的方式去处理。
  • throw可抛出系统自定义异常,通常情况下,是用来抛出用户自定义的异常。
  • throw后面不能跟其它代码块,否则编译不能通过,但是可以在finally语句块中有return语句,finally语句可以让throw和return共存

自定义异常类

定义

JDK中已经帮我们定义好了很多的异常类,但是在某种业务情况下,出现的错误需要我们自定义异常类(比如登录账号时,密码不对,这种错误需要我们自己去自定义异常类) 

在程序中使用自定义异常的步骤

  • 创建自定义异常类
  • 在方法中通过throw抛出异常对象
  • 如果在当前方法中对抛出的异常对象作处理,可以使用try-catch语句块捕获抛出异常对象并且处理,否则要在方法的声明处通过throws关键字指明要抛出给方法调用者的异常。
  • 在出现异常方法的调用者中捕获并处理异常

语法

class UserException extends Exception {
    UserException(){
        super();
        ...//其他语句
    }
}
  • 自定义异常类非常简单,我们如果想要它是受查异常,必须在程序中显式处理,就继承Exception
  • 如果想让这个异常不需要显示处理,就继承RunTimeException父类

例子

package exception_test;

import java.util.Scanner;

public class PasswordException {
    public static void main(String[] args) {
        try {
            Scanner sc= new Scanner(System.in);
            System.out.println("请输入账号");
            String account=sc.nextLine();
            if ("lsc".equals(account)){
                System.out.println("请输入密码");
            }else {
                throw new AccountError("账号不正确");
            }
            String password=sc.nextLine();
            if (password.equals("12345")){
                System.out.println("登录成功");
            }else {
                throw new PasswordError("密码错误");
            }
        } catch (PasswordError passwordError) {
            passwordError.printStackTrace();
        } catch (AccountError accountError) {
            accountError.printStackTrace();
        }
    }
}
class PasswordError extends Exception{
    public PasswordError(String msg) {
        super(msg);
    }
}
class AccountError extends Exception{
    public AccountError(String msg){
        super(msg);
    }
}

异常的快捷方式 

  • 打开资源的一种快捷方式

 

 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

库里不会投三分

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值