安利一波Java异常

1. 什么是异常?

对于异常,简单来说就是代码无法执行,或者执行不下去。在Java中,将程序执行过程中发生的不正常行为称为异常, 这是异常的概念。在Java异常体系结构中, Exception(异常)和Error(错误)是Throwable派生的两个重要子类,可以理解为Throwable是Java异常体系结构中的顶层类。而对于异常和错误它们是不同的,异常可以被开发人员捕捉和处理;而错误一般是系统错误,一般不需要开发人员处理(也无法处理),比如内存溢出。
在此,我们对异常进行扩展。

 public static void main(String[] args) {
        int[] arr = {1,2,3};
        System.out.println(arr[10]);  //数组下标越界异常

在这里插入图片描述
上面就属于数组下标越界异常,接下来还有一个常见异常空指针异常

 int[] arr1 = null;
 System.out.println(arr1.length); //空指针异常

这里在这里插入图片描述这里程序将会无法向下执行,并返回1或者-1(若程序无异常,执行完后会返回0),值得注意的是,当程序出现异常的话,就不会向下执行了。

 int[] arr = {1,2,3};
        System.out.println(arr[10]);  //数组下标越界异常
        System.out.println("这里将不会执行");

在这里插入图片描述

2. 异常的分类

异常分为编译时异常也叫受检查异常运行时异常也叫非受检查异常
顾名思义编译时异常是在程序编译期发生的异常,运行时异常就是程序运行时发生的异常。

(1)编译异常

常见的编译异常就如克隆异常

class Book implements Cloneable{        //实现克隆接口
    @Override
    protected Object clone() throws CloneNotSupportedException {    //重写克隆方法  当实现接口时要重写接口的方法
        return super.clone();
    }
}
 public static void main(String[] args) {
        Book book = new Book();
        Book book1 = (Book) book.clone();  //此时为编译异常  返回值是Object则要进行强制类型转换(向下转型)
    }


此时,会看到clone下面会报错,在程序还没运行就产生异常,这就是编译时异常。

(2)运行时异常

对于运行时异常就如数组下表越界异常和空指针异常,见文章开头。
在此说明RunTimeException以及其子类对应的异常,都称为运行时异常(其中RunTimeExceptionEexception的子类)。比如:NullPointerExceptionArrayIndexOutOfBoundsException、ArithmeticException

3.异常的处理

既然发现了问题,那么就要解决问题,对于异常的处理,在Java中,异常处理主要的5个关键字throw、try、catch、finally、throws
处理分为两种方法:

(1)异常的抛出

对于异常的抛出通常用throw关键字进行抛出,下面为抛出语法:

throw new XXXException("异常产生的原因");

比如获取数组元素中的值,那么就可能会存在数组下标越界或者数组为空的异常出现。

public static void main3(String[] args) {      //异常的抛出
        int[] arr1 = null;
        if(arr1 == null){
            throw new NullPointerException("数组为null");  //异常的抛出,将错误信息展现出来
        }

        int[] arr2 = {1,2,3,4};
        int i = 7;  //获取数组中i下表位置的元素
        if(i<0 || i>=arr2.length){
            throw new ArrayIndexOutOfBoundsException("i不是数组的下表");  //抛出异常
        }

    }

在这里插入图片描述
在这里插入图片描述
这样通过throw关键字就能将异常显示出来,对于一个大工程来说运行后会出现好多代码的输出,这样以来若出现异常,就能使我们很快的找出程序的错误,以便修改程序。
但对于这样的方法,异常还是没用处理,只是警示出来,并没用实际化解决,接下来就是第二个方法:

(2)异常的捕获

对于异常的捕获分为throws关键字声明异常try-catch关键字捕获并处理异常。

1>throws关键字声明异常

在这里,我们实现对于一个简单的登录界面进行异常声明

class User{   //异常的捕获(throws关键字)使用的类
    String name = "wang"; //登录的正确名字,不会出现异常
    public void login(){
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入名字:");
        String userName = scanner.nextLine();
        if(!userName.equals(name)){
            throw new UserNameException("出现异常,登录姓名错误"); //在我的编译器里面为24行,这个异常为我自定义的异常,后面会展开说明
        }
    }
}

public static void main5(String[] args) throws NullPointerException{  //异常的捕获(异常声明throws关键字,并不会处理解决异常)
       User user = new User();
       user.login();    //在我的编译里面为31行
    }

在这里插入图片描述这里面会看到两行进行异常警告,24行和31行。throws处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常。 其中24行为异常的抛出,而异常没有得到解决,就会转到方法的调用处也就是31行来进行判断,而31行也没解决,则就会再次抛出异常。
注意: 1. throws必须跟在方法的参数列表之后
2. 声明的异常必须是 Exception 或者 Exception 的子类
3. 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。例如
:public void Book(String s) throws NullPointerException,ArrayIndexOutOfBoundsException{}
这里还会发现这种还是不能实质性处理异常,若你是用户,这样抛出异常就只有你自己来进行解决才能使程序继续运行下去,那么作为用户我们就是要一个完整的程序,为什么出现异常还要我们自己解决呢?这显然是不符合实际的。接下来Java就给我们又提供了一个异常处理的方法

2>try-catch关键字捕获并处理异常

语法:

try{
      //可能出现异常的代码
}catch(要捕获的异常类型 e){
     //若可能出现异常的代码出现异常,那么如果这个异常符合要捕获的异常类型,就会执行这个catch里面的语句,这个语句一般存放处理并解决这个异常的操作
}catch(要捕获的异常类型 e){
   //catch语句可以有多个,但至少要有一个
   //若前面没有捕获到异常,则进行和这个catch要捕获异常类型的判断,若符合就执行操作,否则终止程序
}finally{
   //若有finally语句(可加可不加),这里面的语句一定会执行,不管是否被捕获到
}
//这里的代码若异常捕获成功(当然也就会被处理),这里的后续代码一定会被执行
//若被捕获没处理(即不符合异常类型),则后续代码不会执行

 public static void main(String[] args) {    //异常的捕获(try-catch方法,在catch中进行异常的处理解决)
        try{  //将可能出现异常的语句放到里面
            int[] arr1 = null;
            System.out.println(arr1.length); //空指针异常
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("数组下表越界异常异常");
            System.out.println("此处对异常进行处理,处理过后执行跳出后的代码");
        }catch (NullPointerException e){
            System.out.println("代码空指针异常");
            System.out.println("此处对异常进行处理,处理过后执行跳出后的代码");
        }finally {
            System.out.println("此处代码不管异常有没有被捕获或处理,该段代码一定会执行");
        }
        //此处为接下来要执行的代码
        //当异常被捕获并处理后,就会执行接下来的代码,没被处理(即捕获类型不符),接下来的代码将不会执行
        System.out.println("异常处理解决完成后,执行此处的代码");
    }

在这里插入图片描述
若想对异常进行打印输出则可加上e.printStackTrace();
在这里插入图片描述

在这里插入图片描述
这样一来异常的捕获和处理就欧克了,不用用户自己修改了,更加符合实际。
还要注意:
**1.**如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM(Java虚拟机)收到后中断程序----异常是按照类型来捕获的。
**2.**如果多个异常的处理方式是完全相同, 也可以写成这样:

catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
...    //这里用符号 | 进行连接
}

