Java:认识异常

一、异常的概念

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

Java当中,描述异常是根据类来进行描述的!!!不同的类表示不同的异常。

如图:

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

Error:是指Java虚拟机无法解决的严重问题,比如StackOverflowError,一旦发生回天乏术

Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。比如感冒、发烧,这种日常中的小病在Java中称之为Exception 

1、Error

如图下的代码:

 程序运行:

系统会不停地调用func(),每次调用都会在栈上开辟内存,最后内存被挤爆,因此会出现错误! (StackOverflowError)这个就是栈溢出错误!



2、Exception

Exception其实还分为两种情况:运行时异常编译时异常! 



运行时异常(RuntimeException类)

运行时异常就是程序在运行过程中报的异常。

举个例子:

 程序运行:

程序在run(运行)时才发现,array这个数组没有指向任何对象,因此无法计算数组长度!



 编译时异常:(Exception类)

编译时异常就是程序在编译的时候发生的异常!

举个例子:

如图:

代码用红色波浪线提示,鼠标指向波浪线,然后会出现异常(exception)提示,程序还未执行就报异常错误,这个就是编译时异常!

注意:

编译时出现的语法性错误,并不能称之为异常!(如关键字拼写错误)

就像这个,命名了两个相同名字的变量,这种是编译时的语法问题,并不是异常!



二、异常的处理 

既然异常出现了,那么我们就要想办法处理!处理异常有一个前提,那就是抛出异常!

1、抛出异常分为两类:

1、程序本身触发异常sho

2、手动抛出异常----->>(throw)

 第一种就是我们前面所说的,程序会自己抛出异常!下面我们介绍如何手动抛出异常!

让我们看看实例:(以非受查异常为例子)

如果func()方法传入的数组指向null,抛出一个非受查异常(运行时异常)

public class Test1 {

public static void func(int [] array){
    if(array==null){
        throw new RuntimeException();
    }
}
    public static void main(String[] args) {
      int [] array=null;
      func(array);
    }
}

程序运行结果:



再来看看一个实例:(以受查异常为例)

如图:

如果func()方法传入的数组指向null,抛出一个受查异常(编译时异常)

为什么会出现红色波浪线?

答:这是因为我们抛出的是一个受查异常(编译时异常),规定受查异常必须经过处理才能运行! 

2、解决方法:

一、添加异常的声明操作

此时,在main方法中调用func()方法的时候!也会出现红色波浪线!

 那是因为,如果main方法调用了func方法,main方法也要进行异常的声明!

添加声明后:

二、try......catch...... 

 虽然按照方法1添加声明后程序不会报错,但是真正的异常还没有处理!此时异常交给JAM处理了,但是JAM没有处理,它的处理方式就是直接崩溃!

那么该如何处理?

答:利用try......catch......

让我们看看实例:

try{

//可能出现异常的代码块

}  catch(要捕获的异常类型  e){

//对异常进行处理

 }

public class Test1 {

//func方法依旧要添加异常声明!
public static void func(int [] array) throws Exception {
    if(array==null){
        throw new Exception("传个参数看看");
    }
}

//main方法不用添加异常声明,但是要使用try...catch...处理异常!
    public static void main(String[] args) {
    try{
        //存放可能出现异常的代码
        int [] array=null;
        func(array);
    }catch(Exception e){
            System.out.println("捕获到了Exception异常" +
                    "此时这里可以开始处理异常了");
    }
//异常解决后,正常执行后面的语句
        System.out.println("异常处理结束,程序继续进行!");
}
}

程序运行结果:

可以看到,异常解决后,后面的语句仍然可以正常执行!

另外,如果我们要看看异常是在哪里抛出的,可以使用e.printStackTrace()打印出来:



异常处理流程:

程序会先执行try中的代码-->如果try中的代码出现异常,就结束try中的代码,看和catch中的异常是否匹配 



 3、异常类型不匹配

我们知道,异常分为很多种类型:空指针异常,算术异常......如果try{}里面的异常类型,与catch()里面写的要捕获的异常类型不匹配,程序还是无法解决异常的!

比如:

如下出现的其实是一个空指针异常,但是catch却要捕获一个算术异常。

