JavaSE学习值之--认识异常

news2024/11/30 2:30:35

 💕"有效知识的前提是承认知识边界,承认我们对边界那边的一切无可奉告。"💕

作者:Mylvzi 

 文章主要内容:JavaSE学习值之--认识异常 

一.什么是异常?

  异常就是程序在运行的时候产生的不正常的行为  小细节没注意到,发生了小的错误,比如:

1.算数异常

        System.out.println(10/0);

2.空指针异常  null.length

        int[] arr = null;
        System.out.println(arr.length);

3.数组越界异常

        int[] arr = new int[3];
        System.out.println(arr[5]);


  

  可见Java中java中不同类型的异常,都有与其对应的类来进行描述,实际上Java中的异常是一个大的体系

二.异常的体系:

异常它是由编译器识别并给出的,证明其存在于编译器所自带的库文件中,应该是一个类

1. Throwable:

  是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception

2. Error:

  指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表: StackOverflowError和OutOfMemoryError,一旦发生回力乏术。(递归调用时)

3. Exception:

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

三.异常的分类:

  主要是分为两大类 

  • 运行时异常(不受查异常)
    RuntimeException()
  • 编译时异常(受查异常)

1. RunTimeException 包括他的所有子类

在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception)

注意:

  在编写代码过程中出现的语法错误不是运行时异常,比如把main写成了mian,编译器在编译的时候就会报错,这叫错"编译期错误",而运行时异常的产生是所写代码已经经过编译生成.class文件了,再交由JVM运行时产生的错误

补充:RuntimeException的子类

2.编译时异常

  在编译的时候就会发生的异常,又称作受查异常(Checked Exception)

比如之前实现的Cloneable接口

class Stu implements Cloneable {
    String name;
    int age;

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

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

    @Override
    public String toString() {
        return "Stu{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args){
        Stu stu = new Stu("lvzi",18);
        Stu stu2 =(Stu) stu.clone();
        System.out.println(stu2);
}

之前提到,必须在main方法的签名中声明抛出异常,否则会出现异常

 这种在编译阶段就报错的异常叫做编译时异常,又叫做受查异常(Checked Exception)

四.异常的处理

1.防御式编程

    错误的代码总是存在且不可避免地,我们要做的就是当程序出现异常的时候及时告诉程序员,来帮助他修改代码,主要有两种思路:

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

  即对每一个流程都充分想到其可能会发生的错误,及时处理

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

  每进行一步就进行一次检查,但这种方式使正确代码和异常检查的代码混在一起,使得整个代码很混乱,不推荐

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

try {
登陆游戏();
开始匹配();
游戏确认();
选择英雄();
载入游戏画面();
...
} catch (登陆游戏异常) {
处理登陆游戏异常;
} catch (开始匹配异常) {
处理开始匹配异常;
} catch (游戏确认异常) {
处理游戏确认异常;
} catch (选择英雄异常) {
处理选择英雄异常;
} catch (载入游戏画面异常) {
处理载入游戏画面异常;
}

  EAFP的思维简单来说就是管他三七二十一,先执行再说,产生的异常事后处理即可

这种思维是的操作部分和异常处理部分分离,让程序员更加模块化的思考,更有利于代码的编写!!!

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

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

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

throw new XXXException("异常产生的原因")

 举例:

    public static int[] arr ={1,2,3};

    public static int getElem(int index) {
        if(index < 0 || index > arr.length) {
            throw new ArrayIndexOutOfBoundsException(index + "索引不正确,数组越界");
        }

        return arr[index];
    }
    public static void main(String[] args) {
        int[] arr ={1,2,3};
        System.out.println(getElem(0));// 正确  输出1
        System.out.println(getElem(10));// 异常
        System.out.println(getElem(1));// 不输出

    }

 注意:

  • throw必须写在方法内部
  • 抛出的异常必须是Exception或Exception的子类
  • 如果抛出的异常是RuntimeException或其子类,不需要处理,直接交给JVM处理
  • 如果抛出的异常是受查异常,必须通过throws进行处理,否则无法通过编译
  • .异常一旦抛出,其后的代码就不会执行

五.异常的捕获 

   异常的捕获 就是异常处理的具体方式,包括两种方式

  • try,catch语句
  • throws声明

1.异常声明throws 

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

语法格式: 修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{ } 

 比如上文的Cloneable接口的实现:

注意:

