【Java中的异常机制基础篇】简单介绍Java中的异常、错误及检查型异常和运行时异常等分类。并讲解及编码实现Java中的异常处理机制try、catch、finally代码块结构。最后讲解自定义异常类。

一、异常

在这里插入图片描述

如:

package com.exception.demo01;

public class Demo01 {
    public static void main(String[] args) {
//        new Demo01().a();  // StackOverflowError: 堆栈溢出错误
        System.out.println(11 / 0);  // ArithmeticException: 算术异常
    }
    public void a (){
        b();
    }
    public void b (){
        a();
    }
}

在这里插入图片描述

二、异常体系结构

在这里插入图片描述

Error(错误):AWTError(GUI组件错误)、VirtulMachineError(JVM虚拟机错误)。

Exception(异常):IOException(输入输出异常,属于Checked Exception检查型异常)、RuntimeException(运行时异常,属于Unchecked Exception非检查型异常)。

在这里插入图片描述

常见错误有:OutOfMemoryError(内存耗尽)、StackOverflowError(栈溢出)、NoClassDefFoundError(类定义未找到)。

在这里插入图片描述

Exception(异常)分为两类:RuntimeException( 运行时异常 / 非检查型异常)、其他Exception(检查型异常(Checked Exception) / 非运行时异常)。

检查型异常(Checked Exception):也叫非运行时异常。是指在Java编程中,必须在编译时显式处理的异常。编译器会强制要求开发者对这类异常进行处理,否则编译无法通过。如:IOException(文件未找到、读写错误等)、SQLException(数据库操作错误)、ClassNotFoundException(类加载失败)等。

非检查型异常(Unchecked Exception):包括运行时异常(RuntimeException)和错误(Error)。运行时异常如:NullPointerException(空指针)、ArrayIndexOutOfBoundsException(数组越界)、IllegalArgumentException(非法参数)、ArithmeticException(算术异常,如除零)。

三、异常处理机制

在这里插入图片描述

异常处理关键字:

package com.exception.demo01;

/*
   try     --> 可能抛出异常的代码。未被捕获的异常最终会导致线程/程序终止。
      throws   -->     用在方法声明上,表示该方法可能抛出哪些异常。
      throw    -->     用在方法内部,立即中止当前代码块的执行,主动抛出一个异常对象。
   catch   --> 捕获并处理异常
   finally --> 最终执行的清理代码
 */

/* 抛出的异常会抛到哪里:
   抛出的异常会沿着调用栈向上传播:
   1. 如果当前方法有catch块处理该异常,就在那里被捕获。
   2. 如果没有,异常会抛给调用该方法的上层方法。
   3. 如果一直传播到main方法还没有被捕获,最终会传到JVM。导致线程(或主线程,程序)终止,打印异常堆栈信息到控制台。
 */

public class Test {
    public static void main(String[] args) {

//        try {
//            new Test().test(1 , 0);
//        } catch (ArithmeticException e) {  // 捕获 ArithmeticException异常
//            throw new RuntimeException(e);  // 包装后重新抛出 RuntimeException异常
//        }

        int a = 1;
        int b = 0;
        try {  // try监控区域
            if (b == 0 ){
                throw new ArithmeticException();  // 程序运行终止,抛出异常,跳转到匹配的捕获catch代码块。
            }
            System.out.println(a / b);  // 不会执行
        }catch (ArithmeticException e){  // catch捕获异常。ArithmeticException算数异常。
            System.out.println("程序出现异常,变量b不能为0");
        }finally {  // finally(最后),可选语句块。finally代码块无论是否发生异常都会执行,主要用于资源释放和清理工作。finally的优先级:即使在catch中又抛出了新异常,finally也会先执行。
            System.out.println("finally");
        }

        try {
            new Test().a();
        }catch (Error e){  // catch(想要捕获的异常类型)。假设要捕获多个异常: 应从上往下,范围从小到大依次捕获,只会执行其中的一个catch代码块。
            System.out.println("程序出现Error异常");
        }catch (Exception e){
            System.out.println("程序出现Exception异常");
        }catch (Throwable e){  // 参数为想要捕获异常的类型,Throwable为最高超类。
            System.out.println("程序出现Throwable异常");
        }finally {

        }
        try {
            new Test().test(1,0);
        } catch (ArithmeticException e) {
//            e.printStackTrace();  // 打印异常堆栈信息的方法,通常用于调试阶段。
        }
    }

    public void a(){
        b();
    }
    public void b(){
        a();
    }

    /* throws:
       声明方法可能抛出的异常。对于Checked Exception(检查型异常),编译器会强制调用者处理(捕获或继续声明抛出)。
       throws声明实际上是为异常提供传播路径。当方法内部:使用throw抛出异常或调用的其他方法抛出异常时,这些异常会通过throws声明的通道向上传递。
     */

    // ‌检查型异常(Checked Exception)‌是指在Java编程中,必须在编译时显式处理的异常。编译器会强制要求开发者对这类异常进行处理,否则编译无法通过。
    // 检查型异常可以通过try-catch块捕获,或者通过throws声明抛出给上级方法处理。

