Java SE 认识异常 (Java SE完结篇)

1. 异常的概念与体系结构

1.1 异常的概念

在我们的生活中,一个人如果表情痛苦,我们可能会问: 你是生病了吗? 需要我陪你去看医生吗?
在这里插入图片描述
程序也和人是一样的,均会发生一些"生病"的行为,比如: 数据格式不对, 数组越界,网络中断等, 我们把这种程序出现的"生病"行为称为"异常".比如在我们之前写代码经常遇到的"报红",大多数都是异常.

  1. 算数异常
    在这里插入图片描述
  2. 数组越界异常
    在这里插入图片描述
  3. 空指针异常
    在这里插入图片描述
    从上述的展示中可以看出,异常也分不同的类型,都有与其对应的类来进行描述,总的来说,异常本质上是一种类.

1.2 异常的体系结构

异常的种类很多,为了对不同类型的异常或错误进行更好的管理,Java内部制定了异常的体系结构:
在这里插入图片描述

从上图中我们可以看到:

  1. Throwable是异常体系的顶层类,由它派生出了两个子类:Error和Exception,即错误和异常.
  2. Error:指的是Java虚拟机无法解决的严重问题,如JVM内部错误,资源耗尽等,典型代表就是StackOverflowError(栈溢出错误).
  3. Exception: 异常产生后程序员可以通过修改代码进行处理,可以使得程序继续执行.

1.3 异常的分类

根据异常发生的时间不同,我们可以将异常分为:

  1. 编译时异常
    在程序编译期间发生的异常,称为编译时异常,也称为受查异常,我们在用idea编译器时,编译器会在出现异常的语句下面划红线.
 public class Person {
        private int age;
        private String name;

        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
    @Override
        protected Object clone() {
            return super.clone();
        }
}

在这里插入图片描述
从上述的代码我们可以看出,在clone方法之后我们没有对发生的异常进行声明,所以在放重写的方法那里产生了编译时异常,在clone下面划了红线.

  1. 运行时异常
    在程序执行的期间发生的异常,称为运行时异常,也称为非受查异常,RunTimeException以及子类对应的异常,都叫运行时异常,比如我们上面提到的: 算数异常(ArithmeticException),空指针异常(NullPointerException),数组越界异常(ArraryIndexOutOfBoundsException).

2. 异常的处理

2.1 防御式编程

错误在代码中是客观存在的.此时就需要把出现的错误和异常及时通知程序员,主要有以下两种方式:

  1. LBYL: look before your leap. 在操作之前就进行充分的检查,即:事前防御,语法格式如下:
boolean ret = false;
ret = 操作1;
if(!ret){
	处理异常1;
	return;
}
ret = 操作2;
if(!ret){
	处理异常2;
	return;
}
......

缺点: 正常的流程和错误的处理混在一起,代码显得比较凌乱.
2. EAFP: It’s Easier to ask forgiveness than permission. 事后获取原谅比事前获取许可更容易.也就是先操作,遇到的问题放在最后一起处理,即:事后认错型.

try{
	操作1;
	操作2;
	......
}catch(异常1){
	处理异常1;
}catch(异常2){
	处理异常2;
}
.......

优势: 正常的流程和错误的处理是分开的,代码更加清晰,容易理解.
异常处理的核心思想就是EAFP
在Java中,处理异常主要有5个关键字: throw, try, catch, finally , throws.

2.2 异常的抛出

在编写程序时, 如果程序中出现错误, 此时就需要将错误的信息报告给调用者.
在Java中,可以使用throw关键字,抛出一个指定的异常对象,来将错误信息来报告给调用者.具体语法格式如下:

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

我们举一个例子来说明:例如我们要获取数组任意位置的元素和方法.

public static int getElement(int[] array,int index){
        if (array == null){
            throw new NullPointerException("element of array is null");
        }
        if (index > array.length-1||index < 0){
            throw new ArrayIndexOutOfBoundsException("index is out of array");
        }
        return array[index];
    }
public static void main(String[] args) {
        int[] array = {1,2,3};
        getElement(array,3);
        int[] array2 = null;
        getElement(array2,2);
    }

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

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

2.3 异常的捕获

异常的捕获,通常有两种: 异常的throws声明以及try-catch捕获处理

2.3.1 异常throws声明

处于方法声明的参数表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给调用者来处理,即当前方法不处理异常,提醒方法的调用者处理该异常.若调用者没有处理或throws,就会报错,语法格式如下:

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

}

下面我们拿我们上面提到的clone方法来举例

public class Person {
        private int age;
        private String name;

        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
    @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
}
public class MyException {
    public static void main(String[] args)throws CloneNotSupportedException {
        Person p1 = new Person(12,"zhangsan");
        Person p2 = (Person) p1.clone();
    }
}