  •  throws必须跟在方法的参数列表之后
  • 声明的异常必须是 Exception 或者 Exception 的子类
  • 方法内部抛出了多个异常,throws也要抛出多个异常,异常之间通过”,“隔开;如果异常之间存在父子关系,只需声明父类异常即可
public class Config {
File file;
// public void OpenConfig(String filename) throws IOException,FileNotFoundException{
// FileNotFoundException 继承自 IOException
public void OpenConfig(String filename) throws IOException{
if(filename.endsWith(".ini")){
throw new IOException("文件不是.ini文件");
}
if(filename.equals("config.ini")){
throw new FileNotFoundException("配置文件名字不对");
}
// 打开文件
}
public void readConfig(){
}
}
  • 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出 

2 try-catch捕获并处理 

  throws声明异常并没有处理异常,只是将异常交给调用者去处理,一直传递给JVM虚拟机;要想真正处理异常,就需要try-catch语句

注意:

1.try中存放可能出现异常的代码,也就是说不一定会抛出异常

2.catch()存放异常的类型,{}内部存放处理的代码,处理完成后会继续执行后续代码;如果没有捕获到异常,则catch里的语句并不会被执行

3.finally中的语句一定会被执行, 

4.打印异常有三种

5.如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到 JVM收到后中断程序----异常是按照类型来捕获的 

   

6.如果异常之间存在父子关系,子类异常被捕获时在前,父类在后,如果顺序颠倒,子类异常的捕获就没有任何意义了

public static void main(String[] args) {
int[] arr = {1, 2, 3};
try {
System.out.println("before");
arr = null;
System.out.println(arr[100]);
System.out.println("after");
} catch (Exception e) { // Exception可以捕获到所有异常
e.printStackTrace();
}catch (NullPointerException e){ // 永远都捕获执行到
e.printStackTrace();
}
System.out.println("after try catch");
}

 7.无论如何,finally中的代码一定会被执行

public static int[] arr ={1,2,3};

    public static int getElem(int index) {
        return arr[index];
    }
    public static void main(String[] args) {
        int[] arr ={1,2,3};
        try{
            System.out.print(getElem(100));// 异常
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("finally中的代码一定会被执行");
        }
}

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

思考:那我们该如何记录并处理异常呢?什么时间去处理异常呢?

  在目前我们所写的代码中,常常采取记录错误日志的方式来记录异常,通过编译器的报错告诉程序员出错的地方

六.如何设计一个自定义类型的异常?

  将你可能出现的异常设计为一个继承ExceptionRunTimeException的类,添加一个带参数的构造方法(用于打印错误信息),在调用时,一旦出错就抛出对应的异常即可

  注意:继承Exception的类是受查异常,必须在调用的方法声明中添加throws该异常

继承RunTimeException的类是非受查异常,不需要添加声明

e.printStackTrace();打印异常信息

class UsernameException extends RuntimeException {
    public UsernameException(String message) {
        super(message);
    }
}

class PasswdException extends RuntimeException {
    public PasswdException(String message) {
        super(message);
    }
}


class Login {
    private String Username;
    private String Passwd;

    public static void logininfo(String Username,String Passwd) {
        if(!Username.equals("admin")) {
            throw new UsernameException("用户名错误");
        }else if(!Passwd.equals("123456")) {
            throw new PasswdException("密码错误");
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        try {
            Login.logininfo("admin2","123456");
        }catch (UsernameException ex) {
            ex.printStackTrace();
        }catch (PasswdException ex) {
            ex.printStackTrace();
        }
    }
}

异常总结:

1.throw抛出一个异常

  用于参数检测  一旦出错就可以达到终止程序运行的操作

比如传递数组和数组的下标

1.如果数组是null  直接抛出空指针异常,程序停止运行

2.如果下标超过数组的范围,直接抛出数组越界异常,程序停止运行

之前:出现这种错误,只打印错误的信息,比如arr == null  sout("数组为空"),这种方式太温柔了,只告诉你有错误,但不会终止运行;直接抛异常,既能知道错误的信息,又能终止程序的运行

2.异常处理关键字的理解

throws:是一种甩锅行为,通过在方法的生命中添加throws+异常种类,将这个可能出现的异常交给调用者处理,而调用者可以在他的方法声明时添加throws异常,交给jvm去处理,就是一种甩锅,但很方便,不需要设置try catch

try catch finally:   throws并没有处理异常,只是甩锅给其他人;try catch是一种自己解决异常的方法,通过catch捕获异常并记录异常的日志  所谓的捕获异常就是解决该异常(异常都是被抛出的,catch接受抛出的异常,给他限制在笼子里,这样就没有异常了)

finally:有些代码无论程序是否异常都必须要被执行(比如电脑就算连不上网,也要能看到界面啊?再比如,校园网欠费,无法上网,但是我还能打开缴费网站),如果打不开那我怎么解决上网的问题?所以finally就是用来解决这种任何情况下都要执行的代码(也就是说无论你欠不欠费都能打开缴费网站)

