纵横山河万里,终集 Java的错误和异常

本篇会加入个人的所谓‘鱼式疯言’

❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的.
🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 !!!

在这里插入图片描述

前言

让一个人改Bug,你只能折磨他一天
小编帮他改Bug,能折磨他一辈子。

是的,在Java的世界中,我们特别容易出现错误和异常这些Bug。

那么小编该如何拿捏这些Bug呢,下面让我们拭目以待吧 💖 💖 💖

目录

  1. 异常的初识
  2. 异常的处理
  3. finally
  4. 异常的处理流程
  5. 自定义异常类

一. 异常的初识

在生活中,一个人表情痛苦,出于关心

可能会问:你是不是生病了,需要我陪你去看医生吗?

在这里插入图片描述

1. 异常的概念

异常是什么,异常有哪些结构呢,下面小编就来聊聊 💖 💖 💖

在程序中也是一样,程序猿是一帮办事严谨、追求完美的高科技人才。

在日常开发中,绞尽脑汁将代码写的尽善尽美

在程序运行过程中,难免会出现一些奇奇怪怪的问题。

有时通过代码很难去控制

比如:数据格式不对、网络不通畅、内存报警等。

在Java中,将程序执行过程中发生的不正常行为称为异常。 比如之前写代码时经常遇到的:

<1>. 算法异常

class Test1 {
    public static void main(String[] args) {
        System.out.println(10 / 0);
    }
}

在这里插入图片描述

<2>. 数组越界异常

class Test1 {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        System.out.println(arr[100]);
    }
}

在这里插入图片描述

<3>. 空指针异常

class Test1 {
    public static void main(String[] args) {
        int[] arr = null;
        System.out.println(arr.length);
    }
}

在这里插入图片描述

从上述过程中可以看到,java中不同类型的异常,都有与其对应的类来进行描述。

那么这些类是什么呢,他们到底有怎么样的层次关系呢 🤔 🤔 🤔

2. 异常的体系结构

异常种类繁多,为了对不同异常或者错误进行很好的分类管理,Java内部维护了一个异常的体系结构

在这里插入图片描述
从上图中我们就可以清楚的看到:

  1. Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception
  1. Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:

StackOverflowError和OutOfMemoryError,一旦发生回力乏术。

  1. Exception:异常产生后程序员可以通过代码进行处理,使 程序继续执行 。比如:感冒、发烧。我们平时所说的异常就是 Exception

像以下这种就是回天乏术的错误

class Test1 {

    public static void func(int n) {
        func(n-1);
    }
    public static void main(String[] args) {
       func(3);
    }
}

在这里插入图片描述

鱼式疯言

用两句话来概括就是说

错误是严重的,无法拯救

异常是轻微的,可以挽回

3. 异常的分类

异常可能在编译时发生,也可能在程序运行时发生,根据发生的时机不同,可以将异常分为:

<1>. 编译时异常

程序编译期间发生的异常 ,称为编译时异常,也称为受检查异常(Checked Exception)

class Test2 {
    private String name;
    private  int age;

    public Test2(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args)throws CloneNotSupportedException {
        Test2 t1=new Test2("小明",19);
        Test2 t2=(Test2) t1.clone();
    }
}

在这里插入图片描述

这个结果就是因为我们没有继承 implements Cloneable接口而导致编译时的异常

<2>. 运行时异常

在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception)
RunTimeException以及其子类对应的异常,都称为运行时异常。比如:

NullPointerException、

ArrayIndexOutOfBoundsException、

ArithmeticException。

注意:编译时出现的 语法性错误 ,不能称之为异常

例如将 System.out.println 拼写错了, 写成了 system.out.println. 此时编译过程中就会出错, 这是

“编译期” 出错。

运行时异常 指的是程序 已经编译通过得到class 文件了, 再由 JVM 执行过程中出现的错误.

具体的栗子,我们在异常的处理中会细细讲解哦 💖 💖 💖

鱼式疯言

源代码 extends runException 的就是运行时异常

否则就是 编译时异常

好比下图

在这里插入图片描述

二. 异常的处理

错误在代码中是客观存在的. 因此我们要让程序出现问题的时候及时通知程序猿. 主要的方式

在Java的大千世界中

我们处理异常的方式可以无奇不有的哦 ! ! !

1. 防御式编程

LBYL: Look Before You Leap. 在操作之前就做充分的检查. 即:事前防御型