在这里插入图片描述
上述的代码我们可以看出如果我们在main方法调用clone方法的时候也没有直接处理异常,把异常throws了,该异常就会交给jvm来处理,若处理失败,程序会在出现异常的地方立即终止.
我们对上述代码进行一定地修正

public class Person implements Cloneable {
        private int age;
        private String name;

        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
    @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
}
public class MyException {
    public static void main(String[] args) throws CloneNotSupportedException{
        Person p1 = new Person(12,"zhangsan");
        Person p2 = (Person) p1.clone();
    }
}

上述代码我们对Person实现了Cloneable接口,jvm处理异常成功,程序运行之后便不会报错.

[注意事项]

  1. throws必须跟在方法的参数列表之后.
  2. 声明的异常必须是Exception或者是Exception的子类.
  3. 方法内部如果有多个异常,throws之后必须跟上多个异常,之间用逗号隔开,若抛出的异常具有父子关系,直接声明父类即可.
public static int getElement(int[] array,int index) throws RuntimeException{//声明运行时异常即可
        if (array == null){
            throw new NullPointerException("element of array is null");
        }
        if (index > array.length-1||index < 0){
            throw new ArrayIndexOutOfBoundsException("index is out of array");
        }
        return array[index];
    }

  1. 调用抛出异常的方法时,必须对异常进行处理,或者继续使用throws抛出.
public static int getElement(int[] array,int index) throws RuntimeException{
        if (array == null){
            throw new NullPointerException("element of array is null");
        }
        if (index > array.length-1||index < 0){
            throw new ArrayIndexOutOfBoundsException("index is out of array");
        }
        return array[index];
    }
public static void main(String[] args) throws RuntimeException{
       int[] array = {1,2,3};
       getElement(array,3);
       int[] array2 = null;
       getElement(array2,2);
 }
2.3.2 捕获并处理

throws对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。如果真正要对异常进行处理,就需要try-catch.
语法格式如下:

try{
	操作1;
	操作2;
}catch(异常1){
	处理异常1;
}catch(异常2){
	处理异常2;
}finally{
    此处的代码一定会被执行
}
//后续代码

如果捕获异常成功,并且异常被处理成功,会跳出try-catch结构,之后的代码也会被执行,若有异常没有被捕获到,后续的代码就不会被执行.我们下面举个例子:

public static int getElement(int[] array,int index){
        if (array == null){
            throw new NullPointerException("element of array is null");
        }
        if (index > array.length-1||index < 0){
            throw new ArrayIndexOutOfBoundsException("index is out of array");
        }
        return array[index];
    }
    public static void main(String[] args) {
        try {
            int[] array = {1,2,3};
            getElement(array,3);
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("处理了数组越界异常");
        }
        System.out.println("异常处理结束,后续代码被执行");
    }

关于异常的处理方式
异常的种类有很多, 我们要根据不同的业务场景来决定.
对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果
对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿
对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试.
在我们当前的代码中采取的是经过简化的第二种方式. 我们记录的错误日志是出现异常的方法调用信息, 能很快速的让我们找到出现异常的位置. 以后在实际工作中我们会采取更完备的方式来记录异常信息.

[注意事项]

  1. try代码块中,如果有一个地方抛出了异常,这个地方之后的代码不会被执行.
public static int getElement(int[] array,int index) throws RuntimeException{
        if (array == null){
            throw new NullPointerException("element of array is null");
        }
        if (index > array.length-1||index < 0){
            throw new ArrayIndexOutOfBoundsException("index is out of array");
        }
        return array[index];
    }
    public static void main(String[] args) {
        try {
            int[] array = {1,2,3};
            getElement(array,3);
            int[] array2 = null;
            getElement(array2,2);
        }catch (NullPointerException e){
            System.out.println("处理了空指针异常");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("处理了数组越界异常");
        }
        System.out.println("异常处理结束,后续代码被执行");
    }

在这里插入图片描述
由此可见,运行结果中并,没有打印空指针异常.即可说明上述一点.
2. 如果异常类型与catch时的异常类型不匹配,异常就不会被捕获成功,也不会被处理,继续抛出,知道JVM收到后终止.

ublic static void main(String[] args) {
        try {
            int[] array = {1,2,3};
            getElement(array,3);
        }catch (NullPointerException e){
            System.out.println("处理了空指针异常");
        }
        System.out.println("异常处理结束,后续代码被执行");
    }
    public static int getElement(int[] array,int index) throws RuntimeException{
        if (array == null){
            throw new NullPointerException("element of array is null");
        }
        if (index > array.length-1||index < 0){
            throw new ArrayIndexOutOfBoundsException("index is out of array");
        }
        return array[index];
    }