 对于已受查异常来说,其异常会在编译的时候发现,必须通过tycatch语句捕获或者在方法签名使用throws抛出

3.异常处理的流程

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

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

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

相关文章

java_Stream API

文章目录 一、Stream API vs 集合二、Stream 使用的执行流程2.1、创建Stream2.1、中间操作2.1.1. filter2.1.2. limit2.1.3. skip2.1.4. distinct2.1.5. map2.1.6. sorted 一、Stream API vs 集合 Stream API 关注的是多个数据的计算&#xff08;排序、查找、过滤、映射、遍历…

网络工程师知识点4

51、OSPF的LSA类型 52虚链路&#xff1a;作用 解决区域划分不合理的问题 通过建立虚拟链路来实现一般区域与骨干区域的理论化直连 54、NSSA区域的特点 1、可以学习本区域连接的外部路由 2、不学习其他区域转发进来的外部路由 3、与外部区域通信使用ABR自动产生的默认路…

python:爬取网络小说,看这一篇就够了

说明&#xff1a; 本教程仅供于学习研究使用&#xff0c;请勿用于其他用途。 软件安装&#xff1a; 官网下载visual studio Visual Studio: 面向软件开发人员和 Teams 的 IDE 和代码编辑器 (microsoft.com) 点进网页后下拉找到个人免费版本。点击下载即可。 1&#xff1a;找到…

第六章:TF-A学习

TF-A学习 TF-A初探如何获取系统源码移植过程中遇到的问题和解决方案编译报错&#xff1a;arm-none-linux-gnueabi-gcc: not foundTF-A 源码打补丁 遇到assume -R&#xff1f;[n] TF-A初探 为了保证安全 ARM 推出了 Arm Trusted Firmware 的可信固件&#xff0c;简称 TF-A。它…

windows环境cmake的nmake failed

windows环境cmake异常 问题如下&#xff1a; > cmake ..CMake Error at CMakeLists.txt:4 (PROJECT): Running nmake -?failed with:系统找不到指定的文件。CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage CMake Error: CMAKE_CXX_COMPILER not set, after…

中断机制-interrupt和isInterrupted源码分析、中断协商案例

当前线程的中断标识为true&#xff0c;是不是线程就立刻停止&#xff1f; 答案是不立刻停止&#xff0c;具体来说&#xff0c;当对一个线程&#xff0c;调用interrupt时&#xff1a; 如果线程处于正常活动状态&#xff0c;那么会将该线程的中断标志设置为true&#xff0c;仅此…

【C++】:关键字 命名空间 输入输出 缺省参数 函数重载 引用

【本节目标】 C关键字命名空间C输入&输出缺省参数函数重载引用 C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式等 熟悉C语言之后&#xff0c;对C学习有一定的帮助&#xff0c;本章节主要目标&#xff…

关于面试以及小白入职后的一些建议

面试的本质 面试的过程是一个互相选择的过程&#xff1b;面试官的诉求是&#xff0c;了解应聘者的个人基本信息、工作态度、专业能力及其他综合能力是否与公司招聘岗位匹配&#xff1b;面试者的诉求是&#xff0c;拿下招聘岗位offer&#xff0c;获得工作报酬&#xff1b; 面试…

【go学习笔记】Go errors 最佳实践

文章目录 一、Error Type1. Sentinel Error&#xff08;预定义Error字符串错误值&#xff09;1.1 缺点 2. Error types&#xff08;错误类型&#xff09;2.1 缺点 3. Opaque errors&#xff08;不透明错误&#xff09;3.1 Assert errors for behaviour, not type 二、Handling …

详解RocketMQ消息存储原理

本文基于RocketMQ 4.6.0进行源码分析 一. 存储概要设计 RocketMQ存储的文件主要包括CommitLog文件、ConsumeQueue文件、Index文件。RocketMQ将所有topic的消息存储在同一个文件中&#xff0c;确保消息发送时按顺序写文件&#xff0c;尽最大的能力确保消息发送的高性能与高吞吐…

SpringBoot实战(二十五)集成 Shiro

目录 一、Shiro 简介1.1 Shiro 定义1.2 Shiro 核心组件1.3 Shiro 认证过程 二、SpringBoot集成2.1 集成思路2.2 Maven依赖2.3 自定义 Realm2.4 Shiro 配置类2.5 静态资源映射2.6 AuthController2.7 User 实体2.8 用户接口类2.9 用户接口实现类2.10 OrderController&#xff08;…

手把手教你使用Python从零开始搭建感知器

大家好&#xff0c;今天本文将展示如何从零开始实现神经网络的最基本要素&#xff08;感知器&#xff09;&#xff0c;以及人工智能的基本模块背后的数学原理。 虽然人工智能和机器学习等术语已经成为流行词汇&#xff0c;每天都会听到或谈论这些概念&#xff0c;但它们背后的…

软件开发介绍

一、软件开发整体介绍 作为一名软件开发工程师&#xff0c;我们需要了解在软件开发过程中的开发流程&#xff0c;以及软件开发过程中涉及到的岗位角色&#xff0c;角色的分工、职责&#xff0c;并了解软件开发中涉及到的三种软件环境。 1.1 软件开发流程 第一阶段&#xff1a…

MQTT解读【全网最易懂】

目录 前言 一、MQTT相比于TCP长连接的优势 1、协议更标准 2、MQTT协议制定好了很多利于物联网的功能 3、理解数据内容&#xff0c;用数据产生价值 二、选择MQTT还是TCP长连接透传 1、原始的业务场景 2、端对端M2M场景——无人汽车 3、APP控制设备端场景——智能家居、智…

RK3588的GPU驱动和桌面环境

这里主要是以orange pi 5 plus为对象作一个简单的笔记 首先看rk3588的gpu介绍&#xff0c;它用的是ARM的GPU&#xff0c;支持openGL ES和openCL&#xff08;支持什么其实跟GPU驱动有关&#xff0c;arm官方闭源GPU驱动就只支持这两个&#xff09; opi官方提供了debian的xfce和…

Linux网络编程系列之服务器编程——多路复用模型

Linux网络编程系列 &#xff08;够吃&#xff0c;管饱&#xff09; 1、Linux网络编程系列之网络编程基础 2、Linux网络编程系列之TCP协议编程 3、Linux网络编程系列之UDP协议编程 4、Linux网络编程系列之UDP广播 5、Linux网络编程系列之UDP组播 6、Linux网络编程系列之服务器编…

ROS-6.参数的使用

参数的使用 参数服务结构命令行的使用方式运行小海龟命令介绍查看参数获取参数值设置参数保存参数到文件从文件导入参数 通过程序操作创建节点修改cmake编译运行 参数服务结构 ros中存在参数服务管理服务&#xff0c;管理这所有参数&#xff0c;所有节点剋订阅和发布这些节点 …

第三章 内存管理 三、覆盖与交换

目录 一、覆盖技术 二、交换技术 三、总结 一、覆盖技术 1、在覆盖技术中&#xff0c;我们要找到程序的调用结构。 2、因为这些程序不可能同时被调用&#xff08;互斥调用&#xff09;&#xff0c;所以我们只需要选出需要空间最大的程序。 3、在物理内存中开拓一片与最大程…

ABB机器人关于重定位移动讲解

关于机器人如何重定位移动&#xff0c;首先来看一下示教器上的重定位移动是在哪。 从图中所示的坐标位置和操纵杆方向得知&#xff0c;重定位的本质是绕X、Y、Z轴的旋转。那么实现跟摇杆一样的操作&#xff0c;就可以通过改变当前位置的欧拉角来实现&#xff0c;参考Rapid指令…

小米笔记本Pro 15.6“频繁蓝屏解决办法

一、事情的缘起 2020年3月&#xff0c;我在小米官网购买这个笔记本&#xff0c;型号为&#xff1a;小米笔记本Pro 15.6" 2019款 四核i5 8G MX250 深灰。当时买这款笔记本&#xff0c;也是考虑到它屏幕比较大&#xff0c;而且配置也不错&#xff0c;四核8G的内存也足够我办…