边学边记——Java中的异常

目录

一.概念

1.定义及理解

2.示例

(1).算术异常

(2).数组越界异常

(3).空指针异常

二.异常的体系结构

1.结构框图

2.分类

(1).受查异常

(2).非受查异常(运行时异常)

三.异常的处理

1.异常的捕获try-catch

(1).处理过程

(2).具体操作示例

(3).补充——printStackTrace方法和错误输出

2.异常的抛出——throws和throw

(1).throws的用法

(2).throw的用法

3.finally代码块

4.异常的处理流程

四.自定义异常类 

1.背景

2.步骤 

3.示例


一.概念

1.定义及理解

在Java中,将程序执行过程中发生的不正常行为称为异常。

        在C语言中,每当遇到程序中有错误时,程序就会异常退出,从出现错误的这一行往后的代码都无法执行,必须检查并处理好该错误后,程序才能正常执行,其实这样是存在问题的。

        但是在现实生活中,假如我们登录某个系统,需要输入密码,每当你输错密码后,整个登录界面都关闭,需要你重新打开登录系统,这样就很麻烦。所以正常情况下,当我们输入的密码错误时系统不应该关闭,而是提示我们 " 输入的密码错误!" 才对。

        也就是说,其实很多时候,我们在编写程序时出现的是逻辑错误,是可以通过编译(语法错误无法通过编译)的,此时程序不应该直接退出,因此,Java中有一种处理异常的机制,用于捕获异常,使剩下正确的代码能够继续运作,程序能够正常执行。

2.示例

在Java中一切皆是类,异常也是类,每种异常都有对应的类来描述。

(1).算术异常

System.out.println(10/0);

(2).数组越界异常

int[] arr={1,2,3};
System.out.println(arr[100]);

(3).空指针异常

String str=null;
System.out.println(str.length());

二.异常的体系结构

1.结构框图

  • Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception。
  • Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:StackOverflowError和OutOfMemoryError,一旦发生回力乏术。
  • Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说的异常就是Exception。

2.分类

根据异常发生的时机,分为两类:

(1).受查异常

在程序编译阶段需要显示进行异常处理的异常类。

编译阶段必须显示进行异常处理! 

(2).非受查异常(运行时异常)

在程序运行时发生的异常,不需要显示异常处理的异常都是非受查异常。

三.异常的处理

没有进行异常处理之前的情况:

以算术异常为例:

1.异常的捕获try-catch

//处理异常的格式
try{
    //所有可能产生异常的代码块
}catch (/*要捕获异常的类型*/){
    //出现异常之后执行的代码
}finally {
    //无论是否产生异常,一定会执行的代码块
}
//其中finally部分的代码块不是必需代码块

(1).处理过程

        当异常产生时,try中的代码块不再执行,然后去catch中匹配具体的异常类,若匹配成功,则继续执行从catch开始剩下的代码块;若没有相关的catch代码块与出现的异常进行匹配,程序退出。 

(2).具体操作示例

eg

System.out.println("异常发生之前的代码");
try{
    //产生异常,不处理的话程序终止,后续代码不再执行
    System.out.println(10/0);
}catch (ArithmeticException e){
    System.out.println("异常发生了");
}
System.out.println("异常发生之后的代码");

不匹配的情况:

catch代码块只会捕获相对应的异常,若try中出现的异常不是当前catch对应的异常,程序还是会中断,此时可以考虑使用多个代码块。

eg

System.out.println("异常发生之前的代码");
try{
    //产生异常,不处理的话程序终止,后续代码不再执行
    System.out.println(10/0);
}catch (ArithmeticException e){
    System.out.println("算术异常发生了");
}
try{
    String str=null;
    System.out.println(str.length());
}catch (NullPointerException e){
    System.out.println("空指针异常发生了");
}
System.out.println("异常发生之后的代码");

注意1:若多个代码块出现父子关系,则一定是子类写在前面,父类写在后面,父类相当于保底处理,顺序一定不能变,否则子类异常相当于白写。

eg

若此时不是算术异常,也不是空指针异常,就会到最后的Exception异常处。

注意2:每捕获一次异常,就需要一个对应的try和catch代码块,因为try中只会处理第一次遇到的异常,如果有多个异常是不会进行处理的。

eg

(3).补充——printStackTrace方法和错误输出

printStackTrace方法:

捕捉到相关异常后,若想打印异常产生的错误原因以及出现位置,可以调用异常对象的printStackTrace方法。 

 错误输出:

System.err.println();

错误输出,会在控制台输出红色内容,优先级低于标准输出。 

eg: 