那么:程序无法捕获异常,异常交给JAM处理,程序直接崩溃!

 程序运行:(最后一条语句的1,表示不正常退出

改进:

这里将算术异常改为空指针异常,程序就可以正常处理异常!

 程序运行:(最后一条语句的0表示正常退出



4、合并异常

1、 在实际使用过程中,为了同时捕获多种异常,我们可以使用多个catch,捕获不同的异常!

注:使用多个catch时,如果要捕获的异常具有父子类关系,那么一定要把子类异常写在前面,父类异常写在后面! 

如图:



2、也可以将不同的异常类型合并为同一个!(使用‘|’ 连接) 



 三、finally的作用

在写程序时,有些特定的代码,无论程序是否发生异常,都需要执行,比如程序中打开了文件,在程序退出的时候就需要关闭文件。

但是,程序抛出异常可能会导致异常发生处之后的语句无法执行,因此,需要finally来解决这个问题!

注意:finally{}是紧跟在catch(){}后面写的!

让我们看看finally的作用:

如下代码会触发算术异常(0不能作为被除数)

此时程序会抛出异常,看看finally里面的语句是否会被执行?

可以看到,finally里面的语句被执行了,说明异常的抛出不会影响finally里面语句的执行!

 那么,有一个问题,假如程序不抛出异常,finally里面的语句是否还会执行? 

答:会执行!

如图:

可以看到,我这里把会触发算术异常的错误语句去除,因此程序不会抛出异常了!

此时看看finally里面的语句是否会执行?

我们发现:finally里面的语句正常执行了!那么我们得出一个结论:

无论程序是否抛出异常,finally里面的语句都会正常执行!



 通过前面try.....catch......的学习,如果你记忆力好的话,那么你就会发现try......catch......处理好异常后,其后面的正常语句还是可以正常运行的,那么,又何必使用finally{ }呢?

答:这是因为,前面举的例子并不能很好地体现finally的特性!

我再举个例子:

与前面例子不同的是:这里try里面的语句有了return;语句,那么,这个时候,你再看看try......catch......后面的正常语句还会不会执行?

程序运行结果:

如图可知:try......catch.....后面的正常语句并没有打印出来“关闭文件!“也就是说它不会执行。

 这个时候,你使用finally试试!



 四、自定义异常类

  在Java中,设置了各种异常类型,但是在实际使用过程中,我们可能会出现许多种Java中规定以外的异常,为了满足实际需要,我们可以自定义异常类型!

 举个例子:

我们正在创建一个用户登录账号的功能!要求用户输入正确的用户名和密码!

要求:

如果输入用户名错误,抛出一个用户名异常!

如果密码输入错误,抛出一个密码异常!

实现方法如下:

1、在Login类中创建成员变量useName(用户名)和password(密码),并且赋值!

2、实现一个LoginInfo方法:如果用户名输入错误,抛出用户名异常,如果密码输入错误,抛出密码异常!

class Login{
    private String useName="admin";
    private String password="123456";

    public void LoginInfo(String useName,String password) throws UserNameException,PasswordException {
        if(!this.useName.equals(useName)){
            //如果用户名与输入的用户名不匹配,抛出用户名异常
          throw new UserNameException("用户名异常!");
        }
        if(!this.password.equals(password)){
            //如果密码与输入的密码不匹配,抛出密码异常
            throw new PasswordException("密码异常!");
        }

    }
}

注意:

这个LoginInfo方法后面添加throws UserNameException,PasswordException 语句的原因后面解释!在这里请先注意到这个点!



    这个用户名异常类型(UserNameException)和密码异常类型(PasswordException)固然不是Java规定的类型!而是我们自定义的!

因此,我们还需要自定义异常类!

具体做法:

1、新建两个Java Class类:UserNameException和PasswordException

2、这两个类要继承一个父类Exception(编译时异常,也叫受查异常)或RuntimeException(运行时异常,也叫非受查异常)

3、在类中创建两个构造方法,一个带参数,一个不带参数!(具体写法参考下图代码!)

//用户名异常类
public class UserNameException extends Exception{

    public UserNameException(){

    }
    public UserNameException(String msg){
        super(msg);
    }
}
//密码异常类
public class PasswordException extends Exception{
    //创建两个构造方式:一个不带参数和一个带参数
    public PasswordException(){

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

上面我们留下的一个小问题:

为什么在LoginInfo方法后面添加throws UserNameException,PasswordException 语句?

答:因为用户名异常和密码异常继承的父类都是Exception(编译时异常,也叫受查异常),前面的学习的添加异常声明的时候,你还记得为什么要添加异常声明吗?

是因为我们抛出的是一个受查异常!这个用户名异常类密码异常类继承的异常类正是受查异常类!规定抛出受查异常的方法必须要添加异常的声明!



这样,自定义异常就完成了!让我们在main方法中看看效果!

1、调用LoginInfo方法时输入错误的用户名和密码!

程序运行:

程序抛出用户名异常!

 

2、调用LoginInfo方法时输入正确的用户名和密码!

程序运行:

可以看到,程序正常运行

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值