Java异常

本文深入解析Java异常处理的两种主要方式:try-catch-finally用于现场捕获和finally确保资源清理,throws用于向上抛出异常让调用者处理。涵盖异常体系结构、自定义异常及常见异常案例和实践应用。

一、异常体系框架
/*
   /*
   一、异常体系结构
   Java.lang.Throwable
       |----- java.lang.Error:// 一般不编写针对性的代码无法处理的错误:如系统资源耗尽,I/O设备出错
       |----- java.lang.Exception:可以进行异常的处理
           |----- 编译时异常:(受查异常)
               |----- IOException:
                   |----- FileNotFoundExcepion
               |----- ClassNotFoundException
           |----- 运行时异常:(非受查异常)
               |----- 空指针异常
               |----- 数组越界异常
               |----- 类转换异常
               |----- Number Format Exception (数字格式异常):例如将字母字符串转换成数字
               |----- Input Mismatch Exception (输入不匹配异常):要求输入整数,但输入的是字母
               |----- ArithmaticException (算数异常):除0
面试题:常见的异常有哪些?举例说明
*/
*/

二、异常处理方式
  1. 方式一:try - catch - finally (直接处理异常)
  2. 方式二:throws + 异常类型(抛出异常)

三、异常处理过程
  1. 过程一:抛出异常
    • 程序在运行过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象
    • 一旦抛出异常以后,出现异常处以后的代码不再执行
  2. 过程二:抓异常
    • 这个过程即为处理出现的异常

[处理方式1],捕获异常:try - catch - finally的使用
try{
	 //可能会出现的异常
 }
 // 可以捕获多个异常...
 catch (异常类型1 变量名1){
	// 异常1 处理方式
 }
 // ....
 catch(异常类型2 变量名2){
	// 异常2 处理方式
 }
 finally{
     // 一定会执行的代码
 }


1. 捕获异常示例
public static void main(String[] args) {
        try{
            String str = null ;
            System.out.println(str);
        }
        catch (NullPointerException e1){
            System.out.println("出现空指针异常");
        }
        finally {
            System.out.println("这里是一定会执行的代码");
        }
    }
2. try-catch-finally说明:
  1. 使用try将可能出现的异常包装起来,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,在catch中进行匹配
  2. 一旦匹配到某个catch时,就进入catch中进行异常的捕获,一旦处理完成,就跳出当前的try-catch结构。继续执行结构之后的代码
  3. catch中的异常类型如果没有父子类关系,则顺序无关;若存在父子类关系,则子类一定要声明在父类之上,否则会报错
  4. catch中常用方法:
    • String getMessage()
    • void printStackTrace()
  5. 在try语句块中声明/定义的变量出了try-catch-finally结构之后,不能在使用
  6. try-catch-fianlly结构块可以进行嵌套

使用try-catch-finally处理编译时异常,使得程序就不再报错,但是仍可能在运行时报错。相当于使用try-catch-finally将一个编译时异常延迟到运行时出现


3. finally的使用
  1. finally是可选的
  2. 存在finally,则finally中的代码是一定会执行的。即使try中有return语句,catch中有return语句等
  3. 写入finally语句的情况:
    • 像数据库链接、输入输出流、网络编程的Socket等资源,JVM是不能自动回收的,我们需要我们自己手动的进行资源的释放。此时的资源释放,就需要在finally中释放

示例一:

public static void main(String[] args) {
        System.out.println(method());
    }
    public static String method(){
        try{
            int[] arr = {1,2,3};
            System.out.println(arr[4]);
            return "没有异常出现";
        }
        catch (IndexOutOfBoundsException e1){
            System.out.println("异常信息:"+e1.getMessage());
            return "出现数组越界异常" ;
        }
        finally {
            System.out.println("这是finally");
//            return "这里是一定会执行的代码";
        }
    }

结果:

异常信息:4
这是finally
出现数组越界异常

finally中的代码被执行完毕后,执行catch中的return语句
示例二:

 public static String method(){
       try{
            int[] arr = {1,2,3};
            System.out.println(arr[4]);
            return "没有异常出现";
        }
        catch (IndexOutOfBoundsException e1){
            System.out.println("异常信息:"+e1.getMessage());
            return "出现数组越界异常" ;
        }
        finally {
            System.out.println("这是finally");
            return "这里是一定会执行的代码";
        }
    }

结果:

异常信息:4
这是finally
这里是一定会执行的代码

finally中存在return语句,执行返回语句,则catch中的return语句不再被执行


4. 对编译时异常和运行时异常的不同处理
  • 对于运行时异常,不需要添加try-catch进行处理
  • 对于编译时异常,则需要进行处理

