十三、异常及异常的处理

java.lang.Exception 类继承自 Throwable

1 异常的定义

  • 异常就是程序在编译或运行期间,所产生的一种不正常的结果。
  • 异常指程序在运行过程中出现的非正常现象,例如用户输入错误、除数为0、需要处理的文件不存在、数组下标越界等。
  • 在Javca的异常处理机制中,引进了很多用来描述和处理异常的类,称为异常类。
  • 异常类定义中包含了改类异常的信息和异常进行处理的方法。
  • 所谓异常处理,就是指该程序在出现问题时依然可以正确的执行完。
  • 异常对程序产生的影响:当发生异常的时候,程序就会终止,异常代码之后的程序将不再执行。

2 Java中异常的体系结构

异常都是从 Throwable 类派生出来的,而 Throwable 类是直接从 Object 类继承而来。
在这里插入图片描述

2.1 异常的分类(通常)

在这里插入图片描述
在这里插入图片描述

  1. Error:系统内部错误,这类错误由系统进行处理,程序本身无需捕获处理。
  2. Exception:可以处理的异常。
  3. RuntimeException:可以捕获,也可以不捕获的异常。
  4. 继承Exception的其他类:必须捕获,通常在API中会说明这些方法抛出哪些异常。
  • 运行时异常:(父类Exception)

    • ClassCastException 错误的数据类型转换
    • IndexOutOfBoundsException list集合索引越界
    • ArrayIndexOutOfBoundsException 数组访问越界(继承自上面)
    • NullPointerException 空指针异常
    • ArithmeticException 算数异常
    • ArrayStoreException 数据存储异常,操作数组时类型不一致
    • BufferOverflowException 字节溢出异常—IO流操作
    • InputMismatchException 输入不匹配异常
    • NumberFormatException 数字格式异常
  • 编译时异常:

    • ClassNotFoundException 类找不到异常
    • FileNotFoundException 编译文件夹中找不到,就是发布到tomcat中的,不是工程中
    • SQLException 提供有关数据库访问错误或其他错误的信息的异常。( 比如SQL语句写错,访问的表不存在,连接数据库失败等等)
    • IOexception IO流异常。一般在读写数据的时候会出现这种问题。
    • EOFException 输入过程中意外到达文件或流的末尾时,抛出此异常。

2.2 常见的RuntimeException

  1. 错误的类型转换
public class ThrowTest {
    public static void main(String[] args) {
        //错误的类型转换
        Object obj = new String();
        Integer inte = (Integer)obj;
    }
}
Exception in thread "main" java.lang.ClassCastException
  1. 数组访问越界