    public void test(int a, int b) throws ArithmeticException{  // throws: 用于方法声明,表示该方法可能抛出哪些异常。语法: method() throws ExceptionType1,ExceptionType2{}
        if (b == 0){
            throw new ArithmeticException();  // 主动抛出算术异常。throw: 用于方法内部,立即中止当前代码块的执行,主动抛出一个异常对象。throw。语法: throw new ExceptionType()。
        }
        System.out.println(a / b);
    }
}

扩展:

package com.exception.demo01;

public class Test2 {
    public static void main(String[] args) {
        int a = 1;
        int b = 0;

        // 选中需运行代码,快捷键: Ctrl + Alt + T。选择创造语句块类型
        try {
            System.out.println(a / b);
        } catch (Exception e) {
            e.printStackTrace();  // 打印异常堆栈信息的方法,通常用于调试阶段。生产环境更推荐用日志框架(如log.error("异常", e))。
//            System.exit(1);  // 程序结束,不会执行后面的语句。status的参数是程序的退出状态码。0表示程序正常退出。1或2等表示程序异常退出的不同错误类型。1一般表示通用错误。
            throw new RuntimeException(e);  // 将捕获的异常e包装后重新抛出RuntimeException异常。如果外层没有捕获这个异常,线程/程序会终止并打印堆栈信息,并不会往下运行。
        } finally {

        }
    }
}

代码块创建快捷键:

在这里插入图片描述

四、自定义异常

在这里插入图片描述

MyException(自定义异常类):

package com.exception.demo02;

// 自定义的异常类
public class MyException extends Exception{

    // 传递数字 > 10;
    private int detail;

    public MyException(int a) {  // 自定义异常类的构造器。创建一个完整的异常对象
        this.detail = a;
    }

    // toString: 异常的打印信息
    @Override
    public String toString() {
        return "MyException{" + detail + '}';
    }
}

主动抛出并捕获自定义异常:【包含字符串连接,底层运行讲解】

package com.exception.demo02;

public class Test {

    static void test(int a) throws MyException{  // 2. 声明可能抛出MyException异常。异常沿着调用栈向上传播。

        System.out.println("传递的参数为:" + a);
        if (a > 10 ){
            throw new MyException(a);  // 1. 创建并抛出异常对象。调用MyException的构造器,创建一个完整的异常对象。
        }
        System.out.println("OK");  // 不会执行
    }

    public static void main(String[] args) {
        try {
            test(11);
        } catch (MyException e) {  // 3. 最后main方法中的try-catch会捕获MyException异常。
            // 可在此增加一些处理异常的代码
            System.out.println("MyException=>" + e);  // 输出: MyException=>MyException{11}。调用e.toString()
        }

    /* 解释: System.out.println("MyException=>" + e); 为什么会调用自定义异常类MyException的toString方法。
            '+' : Java处理字符串连接时,编译器会将其进行转换。(Java的隐式方法调用机制)
            原始代码: String s = "MyException=>" + e;
            编译后的等效代码:
                    String s = new StringBuilder()
                                    .append("MyException=>")
                                    .append(e)
                                    .toString();
            =========================================================
            相关源码: public AbstractStringBuilder append(String str) {  // JDK8和JDK9+略有不同
                        if (str == null) return appendNull();
                        int len = str.length();
                        ensureCapacityInternal(count + len);  // 检查扩容
                        str.getChars(0, len, value, count);  // 关键复制操作
                        count += len;
                        return this;
                    }
                    public StringBuilder append(Object obj) {  // 该方法直接继承AbstractStringBuilder
                        return append(String.valueOf(obj));
                    }
                    public static String valueOf(Object obj) {  // 【重点: Java处理对象转字符串的核心方法】
                        return (obj == null) ? "null" : obj.toString();  // 重写toString()方法自定义
                    }
                    public String toString() {  // JDK8和JDK9+略有不同
                        return new String(value, 0, count);
                    }
            逐句解释:
            new StringBuilder()这里会创建一个初始容量为16的字符数组。【这样的好处是节省不必要的空间】
            .append("MyException=>")这里会调用StringBuilder.append(String str),际执行其父类AbstractStringBuilder。
                【作用是检查其中的字符串是否>16,是否需要扩容,然后将字符串复制到内部数组中。"MyException=>".getChars(0,13,value,0);计数count=13;】
            .append(e)这里会调用StringBuilder.append(Object obj),触发String.valueOf(e),调用重写的toString()方法。
                .append(e)后面返回为append("MyException{11}"),再调用AbstractStringBuilder append(String str)方法。
                【依然将字符串复制到内部数组中,结合上面一个字符串长度13+新字符串12=25,大于16,随后扩容。新容量计算:(16+1)*2=34。将原数组内容复制到新数组里面,并追加新字符内容。计数count=25;】
            .toString()创建新的String对象。将StringBuilder内部数组中实际使用的部分(前25个字符)复制过去。不直接暴露内部数组(防御性拷贝)。
            最终结果:
                new String(["M","y","E","x",...,"1","1"], 0, 25)
                即"MyException=>MyException{11}"
    */
    }
}

快捷键:ALT + ENTER —— 快速生成代码、修正代码错误、各种自动化操作‌。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值