[处理方式2],抛出异常:throws + 异常类型
  • 对可能出现的异常进行抛出,由高层模块进行处理。
  1. throws + 异常类型 写在方法的声明处。指明执行此方法时,可能出现的异常类型。
  2. 一旦方法执行,出现异常,就会在异常代码出生成一个异常类的对象,此对象满足throws后的异常类型时,就会被抛出。异常代码后续的代码,就不再执行
  3. 抛出异常的处理方式,并没有真正的将异常进行处理,而是抛给了高层模块
	public static void main(String[] args) {
		// 让调用method方法的模块进行异常的处理
        try{
            System.out.println(method());
        }
        catch (IndexOutOfBoundsException e1){
            System.out.println("异常信息:"+e1.getMessage());
            System.out.println("出现数组越界异常");
        }
        catch (NullPointerException e2){
            System.out.println("异常信息:"+e2.getMessage());
            System.out.println("出现空指针异常");
        }
        finally {
            System.out.println("这是finally");
        }
    }
    //抛出可能出现的数组越界异常,让上层模块对该异常进行处理
    public static String method() throws IndexOutOfBoundsException, NullPointerException{
        List list = null ;
        int[] arr = {1,2,3};
        System.out.println(list.get(0));
        System.out.println("arr[4] = "+arr[4]);
        return "没有出现异常";
    }

1. 重写方法异常的规则
  • 子类重写方法抛出的异常类型不大于符类被重写的方法抛出的异常类型

2. 如何选择使用try-catch-finally 还是 throws?
  • 如果父类被重写的方法没有使用throws处理异常,则子类重写的方法也不能够使用throws处理异常,意味着一旦子类中出现异常,必须使用try-catch-finally处理异常
  • 执行的方法a中,先后又调用了另外几个方法,这几个方法是递进关系执行的。建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理
    • 原因:如果在a中调用的方法1出现异常,即使在方法1中进行处理,仍然可能导致得到一个错误的值,该值传递到方法2,方法3,最终会出现一个错误值。

3. 手动抛出异常
  • 使用throw 抛出一个异常对象
  • throw + 创建一个异常对象;例如:throw new Exception;
class User{
    int uid ;
    							// 声明方法中可能出现的异常
    public void register(int uid) throws Exception{
        if(uid>0){
            this.uid = uid;
        }
        // throw 抛出一个异常对象
        throw new Exception("输入的id不合法");
    }
}

四、自定义异常

如何自定义异常类?

  1. 继承于现有的异常结构,RuntimeException、Exception
  2. 提供一个全局常量:serialVersionUID
    • serialVersionUID的意义:去唯一标识自定义的异常类
  3. 提供一个/多个构造器
    自定义异常类
class MyException extends Exception{
    static final long serialVersionUID = -3387516993124229948L;
    public MyException(String message){
        super(message);
    }
}

调用自定义异常类

class User{
    int uid ;
    // 上示例中抛出的Exception换成了自定义的MyException
    public void register(int uid) throws MyException{
        if(uid<=0)
         	throw new MyException("输入的id不合法");
        this.uid = uid;
       
    }
}

五、异常练习
  • 编写程序,接收两个命令行的参数,要求不能输入负数,计算两数相除
    • 数据类型不一致:NumberFormatException
    • 缺少命令行参数:ArrayIndexOutBoundsException
    • 除0:ArithmeticException
    • 输入负数:自定义异常类

代码


public class Main {
    public static void main(String[] args) {
        int res = -1 ;
        try{
            int a = Integer.parseInt(args[0]);
            int b = Integer.parseInt(args[1]);
            res = getDivide(a,b);
        }catch (MyException e){
            System.out.println(e.getMessage());
        }
        catch (ArithmeticException e){
            System.out.println("除0");
        }
        catch (NumberFormatException e){
            System.out.println("数据类型不一致");
        }
        catch (ArrayIndexOutOfBoundsException e){
            System.out.println("缺少命令行参数");
        }
        finally {
            System.out.println(res);
        }
    }
    public static int getDivide(int a, int b) throws MyException {
        if(a<0||b<0)
            throw new MyException("输入了负数");
        return a/b ;
    }
}
// 自定义异常类
class MyException extends Exception{
    static final long serialVersionUID = -338751699312448L;
    public MyException(String message){
        super(message);
    }
}

编译运行

  • 运行环境:IDEA下的命令行
  1. 切换到对应源文件的路径
  2. 使用编写代码时的编码对源文件进行编译
  3. 反复执行生成的.class文件,测试对应的异常
    进行java异常的练习

注意

  • java源文件存在中文,使用javac命令编译时,需要使用对应的编码格式进行编译
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值