//printStackTrace方法
System.out.println("异常发生之前的代码");
try{
    //产生异常,不处理的话程序终止,后续代码不再执行
    System.out.println(10/0);
}catch (ArithmeticException e){
    //错误输出
    System.err.println("算术异常发生了");
    //打印出具体的异常原因以及产生位置
    e.printStackTrace();
}
System.out.println("异常发生之后的代码");

2.异常的抛出——throws和throw

(1).throws的用法

  • throws用在方法声明上,明确表示当前方法可能产生的异常,但是当前方法不处理,若异常产生,将异常抛出给调用者。
  • throw用在方法内部,表示人为进行异常对象的产生,一般和自定义异常搭配使用。

eg: 

throws用在方法声明上,明确表示当前方法可能产生的异常,但是当前方法不处理,若异常产生,将异常抛出给调用者。

主方法处理异常——>主方法内部的其余代码可以正常执行结束。

public static void main(String[] args) {
    try {
        fun();
    }catch (ArithmeticException e){
        System.out.println("main方法中捕获算术异常");
    }
    System.out.println("程序正常退出");
}

public static void fun()throws ArithmeticException{
    //当异常产生,方法直接结束调用,将异常抛回给调用者
    System.out.println(10/0);
    System.out.println("方法结束");
}

PS:若主方法也不处理异常,则该异常就会抛回JVM,程序退出。 

注意:等同于catch可以捕获多个异常,throws也可以抛出多个异常,若抛出的异常有父子关系,只需要声明父类异常即可。

(2).throw的用法

throw用在方法内部,表示人为进行异常对象的产生并抛出,throw关键字就是由程序自己来产生异常对象(JDK中内置的异常类都是由JVM来产生的),当出现某种特殊情况时,程序内部自己来构造异常对象。 

eg:

比如实现一个二分查找,我们就可以在数组找不到数值时使用throw抛出异常。 

//二分查找
public static int binSearch(int[] arr,int toFind) throws NoSuchFieldException {
    int left=0;
    int right=arr.length-1;
    while(left<=right){
        int mid=left+((right-left)/2);
        if(arr[mid]>toFind){
            right=mid-1;
        }
        else if(arr[mid]<toFind){
            left=mid+1;
        }
        else {
            return mid;
        }
   }
    //此时若在数组压根没有找到toFind时,就可以抛出异常,比特定的返回值更加有意义
    //给任何返回值也不合适,toFind有可能成为任何数
    throw new NoSuchFieldException("在当前数组中没有找到该数值!");
}

public static void main(String[] args) throws NoSuchFieldException {
    int[] arr={1,2,3,4,5,6,7,8};
    System.out.println(binSearch(arr,-1));
}

3.finally代码块

finally代码块是无论如何都会执行的代码块,无论是否有异常发生,是否被正确的捕获到,在程序退出前,都会确保finally代码块执行过。

除了一种特殊情况:调用System.exit(),将JVM进程强制退出。

一般资源关闭操作,网络的关闭操作都放在fianlly代码块中,保证资源一定会关闭。

4.异常的处理流程

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

四.自定义异常类 

1.背景

JDK中内置了很多异常类,但是在程序开发过程中,很多场景下出现的问题,JDK中没有相关异常对应,此时我们就需要根据自己的需求来自定义异常类。

比如用户登录时,用户名或密码错误,都属于异常,但是JDK中没有与之相对应的异常类。

2.步骤 

  • 继承Exception(受查)或者RuntimeException(非受查)——>必须显示处理异常的继承Exceotion,可以不显示处理,就继承RuntimeException。
  • 实现一个带String的有参构造,String方法参数,就是异常的原因。

3.示例

public class UserLogin {

    private String username="生菜虾";
    private String password="123456";

    public void login(String username,String password) throws UserWordExcption,PassWordExcption{
        if(!this.username.equals(username)){
            throw new UserWordExcption("用户名错误!");
        }
        if(!this.password.equals(password)){
            throw new PassWordExcption("密码错误!");
        }
    }

    public static void main(String[] args) {
        UserLogin user=new UserLogin();
        try {
            user.login("生菜虾","1234567");
        }
        catch (PassWordExcption | UserWordExcption e){
            e.printStackTrace();
        }
    }
}

//密码异常,受查异常
class PassWordExcption extends Exception{
    public PassWordExcption(String msg){
        super(msg);
    }
}

//用户名异常,非受查异常
class UserWordExcption extends Exception{
    public UserWordExcption(String msg){
        super(msg);
    }
}

若有建议或想法,欢迎一起讨论学习~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值