对Java中异常的认识

news2024/9/27 17:24:49

文章目录

  • 一、异常的概念与体系结构
    • 1.异常的概念
    • 2.异常的体系结构
    • 3.异常的分类
  • 二、异常的处理
    • 1.防御式编程
    • 2. 异常的抛出
    • 3.异常的捕获
    • 4.异常处理流程总结
  • 三、自定义异常类

一、异常的概念与体系结构

1.异常的概念

在生活中,当我们发现朋友表现出不舒服的情况,出于关心,我们都会问是不是病了,要不要去看医生。
程序也是如此,程序员是一群办事比较严谨的人,在日常开发之中,绞尽脑汁将代码尽可能写的尽善尽美,但是在运行过程中,常常会出现各种奇奇怪怪的问题。 例如:数据格式不对、网络不通畅,内存警报等。

在 Java 中,程序在执行过程中出现的不正常行为称之为异常。

通常遇到的情况如下:

  1. 算数异常
        System.out.println(10/0);

在这里插入图片描述

  1. 数组越界异常
        int[] array = {1,2,3};
        System.out.println(array[4]);

在这里插入图片描述
从上述的部分异常中不难看出,java中的不同类型的异常,都有与之对应的类来进行描述。

2.异常的体系结构

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

  1. Throwable:是异常体系的顶层类,其派生出两个重要的子类,Error 和 Exception
  2. Error:指的是 Java 虚拟机无法解决的严重问题,比如:**JVM的内部错误、资源耗尽等,**典型代表:Stack OverflowErrorOutOfMemoryError,一旦发生回力乏术。
  3. Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。就是我们平时说的异常。

3.异常的分类

  1. 编译时异常
    在程序编译时发生的异常,称之为编译时的异常,也称之为受检查异常。
    private  String name;
    private  String gender;
    int age;

    public abnormal clone(){
        return (abnormal)super.clone();
    }

在这里插入图片描述

  1. 运行时异常
    在程序执行期间发生的异常,称之为运行时异常,也称之为非受检查异常

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

二、异常的处理

1.防御式编程

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

代码示例:

boolean ret = false;
ret = 登陆游戏();
if (!ret) {
处理登陆游戏错误;
return;
}
ret = 开始匹配();
if (!ret) {
处理匹配错误;
return;
}
ret = 游戏确认();
if (!ret) {
处理游戏确认错误;
return;
}

**缺陷:**上述代码中不难发现,正常的程序流程和错误处理的代码混和在了一起,代码整体的可读性下降。

  1. EAFP: It’s Easier to Ask Forgiveness than Permission. “事后获取原谅比事前获取许可更容易”. 也就是先操作, 遇到问题再处理. 即:事后认错型

代码示例:

try {
登陆游戏();
开始匹配();
游戏确认();
} catch (登陆游戏异常) {
处理登陆游戏异常;
} catch (开始匹配异常) {
处理开始匹配异常;
} catch (游戏确认异常) {
处理游戏确认异常;
} 

**优势:**正常流程和错误流程是分离开来的,程序员更关注正常的流程,代码更加清晰,更加容易理解代码。

在 Java 中,异常处理的5各主要关键字:throw、try、catch、final、throws.

2. 异常的抛出

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

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

代码示例:

    public static int getElement(int[] array,int index){
        if(array==null){
            throw new NullPointerException("传递的数组为null");
        }
        if(index<0 || index >= array.length){
            throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
        }
        return array[index];
    }

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

在这里插入图片描述

注:

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

3.异常的捕获

异常的捕获,即异常的具体处理方式,主要有两种:异常声明 throws 以及 try-catch 捕获处理。

  1. 异常声明 throws

    当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助 throws 将异常抛给方法的调用者来处理。即,当前方法不处理异常,提醒方法的调用者处理异常。

代码实现:
需求,加载指定的文件 config.ini.

class Config {
    File file;
    public void OpenConfig(String filename) throws IOException{
        if(!filename.endsWith(".ini")){
        //判断如果是ini文件返回 true
            throw new IOException("文件不是.ini文件");
        }
        if(!filename.equals("config.ini")){
        //判断文件夹名称是否一致,一致返回 true
            throw new FileNotFoundException("配置文件名字不对");
        }
// 打开文件
    }
    public void readConfig(){
    }
}

main方法:
情况1:文件准确无误

    public static void main(String[] args) throws IOException {
        Config config = new Config();
        config.OpenConfig("config.ini");
    }

在这里插入图片描述
情况 2:文件后缀有误

        config.OpenConfig("config.zip");

在这里插入图片描述
情况 3:文件名称有误

        config.OpenConfig("another.ini");