class Test3 {
    public static void main(String[] args) {
        boolean b=true;
        if (!b) {
            System.out.println("处理登入异常!");
            System.exit(-1);
        } else {
            System.out.println("处理匹配异常");
            System.exit(-2);

        }

        // ....
    }
}

在这里插入图片描述

2. 认错型编程

EAFP: It’s Easier to Ask Forgiveness than Permission. "

事后获取原谅比事前获取许可更容易". 也就是先操作, 遇到问题再处理. 即:事后认错型

<1>. 异常的抛出

在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测

在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者

具体语法如下:

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

举个栗子

【目标】: 实现一个获取数组中任意位置元素的方法

class Test4 {

    public static int func (int[] arr,int x) {
        if (arr==null) {
            throw new NullPointerException("空指针异常!");
        } else if (x<0 || x>arr.length-1) {
            throw new ArrayIndexOutOfBoundsException("数组越界异常!");
        }
            return arr[x];

    }

    public static void main(String[] args) {
        int[] array={1,2,3};
        func(array,3);
    }
}

在这里插入图片描述

上面结果表明,在我们使用数组之前,可以先检查其是否异常,

如果异常的话抛出对应的提示信息

鱼式疯言

  • throw 必须写在方法体内部
  • 抛出的对象必须是 Exception 或者 Exception 的子类对象
  • 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
  • 如果抛出的是 编译时异常,用户必须处理,否则无法通过编译
  • 异常一旦抛出,其后的代码就不会执行

<3>. 异常的捕获

异常的捕获,也就是异常的具体处理方式,

主要有两种:异常声明throws 以及 try-catch捕获处理。

<3>.1 异常的声明
class Test5 {
    public static void func(int[] array) throws Exception {
        if(array == null) {
            throw new Exception("传个参数看看..."+array);
        }
    }

    public static void main(String[] args) throws Exception{
        int[]arr=null;
        func(arr);
    }
}

在这里插入图片描述

从中我们可以看到

  1. throws 必须跟在方法的 参数列表 之后
  1. 声明的异常必须是 Exception 或者 Exception 的子类
  1. 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型 ,之间用 逗号隔开 ,如果 抛出多个异常类型 具有父子关系直接声明父类 即可。

鱼式疯言

小伙伴们有没有发现一个细节:

当我们调用 func 时是不是 main 也带上 throws 了

是的

所以小编最后一点想提醒小伙伴的是 :

调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出

小爱同学有一个问题:
我们就 throws 抛出就处理异常了吗 ? ? ?

<3>.2 异常的捕获

throws 对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。

如果真正要对异常进行处理,就需要 try-catch

try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获

举个栗子
class Test6 {

    public static void func1 (int[] arr) {
        try {
            System.out.println(arr.length);
        } catch (NullPointerException e1) {
            e1.printStackTrace();
            System.out.println("空指针异常");
        } catch (ArrayIndexOutOfBoundsException e2) {
            e2.printStackTrace();
            System.out.println("数组越界异常");
        } catch (InputMismatchException e3) {
            e3.printStackTrace();
            System.out.println("输入不匹配异常");
        } catch (ArithmeticException e4) {
            e4.printStackTrace();
            System.out.println("算术异常");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("其他异常");
        }
    }

    public static void main(String[] args) {
            int [] array=null;
            func1(null);
    }
}

在这里插入图片描述

每个不同的 异常种类 对应不同的 异常类

但是这些异常类都有

在这里插入图片描述
在这里插入图片描述
由于 Exception 类是所有异常类的父类.

因此可以用这个类型表示 捕捉所有异常.

所以他们共同继承一个 大类 就是我们的 Exception 类,当我们需要其他异常来捕获的话,可以用 Exception 来捕获

上图就清楚的 示范了
在这里插入图片描述
最后小编要提一嘴的是

异常的种类有很多, 我们要根据不同的业务场景来决定. 对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃,

防止造成更严重的后果 对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿

对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试. 在我们当前的代码中采取的是经过简化的第二种方式.

我们记录的错误日志是出现异常的方法调用信息, 能很 快速的让我们找到出现异常的位置. 以后在实际工作中我们会采取更完备的方式来记录异常信息.

鱼式疯言

记住两点就OK了

  1. 不同异常用不同类来捕获,Exception 一定放 最后(好比分支语句)
  2. 程序猿不能捕获,JVM 会捕获

三. finally

1. finally 简介

在写程序时,有些特定的代码,

不论程序是否发生异常,都需要执行,比如程序中打开的资源: 网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。