在这里插入图片描述
3. try中可能会有多个异常,必须用多个catch来捕获

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 (ArrayIndexOutOfBoundsException e) {
            System.out.println("这是个数组下标越界异常");
            e.printStackTrace();
        } catch (NullPointerException e) {
            System.out.println("这是个空指针异常");
            e.printStackTrace();
        }
        System.out.println("after try catch");
    }

若多个异常的处理方式完全相同,也可以写成这样:

catch (ArrayIndexOutOfBoundsException|NullPointerException){
......
}
  1. 如果异常之间有父子关系,一定是子类在前,父类在后.
public static void main(String[] args) {
        try {
            int[] array = {1,2,3};
            getElement(array,3);
        }catch (Exception e){
            System.out.println("处理了异常");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("处理了数组越界异常");
        }
        System.out.println("异常处理结束,后续代码被执行");
    }

在这里插入图片描述
数组越界异常已经被第一个catch处理了,该异常的捕获类型属于数组越界处理的父类,而后面的子类没有处理到,所以会报错.

2.3.3 finally

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

语法格式:
try{
	// 可能会发生异常的代码
}catch(异常类型 e){
	// 对捕获到的异常进行处理
}finally{
	// 此处的语句无论是否发生异常,都会被执行到
}
	// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行
public static void main(String[] args) {
        try {
            int[] array = {1,2,3};
            getElement(array,3);
        }catch (NullPointerException e){
            System.out.println("处理了空指针异常");
        }finally {
            System.out.println("一定会被执行");
        }
        System.out.println("异常处理结束,后续代码被执行");
    }

在这里插入图片描述
我们从上述执行结果可以看到,无论异常有没有被捕获到,finally一定会被执行到.
finally的代码一般用来进行一些资源清理的扫尾工作.例如:

public 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 {
		if(null != sc){
		sc.close();
	}
	return 0;
}

从上述代码中,我们知道如果sc被成功调用,直接返回了,若没有finally语句,sc的资源没有进行关闭,造成了资源泄露.

面试题

  1. throw 和 throws 的区别?throw用来抛出异常,throws用来声明异常,提醒调用者.
  2. finally中的语句一定会执行吗?一定会.

2.4 异常处理的流程 (总结)

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

3. 自定义异常

Java出了本身给出的异常类之外,我们还可以自定义异常,在重写的构造方法中,我们可以自定义异常原因.

ublic class PassWordException extends RuntimeException{//继承于运行时异常
    public PassWordException(String message) {//构造方法输入出现异常的原因
        super(message);
    }
}
public static void main(String[] args) {
        String password = "123457";
        if (password != "123456"){
            throw new PassWordException("password is false");
        }
    }

在这里插入图片描述
[注意事项]
继承自Exception时默认是编译时异常.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ASO优化:App在App Store的权重影响因素

1.App的标题描述 App的标题、描述是能引导用户下载的重要部分&#xff0c;此处关键词占比的权重是最大的。比如说爱奇艺&#xff0c;最近主推的就是由任嘉伦、刑菲主演的《烈焰》。它就把主推的内容放在副标题处&#xff0c;获得很大的曝光量&#xff0c;娱乐榜直接排第一名了…

C语言学习笔记day8

一维数组冒泡排序法 1. 作用 将乱序的一维数组按照从小到大的顺序排列 2. 原理示意图 3. 代码 #include <stdio.h> #include <stdlib.h> #include <time.h>int main(void) {int a[5] {0};int len sizeof(a) / sizeof(a[0]);int i 0;int j 0;int tmp …

Vue工程化基础

一Ajax 1.1Ajax概述&#xff1a; 异步与同步 繁琐被淘汰了。 二Axios2 前后端混合开发&#xff1a; 前后端分离开发&#xff1a; YAPI 三前端开发工程化 四Vue脚手架 项目的认识 改变端口号 五Vue开发流程&#xff1a; 六Element组件 6.1快速入门 下载> npm install e…

Python数据分析-Matplotlib1

一、折线图的绘制 1.数据分析流程 2.运用Matplot绘制折线图 #encodingutf-8 import random from matplotlib import pyplot as plt #绘图工具库 from matplotlib import font_manager #解决中文显示问题 from cProfile import label #设置字体方式 my_font font_manager.Fon…

kafka集群介绍及搭建

介绍 kafka是一个高性能、低延迟、分布式的消息传递系统&#xff0c;特点在于实时处理数据。集群由多个成员节点broker组成&#xff0c;每个节点都可以独立处理消息传递和存储任务。 路由策略 发布消息由key、value组成&#xff0c;真正的消息是value&#xff0c;key是标识路…

Two Birds with One Stone

learnable mask M 辅助信息 作者未提供代码

Illustrator 2024:创意与技术的完美融合,引领矢量设计新潮流

Illustrator 2024是一款由Adobe公司倾力打造的强大矢量图形设计软件&#xff0c;以其丰富的绘图工具、卓越的设计功能和直观的操作界面&#xff0c;成为专业设计师和创意工作者的首选工具。这款软件不仅提供了画笔、铅笔、形状、路径等多种工具&#xff0c;帮助用户轻松创建各种…