在这里插入图片描述
注:

  1. throws必须跟在方法的参数列表之后
  2. 声明的异常必须是 Exception 或者 Exception的子类。
  3. 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用 throws 抛出。

2.try-catch 捕获并处理

代码实现

需求:读取配置文件,如果配置文件不是指定名字,抛出异常,调用者进行异常处理。

class Config {
    File file;
    public void OpenConfig(String filename) throws IOException{
        if(!filename.endsWith(".ini")){
            throw new IOException("文件不是.ini文件");
        }
// 打开文件
    }
    public void readConfig(){
    }
}
    public static void main(String[] args) {
        Config config = new Config();
        try{
            config.OpenConfig("config.txt");
            System.out.println("文件打开成功");
        }catch(IOException e){
            e.printStackTrace();    //打印全面的异常信息
        }

        //一旦异常被捕获处理,后续代码会执行
        System.out.println("后续代码");
    }

在这里插入图片描述

注意事项:

  1. try 块内的异常抛出后未被处理,之后的代码就不会被执行。
  2. 如果抛出的异常类型与 catch 时的异常类型不匹配,那么异常将不会被成功捕获。
    public static void main(String[] args) {
        try{
            int[] array = {0,1,2};
            System.out.println(array[3]);   // 此处会抛出数组越界异常
        }catch(NullPointerException e){
            e.printStackTrace();    //捕获时捕获的为空指针异常,真正的异常无法捕获到
        }
        System.out.println("后续代码");
    }

在这里插入图片描述

  1. try 中可能会抛出很多个不同的异常,则必须要用多个 catch 来捕获—— 即,多种异常,多次捕获。
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        try{
            System.out.println("before");
            System.out.println(arr[100]);
            System.out.println("after");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("这是数组下标异常");
            e.printStackTrace();
        }catch (NullPointerException e){
            System.out.println("这是空指针异常");
            e.printStackTrace();
        }

        System.out.println("after try catch");
    }

在这里插入图片描述

  1. 如果在捕获异常的类中有父子关系,一定要子类异常在前 catch,父类异常在后 catch,否则语法错误!
    public static void main(String[] args) {
        int[] arr = {1,2,3};

        try{
            System.out.println("before");
            arr = null;
            System.out.println("after");
        }catch (Exception e){   //捕获所有异常
            e.printStackTrace();
        }catch (NullPointerException e){    //已经被前面捕获
            e.printStackTrace();
        }
    }

在这里插入图片描述

总结:由于 Exception 类是所有异常类的父类. 因此可以用这个类型表示捕捉所有异常.

  1. finally关键字

在我们写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源: 网络连接、数据库连接等。在程序正常或者异常退出时,必须要对资源进行回收。另外,因为异常引发的程序跳转,可能会导致有的语句执行不到, finally 就是用来解决这个问题。

语法格式:

try{
// 可能会发生异常的代码
}catch(异常类型 e){
// 对捕获到的异常进行处理
}finally{
// 此处的语句无论是否发生异常,都会被执行到
}
// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行

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

在这里插入图片描述

这里会有一个问题:既然 finally 和 try-catch-finally 后的代码都会执行,呢为啥还要有 finally 呢?

举例解释,需求:实现 getData 方法,内部输入一个整形数字,然后将改数字返回,并且在 main 方法中的进行打印。

代码实现:

class TestFinally{
    public static int getData(){
        Scanner sc = null;
        try {
            sc = new Scanner(System.in);
            int data = sc.nextInt();
            return data;
        }catch (InputMismatchException e){
            e.printStackTrace();
        }finally {
            System.out.println("finally中的代码");
        }
        System.out.println("try-catch-finally之后代码");
        if(null != sc){
            sc.close();
        }
        return 0;
    }
}
    public static void main(String[] args) {
        TestFinally testFinally = new TestFinally();
        int data = testFinally.getData();
        System.out.println(data);
    }

正常输入时:
在这里插入图片描述
非正常输入:
在这里插入图片描述
从上面的程序我们不难看出,如果输入正确,成功接收程序后就会返回,try-catch-finally之后的代码根本就没有执行,即,sc.close() 就没有执行,输入流没有被释放,会造成资源的泄露。

注意: finally 中的代码一定会执行,一般情况下 finally 中的代码会用来做一些资源清理的工作。

4.异常处理流程总结

  • 程序先执行 try 中的代码
  • 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
  • 如果找到匹配的异常类型, 就会执行 catch 中的代码
  • 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
  • 无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
  • 如果上层调用者也没有处理的了异常, 就继续向上传递.
  • 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

三、自定义异常类