public class ThrowTest {
    public static void main(String[] args) {
        //数组访问越界
        int[] arr = new int[5];
        System.out.println(arr[6]);
    }
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
  1. 访问null指针
public class ThrowTest {
    public static void main(String[] args) {
        //访问null指针
        String str = null;
        System.out.println(str.equals("java"));
    }
}
Exception in thread "main" java.lang.NullPointerException
  1. 算术异常
public class ThrowTest {
    public static void main(String[] args) {
        //算数异常
        System.out.println(1/0);
    }
}
Exception in thread "main" java.lang.ArithmeticException

3 异常的处理

Java 中对异常的处理提供了一种异常处理模型:抓抛模型。
编译器异常继承:Exception
运行时异常继承:RuntimeException

3.1 捕获异常

try{
		包含有可能发生异常的代码
}catch(异常类型 变量){  //捕获
		针对这种异常的处理
}finally{
		无论程序是否发生异常,都需要执行的代码
}

实例1:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ThrowTest {
    public static void main(String[] args){
        // 解析异常 ParseException
        String strDate = "2020-12-23 " ;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;  // 有可能发生异常,必须处理,不处理程序无法正常执行
        try {
            date = sdf.parse(strDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println(date);
    }
}

运行结果:

java.text.ParseException: Unparseable date: "2020-12-23 "
	at java.base/java.text.DateFormat.parse(DateFormat.java:395)
	at ThrowDemo.ThrowTest.main(ThrowTest.java:14)
null

通过catch捕获异常,并对异常做出相应的处理,这样就可以保证程序继续执行。

在捕获异常时,可以有多个catch:

我们将可能出现异常的语句放到try{ }中

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ThrowTest {
    public static void main(String[] args){
        //运行时异常
        int[] arr = new int[5];

        //空指针异常
        String str = null;

        //类型转换异常
        Object obj = new String();

        //算术异常

        // 解析异常 ParseException
        String strDate = "2020-12-23 " ;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;  // 有可能发生异常,必须处理,不处理程序无法正常执行
        try {
            System.out.println(arr[5]);
            System.out.println(str.equals("abc"));
            Integer iter = (Integer)obj;
            System.out.println(1/0);
            date = sdf.parse(strDate);
        } catch (ParseException e) {
            System.out.println("进行了异常处理");
            e.printStackTrace();
        }catch (ArrayIndexOutOfBoundsException ae){
            System.out.println("处理数组下标越界异常");
        }catch(NullPointerException ne){
            System.out.println("处理空指针异常");
        }
        System.out.println(date);
        System.out.println("程序执行结束");
    }
}

运行结果:

处理数组下标越界异常
null
程序执行结束

从运行结果中可以看出,程序在处理完数组下标越界之后,try{ } 中的其他异常不再捕获。

多异常捕获的写法:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ThrowTest {
    public static void main(String[] args){
        //运行时异常
        int[] arr = new int[5];

        //空指针异常
        String str = null;

        //类型转换异常
        Object obj = new String();

        //算术异常

        // 解析异常 ParseException
        String strDate = "2020-12-23 " ;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = null;  // 有可能发生异常,必须处理,不处理程序无法正常执行
        try {
            System.out.println(arr[5]);
            System.out.println(str.equals("abc"));
            Integer iter = (Integer)obj;
            System.out.println(1/0);
            date = sdf.parse(strDate);
        } catch (ParseException | ArrayIndexOutOfBoundsException | NullPointerException e)  //仅限于jdk7以上
        {
            System.out.println("进行了异常处理");
            //e.printStackTrace();
        }
        System.out.println(date);
        System.out.println("程序执行结束");
    }
}

运行结果:

进行了异常处理
null
程序执行结束

通过异常的多态来捕获(因为Exception为这些异常的父类,所以可以用来替代):

try {
            System.out.println(arr[5]);
            System.out.println(str.equals("abc"));
            Integer iter = (Integer)obj;
            System.out.println(1/0);
            date = sdf.parse(strDate);
        } catch (Exception e) {
            System.out.println("进行了异常处理");
            //e.printStackTrace();
        }

这样写是错误的:

catch (ArrayIndexOutOfBoundsException | NullPointerException | Exception e) {
            System.out.println("进行了异常处理");
            //e.printStackTrace();
        }

但是可以这样写:

catch (ParseException e) {
            System.out.println("进行了异常处理");
            //e.printStackTrace();
        }catch (NullPointerException ne){

        }catch (ArrayIndexOutOfBoundsException ae){

        }catch (Exception ee){
            
        }

在处理异常时,并不要求抛出的异常同 catch 所声明的异常完全匹配,子类的对象也可以匹配父类的处理程序。比如异常 A 继承于异常 B,那么在处理多个异常时,一定要将异常 A 放在异常 B 之前捕获,如果将异常 B 放在异常 A 之前,那么将永远匹配到异常 B,异常 A 将永远不可能执行,并且编译器将会报错。

捕获异常:将有可能发生异常的代码写在try块中,当发生异常的时候,就会执行相应的catch块的内容,可以保证异常处理之后的代码的正常执行,从而使得程序可以正常终止。

在程序设计时,只需将有可能发生异常的代码放在try块中,而不要将没有异常发生的代码添加到try,这样会影响程序的执行的效率。

异常信息的分析:

在这里插入图片描述

异常信息的描述(一般用printStackTrace()):

返回值类型方法
StringgetMessage() 返回此throwable的详细消息字符串。
voidprintStackTrace() 将此throwable和其追溯打印到标准错误流。
StringtoString() 返回此可抛出的简短描述。

在异常体系中,所有的子类都没有具体方法,方法都是来自于Throwable:

	try {
            System.out.println(arr[5]);
            System.out.println(str.equals("abc"));
            Integer iter = (Integer)obj;
            System.out.println(1/0);
            date = sdf.parse(strDate);
        } catch (ParseException e) {
            System.out.println("进行了异常处理");
            e.printStackTrace();
        }catch (NullPointerException ne){
            ne.printStackTrace();
        }catch (ArrayIndexOutOfBoundsException ae){
            String message = ae.getMessage();
            System.out.println(message);
            String msg = ae.toString();
            System.out.println(msg);
            ae.printStackTrace();   //得到异常的全部信息
        }

数组下标越界异常:

Index 5 out of bounds for length 5
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
null
程序执行结束
java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
	at ThrowDemo.ThrowTest.main(ThrowTest.java:26)

编译期异常和运行时异常的区别

  1. 运行时异常不处理,不会影响程序的运行,而运行期异常则 须做出相应的处理,否则程序无法运行;

  2. 运行时异常往往都可以通过优化代码来进行规避,运行时异常的出现,都是我们的程序存在逻辑上的漏洞或者缺陷,在实际处理中处理的重点是编译期异常。

3.2 抛出异常

public class Test1 {
    public static Date str2Date(String strdate) throws ParseException {
        String strDate = "2020-12-23 " ;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse(strDate);
        return date;
    }
}

抛出异常是在方法的声明上通过throw关键字来进行声明。

告诉方法的调用者,该方法存在这样类型的异常,throw后面跟的是异常类型。

在throws后边可以跟多个类型异常

抛出异常是抛给了方法的调用者,此时对于方法的调用者来说将有两种选择:

  1. 使用try{}catch{}来捕获异常并处理
  2. 不处理继续使用throws往外抛
    直到在main方法中,如果此时也不处理,则抛出就抛给了jvm,JVM对于接受到的异常的默认处理:
    在这里插入图片描述

以上就是jvm的默认处理方式:打印异常的堆栈信息,并终止程序的执行。

异常信息打印顺序:首先打印的是距离抛出异常最近的语句,接着是调用该方法的方法,一直到最开始被调用的方法。

对于与业务相关的异常,就需要我们自己来定义异常

3.3 注意事项

  • 一个try 块不是只有一个catch语句,也可以不使用catch
  • catch,finally不能单独使用
  • 多重捕获(multi-catch)可以对多个 catch 语句进行优化。
  • finally 总会被执行,除非 catch 中出现了 System.exit(1)语句。

4. 自定义异常(MyException)

自定义异常可以更加明确定位异常出错的位置和给出详细出错信息

  1. 自定义异常有两种:

    1. 自定义编译期异常
    2. 自定义运行时异常
  2. 如果要自定义一个编译期异常,则继承Exception,并实现相应的构造方法即可。
    如果要定义一个运行时异常,则继承RuntimeException,并实现相应的构造方法。

  3. 实例:

  • 需求:编写程序模拟用户注册
    程序开始执行时,提示用户输入“用户名”和“密码”信息。
    输入信息之后,后台java程序模拟用户注册。
    注册时用户名要求长度在[6-14]之间,小于或者大于都表示异常。
    注意: 完成注册的方法放到一个单独的类中。 异常类自定义即可。
class UserService {
    public void register(String username,String password){
        //这个方法中完成注册!
    }
}
  • 实现:
public class UserExcepton extends Exception{
    public UserExcepton(){
        super();
    }
    public UserExcepton(String username){
        super(username);
    }
}
import java.util.Scanner;

public class UserService {
    public void register(String username , String password) throws UserExcepton {
        int l = username.length();
        if(l <= 14 && l >= 6){
            System.out.println("注册成功");
        }else{
            throw new UserExcepton("用户名输入不合法");
        }
    }

    public static void main(String[] args) throws UserExcepton {
        UserService user = new UserService();
        Scanner sc = new Scanner(System.in);
        System.out.println("输入用户名:");
        String username = sc.nextLine();
        System.out.println("输入密码:");
        String password = sc.nextLine();
        user.register(username,password);
    }
}

运行结果:

输入用户名:
乐呵呵123
输入密码:
abc123
注册成功

输入用户名:
乐呵呵
输入密码:
abc123
Exception in thread "main" Day_1_1.UserExcepton: 用户名输入不合法
	at Day_1_1.UserService.register(UserService.java:11)
	at Day_1_1.UserService.main(UserService.java:22)
  1. throw和throws的区别

    1. 位置:throws用在方法的声明上,throw在方法体
    2. 抛出类型:throws抛出的是异常的类型,throw抛出的是异常对象
    3. 数量:throws可以抛出多个异常 throw只能抛出 一个异常对象
  2. 什么时候抓什么时候抛?

    • 捕获异常一般都在我们可以自己处理,并且处理之后不会再次产生新的异常时,才捕获处理。
    • 如果不能完全处理异常,则将异常抛出给下一个方法的调用者,让其来做出相应的处理,直到最后抛给jvm。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BORN(^-^)

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

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

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

打赏作者

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

抵扣说明:

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

余额充值