**3.**不能将父类异常捕获写到子类异常捕获前面,例如:
在这里插入图片描述
这样程序就会出错。
4.既然父类异常捕获包含所以子类异常捕获那么只写一个catch语句(父类异常捕获)可以吗? 当然是可以的,但是这样不推荐,就好像“没捕获一样”。
5.当捕获成功后,后续代码还会执行,那么为什么还要有finally呢?
显然是 有必要 的,当捕获成功后,会处理异常,若catch语句中有return语句,那么将不会执行后续代码, 若一个程序执行完要释放资源,而执行释放资源的代码当然不能写在try-catch语句中(因为它是捕获并处理异常的语句,而释放资源语句一般要放到末尾进行用完后释放),如果没finally语句,将会出现资源泄漏。在这里finally语句就是起到释放资源的作用会将释放资源语句放到finally语句中。

(3)异常处理流程

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

4.自定义异常类

虽然Java中有很多异常类,但在有的程序实现中它们并不是“最简”,或者不能完全满足我们的要求,这是就需要我们自己定义异常类来进行程序异常捕获。 我们经常登录都会需要用户名和密码,那么就要先进行登录判断才能进行下一步操作,在这里我们就可以认为输入用户名或者密码错误后给用户抛出一个异常来提醒用户输入错误。 下面是代码实现:

public class Test3 {
    String name = "zhang";   //正确用户名
    String password = "12344";  //正确密码
    public  void login(){
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String userName = scanner.nextLine();
        if(!userName.equals(name)){
            throw new UserNameException("用户名输入错误");
        }

        System.out.println("请输入密码:");
        String userPassword = scanner.nextLine();
        if(!userPassword.equals(password)){
            throw new UserPasswordException("密码输入错误");
        }
    }
    public static void main(String[] args) {
        Test3 test3 = new Test3();
        try{
            test3.login();      //表示这块可能会出现异常
        }catch (UserNameException e){
            e.printStackTrace();
            System.out.println("用户名输入异常");
        }catch (UserPasswordException e){
            e.printStackTrace();
            System.out.println("密码输入异常");
        }
    }
}

首先对登录界面进行操作,记录一个正确用户名和密码用于异常判断。然后用try-catch方法进行异常的抛出和处理。
下来就要实现自定义异常类实现:

public class UserNameException extends RuntimeException{
    public UserNameException(String s){   //对用户名自定义异常类
        super(s);
    }
}

public class UserPasswordException extends RuntimeException{
    public UserPasswordException(String s){  //对密码自定义异常类
        super(s);
    }
}

这样一个简单自定义异常类判断就欧克了。
在这里插入图片描述在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值