在 Java 中虽然已经内置了丰富的异常类,但是,在我们的日常开发中所遇到的一些异常,并不能被完全覆盖到,此时我们就需要创建异常类来维护我们实际情况的代码。

举例:实现一个用户登录系统。

  1. 首先,先定义两个异常类
// 密码错误时的异常类
public class PasswordException extends Exception{
    public PasswordException(String massage){
        super(massage);		//这里的super类是用来改变异常输出语句
    }
}
//	用户名错误是的异常类
public class UserNameException extends Exception{
    public UserNameException(String massage){
        super(massage);
    }
}

class Login{
	// 内部设定好用户名、密码
    private String userName = "admin";
    private String password = "123456";

    public  void logins(String UserName,String Password) throws UserNameException,PasswordException{
        if (!UserName.equals(userName)){
        // 修改捕获错误时的报错原因
            throw new UserNameException("用户名错误");
        }
        if (!Password.equals(password)){
            throw new PasswordException("密码错误");
        }
        System.out.println("登录成功");
    }
}
    public static void main(String[] args) {
        Login login = new Login();
        try{
            login.logins("admin","123456");
        }catch(UserNameException e){
            e.printStackTrace();
        }catch (PasswordException e){
            e.printStackTrace();
        }
    }

情况展现:

  1. 正常登录

在这里插入图片描述

  1. 用户名错误

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

  1. 密码错误

在这里插入图片描述
在这里插入图片描述
注意事项

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

到此, 文章结束, 如有不足, 欢迎提出. 如有错误, 欢迎指正!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/130982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LabVIEW ​​单​进程​共享​变量​与​​全局​变量

LabVIEW ​​单​进程​共享​变量​与​​全局​变量 单​进程​共享​变量​与​LabVIEW​全局​变量​相似。​事实​上&#xff0c;​单​进程​共享​变量​的​实现​是在​LabVIEW​全局​变量​的​基础​上​增加​了​时间​标识​功能。 ​为了​比较​单​进程​…

《终身成长》读书笔记

文章目录书籍信息思维模式思维模式解析关于能力和成就的真相体育&#xff1a;冠军的思维模式商业&#xff1a;思维模式和领导力人际关系&#xff1a;关于相处的思维模式父母、老师与教练&#xff1a;思维模式的传播改变思维模式书籍信息 书名&#xff1a;《终身成长》 作者&am…

Java算法_LeetCode26:删除排序数组中的重复项

LeetCode26&#xff1a;删除排序数组中的重复项 给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。 由于在某些语言中不能改变数组的长度&a…

设计模式-责任链模式之**动态配置责任链**

正在参加2022年度博客之星评选&#xff0c;大家帮我点个五星好评。有投必会 责任链的优势劣势我就不说&#xff0c;百度上很多。 1、怎么&#xff1a;动态配置责任链 通过配置文件yml来指定你的责任链的执行顺序。 地址 配置文件如何配置&#xff0c;怎么读取 为什么&#x…

HTTP协议详解 - 通过C++实现HTTP服务剖析HTTP协议

前言 C/C程序员一般很少会接触到HTTP服务端的东西&#xff0c;所以对HTTP的理解一般停留在理论。 本文章实现通过C实现了一个http服务&#xff0c;可以通过代码对HTTP协议有更深的理解&#xff0c;并且通过抓包工具对HTTP协议进行更为详细的分析。 HTTP协议简介 HTTP(hypert…

Linux--多线程

目录1. 什么是线程2. 创建线程3. 线程等待3.1 pthread_join函数3.2 线程分离3.2 线程终止的方案4. 线程ID1. 什么是线程 Linux中没有专门为线程设计TCB&#xff0c;而是用进程的PCB来模拟进程。 这也是为什么有种观点会说Linux下没有真正意义上的线程。 对于线程来说&#xf…

Elasticsearch搜索引擎(二)——SpringData Elasticsearch

SpringData Elasticsearch SpringData介绍 Spring Data是一个用于简化数据库访问&#xff0c;并支持云服务的开源框架。其主要目标是使得对数据的访问变得方便快捷&#xff0c;并支持map-reduce框架和云计算数据服务。 Spring Data可以极大的简化JPA的写法&#xff0c;可以在…

CSND近期推出的猿如意到底怎么样?

CSND近期推出的猿如意到底怎么样&#xff1f; 投稿测评正文 猿如意传送门 猿如意下载地址&#xff1a;猿如意-程序员的如意兵器,工具代码,一搜就有 猿如意使用了几次了&#xff0c;今天来想分享一下我对于猿如意的使用感受吧&#xff01;&#xff01; 先说结论&#xff1a…