Python+Appium+Pytest+Allure实战APP自动化测试!

pytest只是单独的一个单元测试框架&#xff0c;要完成app测试自动化需要把pytest和appium进行整合&#xff0c;同时利用allure完成测试报告的产出。 编写常规的线性脚本具体的步骤如下&#xff1a; 1、设计待测试APP的自动化测试用例 2、新建app测试项目 3、配置conftest.py文…

精读《架构设计之 DCI》

本期精读文章是&#xff1a;The DCI Architecture 1 引言 随着前端 ES6 ES7 的一路前行&#xff0c; 我们大前端借鉴和引进了各种其他编程语言中的概念、特性、模式; 我们可以使用函数式 Functional 编程设计&#xff0c;可以使用面向对象 OOP 的设计&#xff0c;可以使用面向…

ai写作一键生成,分享6种好用的写作软件,一定要看

在写文章时&#xff0c;我们常常会遇到灵感丧失、词句不顺的情况&#xff0c;为了解决这一问题&#xff0c;小编为大家推荐几款实用的AI写作软件&#xff0c;一同来探索一下吧&#xff01; 一、爱制作AI 爱制作AI是一款专注于写作的软件&#xff0c;强大的智能数据库让它备受…

避免内存泄漏及泄漏后的排查方法【C++】

内存泄漏 前言编码std::unique_ptr申请单个对象申请对象数组 std::shared_ptr申请单个对象申请对象数组 编码总结 前言 最近在工作中被内存泄漏疯狂折磨&#xff0c;整理一下自己的思考。 编码 最近在工作中被内存泄漏疯狂折磨&#xff0c;我真的奉劝各位&#xff0c;如果你…

生成式 AI 术语指南:带有配图说明,没有数学公式

编者按&#xff1a; 生成式人工智能技术的发展日新月异&#xff0c;这一领域涉及到了越来越多的专业术语和概念。对于刚接触这一领域的新手来说&#xff0c;理解这些术语算是一个门槛。我们有必要整理和解释这些术语&#xff0c;帮助更多人快速入门&#xff0c;投身 AI 事业。 …

Leetcode 202.快乐数 JAVA

题目 思路 要注意题目中说的无限循环&#xff1a;它是指在求平方和的过程中&#xff0c;会再次出现之前的值&#xff08;想象一个圈&#xff09;&#xff0c;这种情况的时候肯定算不出1来。 所以我们要设定跳出循环的条件是&#xff1a;当平方和结果为1或者出现循环了 出现循…

应届生岗位直达服务

详情请私信了解 技术面试&#xff1a; C技术深入学习资源礼包&#xff08;岗位技术栈查漏补缺/非卖品&#xff09; 系统设计面试的准备 模拟技术面试和问题纠错反馈 职业发展和软技能&#xff1a; 简历优化和面试技巧 职业规划和目标设定 沟通和团队协作技能 实际…

Redis的安装和部署教程(Windows环境)

一、安装Redis服务 1、下载Redis压缩包 以下这个是我网盘里面的&#xff08;这个是v8.0版本的&#xff0c;支持导入.rdb数据文件&#xff09; 链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;x0f1 --来自百度网盘超级会员V5的分享 2、解压到文件夹 将下载的压缩…

NSGA-III算法:如何在多目标优化问题中找到最合适的解

当我们面临多个目标函数时&#xff0c;单目标的遗传算法可能无法满足需求。这时&#xff0c;我们可以引入多目标遗传算法。在这种情况下&#xff0c;目标函数可能存在冲突&#xff0c;例如&#xff0c;一个目标函数需要最小化&#xff0c;而另一个目标函数需要最大化。某个目标…

利用Python进行网络爬虫:Beautiful Soup和Requests的应用【第131篇—Beautiful Soup】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 利用Python进行网络爬虫&#xff1a;Beautiful Soup和Requests的应用 在网络数据变得日益丰…

基于多尺度视网膜增强图像去雾算法(MSR,Multi-Scale Retinex),Matalb实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供有偿…

Mit6.s081 前置开发环境: 虚拟机ubuntu + ssh + vscode

虚拟机 ssh vscode 前置条件 下载VMware Download VMware Workstation ProUbuntuUbuntu系统下载 | Ubuntu vscode Visual Studio Code - Code Editing. Redefined Ubuntu版本&#xff1a;20.04 Ubuntu基本操作 ubuntu 安装 ssh 服务 sudo apt-get install openssh-serv…

前端学习之css伪元素选择器

伪元素选择器 &#xff08;注释是对各个内容的解释与理解&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>伪元素选择器</title><!-- 双冒号开头一般都称为伪元素&#xff0c;…