另外,因为异常会引发 程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的。

2. 举个栗子

class Test {

    public static void main(String[] args) {
        try{
            int[] arr = {1,2,3};
            arr[100] = 10;
            arr[0] = 10;
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            System.out.println(e);
        }finally {
            System.out.println("finally中的代码一定会执行");
        }
        System.out.println("如果没有抛出异常,或者异常被处理了,try-catch后的代码也会执行");
    }
}

在这里插入图片描述

其实一般以 try-catch-finally 后的代码都会执行。

很少我们 finally 单独使用

3. finally 实际运用场景

class Test6 {

    public static void func1 (int[] arr,int x) {
        try {
            System.out.println(arr[x]);
        } catch (NullPointerException e1) {
            e1.printStackTrace();
            System.out.println("空指针异常");
        } catch (ArrayIndexOutOfBoundsException e2) {

            e2.printStackTrace();
            System.out.println("数组越界异常");
        } catch (InputMismatchException e3) {
            e3.printStackTrace();
            System.out.println("输入不匹配异常");
        } catch (ArithmeticException e4) {
            e4.printStackTrace();
            System.out.println("算术异常");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("其他异常");
        } finally {
            arr=null;
            System.out.println("数组空间已释放");
        }
    }

    public static void main(String[] args) {
            int [] array={1,2,3};
            func1(array,6);
    }
}

在这里插入图片描述

如果没有 finally 来处理的话,就很有可能造成数组空间资源的浪费

鱼式疯言

注意:

finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作。

最后还有一个小建议**(不要在 finally 中写 return)**

就像如下代码可读性是很差的

class Test {


    public static int func() {
        try {
            return 10;
        } finally {
            return 20;
        }
    }

        public static void main(String[] args) {
            System.out.println(func());
        }


}

在这里插入图片描述

四. 异常的处理流程

1. 举个栗子

class Test {
    public static void main(String[] args) {
        func();
        System.out.println("after try catch");
    }
    public static void func() {
        int[] arr = {1, 2, 3};
        System.out.println(arr[100]);
    }
}

在这里插入图片描述

程序先执行 try 中的代码

如果 try 中的代码出现 异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.

如果找到匹配的异常类型, 就会执行 catch 中的代码

如果没有找到匹配的异常类型, 就会将异常 向上传递到上层调用者.

无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).

如果上层调用者也没有处理的了异常, 就继续向上传递.

一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

鱼式疯言

一句话:

最后调用的前报提示,然后一直往前找调用他的人,直 到 main 方法为止

五. 自定义异常类

Java 中虽然已经内置了丰富的异常类,

但是并不能完全表示实际开发中所遇到的一些异常

此时就需要维护符合我们 实际情况的异常结构.

这里不是本篇文章的重点哦,宝子们了解即可哦

1. 实际运用

class NameException  extends Exception{

    public NameException(String message) {
        super(message);
    }
}

class PasswordException  extends Exception{

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

class Test1 {
    public static void func2 (String name ,String num) throws NameException,PasswordException{
        String n="mypp";
        String m="123456";

        if (!name.equals(n)) {
            throw new NameException("用户名错误!");
        }

        if (!num.equals(m)) {
            throw  new PasswordException("密码错误!");
        }
    }

    public static void main(String[] args) {
        try {
            func2("mypp","123");
        } catch (NameException e1) {
            e1.printStackTrace();
            System.out.println("用户名!!!!");
        } catch (PasswordException e2) {
            e2.printStackTrace();
            System.out.println("密码!!!!");
        }
    }

}

在这里插入图片描述

  • 自定义异常通常会继承自 Exception 或者 RuntimeException
  • 继承自 Exception 的异常默认是受查异常
  • 继承自 RuntimeException 的异常默认是非受查异常.

总结

  • 异常的初识:简单了解了异常的概念并结合平常的易错点
  • 异常的处理: 详细的说明了我们处理异常然后声明,然后捕捉到要义,以及注意点
  • finally: 收尾处理必备关键字
  • 异常的处理流程: 明白了异常处理的顺序是怎么样的
  • 自定义异常类: 如果Java自身异常不够用,实际的运用继承类的思想去解决问题

如果觉得小编写的还不错的咱可支持 三连 下 (定有回访哦) , 不妥当的咱请评论区 指正

希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大 动力 💖 💖 💖

在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

邂逅岁月

感谢干爹的超能力

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

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

打赏作者

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

抵扣说明:

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

余额充值