吴恩达《机器学习》——Logistics回归代码实现

Logisitc回归1. Sigmoid与二分类Sigmoid函数为什么Sigmoid函数可以表示二分类概率&#xff1f;2. Logistics回归交叉熵损失函数梯度过拟合与欠拟合正则化3. Python代码实现4. 单维与多维Logistic分类单维数据分类多维数据分类数据集、源文件可以在Github项目中获得 链接: https…

04 kafka 中一些常用的配置的使用

前言 呵呵 也是最近有一些 搭建 kafka 的环境的需求 然后 从新看了一下 一部分的配置情况, 这里 大致理一下 一些我这里比较关心的配置 那些配置关联了 kafka 服务器绑定服务 绑定 tcp 服务的配置来自于这里, 读取的是 config.dataPlaneListeners config.dataPlaneListen…

Java母婴商城母婴店孕妇商城婴幼儿商城网站系统源码

简介 java使用ssm开发的母婴商城系统&#xff0c;用户可以注册浏览商品&#xff0c;加入购物车或者直接下单购买&#xff0c;在个人中心管理收货地址和订单&#xff0c;管理员也就是商家登录后台可以发布商品&#xff0c;上下架商品&#xff0c;处理待发货订单等。 演示视频&…

Allegro如何在PCB上查看pin number的三种方法操作指导

Allegro如何在PCB上查看pin number的三种方法操作指导 Allegro支持快捷的在PCB上查看pin number,如下图 具体操作如下 方法一:show element 选择Show Element命令Find选择Pins

2022年学习机器人和人工智能的一些期待

2022年学习机器人和人工智能的一些体会 2023年即将到来&#xff0c;满满的期待。 做好规划是非常非常重要的&#xff0c;有时候甚至比认真做事本身更为重要。 《礼记中庸》&#xff1a;“凡事豫则立&#xff0c;不豫则废。言前定则不跲&#xff0c;事前定则不困&#xff0c;行…

视频解码学习备忘

媒体文件知识 日常都是播放器直接播&#xff0c;其实这里面还有不少内容的。 首先是视频容器&#xff0c;就是所谓的.mp4 .mkv 这类文件,其目的主要就是用来存放音频视频字幕等内容&#xff0c;所以叫做容器。这些都有一定规范&#xff0c;比如mp4&#xff0c;叫ISO 14496-12…

流程图+BPMN+脑图 JointJS++ 3.6.3 Crack

一个库&#xff0c;‍ 无限的UI选择&#xff0c;直接在您的应用程序中享受交互式流程图、BPMN 和其他图表工作室。利用我们的模板化应用程序&#xff0c;将开发时间缩短至几天。 经过十多年和数百万行代码的编写&#xff0c;我们推出了一组随时可用的功能&#xff0c;以帮助您创…

数据结构进阶 二叉树OJ题二

作者&#xff1a;小萌新 专栏&#xff1a;数据结构进阶 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;介绍几道二叉树的oj题 题目一 二叉搜索树与双向链表 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表…

帆软—图表专题

饼图 优点&#xff1a;能很直观的看到每一个部分在整体中所占的比例。 缺点&#xff1a;不适合较大的数据集&#xff08;分类&#xff09;展现&#xff0c;数据项中不能有负值&#xff0c;当比例接近时&#xff0c;人眼很难准确判别。 变形饼图 柱形图 特点 优点&#xff…

SpringBoot HandlerMethodArgumentResolver接口自定义参数解析器

参考资料 【SpringBoot】HandlerMethodArgumentResolver的简单使用HandlerMethodArgumentResolver(参数解析器)的作用使用小案例HandlerMethodArgumentResolver用于统一获取当前登录用户 目录一. 需求二. 前期准备三. 实现HandlerMethodArgumentResolver接口四. 将自定义的方法…

一套javassf门户网站管理系统源码分享

淘源码&#xff1a;国内专业的免费源码下载平台 JAVA SSF项目框架源码 后台管理系统源码 一套以SpringMVCSpringHibernateFreemarkerShiroLuceneHtml5jQuery为技术核心架构的门户网站管理系统源码。 源码免费分享&#xff0c;需要源码学习可私信我。 主要功能&#xff1a; 内…

笑对过往、活在当下、期盼未来

哈喽大家好&#xff0c;我是阿Q。 去年今日&#xff0c;也是安静的午后&#xff0c;拿起铅笔在纸上寥寥草草的写下几个年终关键词&#xff0c;思索良久&#xff0c;迟迟未能下笔。 时至今日&#xff0c;年末将至&#xff0c;再次执笔已时隔一年&#xff0c;再不拿笔记录这转瞬…