[Java]异常处理

news2024/9/22 6:59:45

文章目录

  • 🥽 异常概述
  • 🥽 异常的分类
  • 🥽 异常的处理
    • 🌊 异常处理机制一:try-catch-finally
      • 💦 语法结构
      • 💦 try-catch
      • 💦 finally
      • 💦 try-catch-finally处理异常的执行流程
    • 🌊 编译时异常与运行时异常的不同处理
    • 🌊 异常处理机制二:throws+异常类型
    • 🌊 重写方法异常抛出的规则
    • 🌊 异常处理方式的选择
  • 🥽 手动抛出异常对象
  • 🥽 用户自定义异常类


🥽 异常概述

  • 异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)

  • Java程序在执行过程中所发生的异常事件可分为两类:

    • Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。
    • Exception(异常): 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:
      • 空指针访问
      • 试图读取不存在的文件
      • 网络连接中断
      • 数组角标越界
  • 对于这些错误,一般有两种解决方法:一是遇到错误就终止程序的运行。另一种方法是由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。

  • 捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。比如:除数为0,数组下标越界等。

🥽 异常的分类

异常的分类:编译时异常和运行时异常

  • 运行时异常
    • 是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子 类都是运行时异常。
    • 对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
  • 编译时异常
    • 是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。
    • 对于这类异常,如果程序不处理,可能会带来意想不到的结果。

在这里插入图片描述

🥽 异常的处理

  • Java采用的异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。
  • Java提供的是异常处理的抓抛模型。
    • 抛:Java程序的执行过程中如出现异常,会生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。一旦抛出异常对象,抛出异常对象位置之后的代码不再执行。
    • 抓:异常的处理方式
      • try-catch-finally
      • throws
  • 异常对象的生成
    • 由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,如果在当前代码中没有找到相应的处理程序,就会在后台自动创建一个对应异常类的实例对象并抛出——自动抛出
    • 由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样
  • 如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理。如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。这一过程称为捕获(catch)异常。
  • 如果一个异常回到main()方法,并且main()也不处理,则程序运行终止。
  • 程序员通常只能处理Exception,而对Error无能为力。

在这里插入图片描述

🌊 异常处理机制一:try-catch-finally

💦 语法结构

try{
...... //可能产生异常的代码
}
catch( ExceptionName1 e ){
...... //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
...... //当产生ExceptionName2型异常时的处置措施
}
......
[ finally{
...... //无论是否发生异常,都无条件执行的语句
} ]

💦 try-catch

  • try
    • 捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。
  • catch (Exceptiontype e)
    • 在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
    • 如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可以用其父类作为catch的参数。比 如 : 可 以 用 ArithmeticException 类 作 为 参 数 的 地 方 , 就 可 以 用RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。但不能是与ArithmeticException类无关的异常,如NullPointerException(catch中的语句将不会执行)。
    • 捕获异常的有关信息:
      • 与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。
      • getMessage() 获取异常信息,返回字符串
      • printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。该方法会将获取到的信息在控制台进行打印。
    @Test
    public void test1() {
        String str = "123";
        str = "abc";
        int num = 0;
        try {
            num = Integer.parseInt(str);
            System.out.println("hello-----1");
        } catch (NumberFormatException e) {
            //System.out.println("出现数值转换异常了,不要着急....");
            //String getMessage():
            //System.out.println(e.getMessage());
            //printStackTrace():
            e.printStackTrace();
        } catch (NullPointerException e) {
            System.out.println("出现空指针异常了,不要着急....");
        } catch (Exception e) {
            System.out.println("出现异常了,不要着急....");
        }
        System.out.println(num);
        System.out.println("hello-----2");
    }

在这里插入图片描述

  1. finally是可选的。
  2. 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
  3. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的 try-catch结构(在没有写finally的情况)。继续执行其后的代码
  4. catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。 catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错
  5. 常用的异常对象处理的方式: ① String getMessage() ② printStackTrace()
  6. 在try结构中声明的变量,在出了try结构以后,就不能再被调用

使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。

💦 finally

  • 捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
  • 不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,try语句或catch语句中是否有return,finally块中的语句都会被执行。
  • finally语句是可选的
    @Test
    public void testMethod() {
        int num = method();
        System.out.println(num);
    }

    public int method() {
        try {
            int[] arr = new int[10];
            System.out.println(arr[10]); // 出现异常,生成异常对象进入对应的catch语句
            return 1;
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
            return 2; // 由于有finally语句(一定会执行),所以进入finally语句再回来执行return
        } finally {
            System.out.println("我一定会被执行");
            return 3; // 由于在finally语句中进行了return,直接结束该方法返回3
        }
    }

在这里插入图片描述

    @Test
    public void test2() {
        FileInputStream fis = null;
        try {
            File file = new File("hello1.txt");
            fis = new FileInputStream(file);
            int data = fis.read();
            while (data != -1) {
                System.out.print((char) data);
                data = fis.read();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 避免空指针异常
                // fis = new FileInputStream(file); 可能报异常而创建失败会使得fis为null
                if (fis != null) fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  • 像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
  • try-catch-finally结构可以嵌套

💦 try-catch-finally处理异常的执行流程

在这里插入图片描述

🌊 编译时异常与运行时异常的不同处理

  • 开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理。
  • 对于运行时异常一般是代码的逻辑出现问题,所以一般不采用try-catch,因为是代码本身出现问题,所以一般采用修改代码处理出现的运行时异常。编译时异常一般情况下不是由于代码出现问题,所以一般采用try-catch处理编译时异常。

🌊 异常处理机制二:throws+异常类型

如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。

“throws + 异常类型” 写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!

    public static void main(String[] args) {
        // method2没有对method1抛出的异常进行处理
        // 而是继续抛出异常,所以此处需要异常处理
        try {
            method2();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 由于method3中对异常进行处理了,没有抛出异常
        // 此处不用异常处理
        // method3();
    }
    
    public static void method3() {
        try {
            method2();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static void method2() throws IOException {
        method1();
    }
    
    public static void method1() throws FileNotFoundException, IOException {
        File file = new File("hello1.txt");
        FileInputStream fis = new FileInputStream(file);
        int data = fis.read();
        while (data != -1) {
            System.out.print((char) data);
            data = fis.read();
        }
        fis.close();
        // 由于文件不存在
        // File file = new File("hello1.txt"); 此处就会生成异常对象
        // File file = new File("hello1.txt"); 后的代码不会执行
        System.out.println("hahaha!");
    }

try-catch-finally:真正的将异常给处理掉了。 throws的方式只是将异常抛给了方法的调用者。并没有真正将异常处理掉。

🌊 重写方法异常抛出的规则

子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。

public class OverrideTest {
    public static void main(String[] args) {
        OverrideTest test = new OverrideTest();
        // 此时将子类作为形参传递给接收父类类型参数的方法
        // 如果子类重写方法抛出的异常小于等于父类被重写方法抛出的异常
        // 则在调用方法中可以对异常继续捕获处理
        // 如果子类重写方法抛出的异常大于父类被重写方法抛出的异常
        // 则在调用方法中不能对异常继续捕获处理,抛出的异常类型大于catch中捕获的异常
        test.display(new SubClass());
    }

    public void display(SuperClass s) {
        try {
            s.method();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class SuperClass {
    public void method() throws IOException {
        
    }
}

class SubClass extends SuperClass {
    public void method() throws FileNotFoundException {

    }
}

🌊 异常处理方式的选择

  • 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
  • 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。

🥽 手动抛出异常对象

public class StudentTest {
	public static void main(String[] args) {
		try {
			Student s = new Student();
			s.regist(-1001); 
			// 如果在regist方法中出现逻辑不允许的异常
			// 手动生成一个异常对象,并将该异常对象抛出
			// 可以阻止后续代码的执行,阻止逻辑错误后继续运行
			System.out.println(s);
		} catch (Exception e) {
//			e.printStackTrace();
			System.out.println(e.getMessage());
		}
	}
}


class Student{
	
	private int id;
	
	// 抛出手动生成的异常对象
	public void regist(int id) throws Exception {
		if(id > 0){
			this.id = id;
		}else{
			//System.out.println("您输入的数据非法!");
			//手动抛出异常对象
			//throw new RuntimeException("您输入的数据非法!");
			//传入的字符串相当于为e.getMessage()读取的属性赋值
			throw new Exception("您输入的数据非法!");
		}
	}

	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}
}

🥽 用户自定义异常类

  • 如何自定义异常类?
    1. 继承于现有的异常结构:RuntimeException 、Exception
    2. 提供全局常量:serialVersionUID,用于唯一标识一个类,序列化时用于不同类的识别
    3. 提供重载的构造器,一般一个无参构造器和一个需要传入异常信息为形参的构造器
public class MyException extends Exception{
	// serialVersionUID,用于唯一标识一个类,序列化时用于不同类的识别
	static final long serialVersionUID = -7034897193246939L;
	
	public MyException(){
		
	}
	
	public MyException(String msg){
		super(msg); // 调用父类相应的构造器为e.getMessage()读取的属性赋值
	}
}
public class StudentTest {
	public static void main(String[] args) {
		try {
			Student s = new Student();
			s.regist(-1001);
			System.out.println(s);
		} catch (Exception e) {
//			e.printStackTrace();
			System.out.println(e.getMessage());
		}
	}
}


class Student{
	
	private int id;
	
	public void regist(int id) throws Exception {
		if(id > 0){
			this.id = id;
		}else{
//			System.out.println("您输入的数据非法!");
			//手动抛出异常对象
//			throw new RuntimeException("您输入的数据非法!");
//			throw new Exception("您输入的数据非法!");
			// 抛出自定义异常对象
			// 手动抛出的只能是异常体系的对象
			throw new MyException("不能输入负数");
		}
		
	}

	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}
}

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

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

相关文章

7.0、Linux-Vim编辑器以及常用命令详解

7.0、Linux-Vim编辑器以及常用命令详解 什么是 Vim 编辑器 -> Vim 是从 vi 发展出来的一个文本编辑器;代码补全、编译以及错误等方便编程的功能特别丰富,在程序员中被广泛使用;简单的来说,vi 是老式的字处理器,不过…

未知感知对象检测:从开放视频中学习你不知道的东西(学习笔记)

Unknown-Aware Object Detection: Learning What You Dont Know from Videos in the wild paper: https://arxiv.org/abs/2203.03800 code: https://github.com/deeplearning-wisc/stud the Wild 弄一个靠谱的目标检测器,完成OOD问题 什么是out of distribution (…

PHP Tools for Visual Studio 2019-2022 1.7 Crack

PHP Tools 是一个完整的 PHP 开发环境,位于单个软件包中。利用众所周知的行业标准 IDE 开发小型项目直至大型 PHP 应用程序。 该编辑器具有智能代码分析和快速抢占式代码完成功能。通过大量的导航功能、手边的本地化手册或快速重构操作来提高您的工作效率。 检查代码…

基于Amlogic 安卓9.0, 驱动简说(五):基于GPIO、LED子系统的LED驱动

一、篇头 本章介绍LED子系统的使用。使用LED子系统,可以轻松实现对LED,例如常见的闪烁和亮度控制功能。简单起见,本章先使用GPIO实现,在不模拟PWM的情况下,只能实现点亮和灭灯的效果,重点是介绍GPIO、LED子…

腾讯前端一面常考vue面试题汇总

vue2.x详细 1. 分析 首先找到vue的构造函数 源码位置:src\core\instance\index.js function Vue (options) {if (process.env.NODE_ENV ! production &&!(this instanceof Vue)) {warn(Vue is a constructor and should be called with the new keyword…

【Linux】Linux进程的理解 --- 进程描述符、状态、优先级、切换…

如果不改变自己,就别把跨年搞的和分水岭一样,记住你今年是什么吊样,明年就还会是什么吊样!!! 文章目录一、冯诺依曼体系结构(硬件)二、操作系统(软件)1.操作…

HCIP第六天

文章目录一,实验要求二,搭建拓扑图三,配置IP地址和环回地址四,宣告OSPF五,抓取流量六,测试一,实验要求 所有到达目标的路径最优,互有备份二,搭建拓扑图 三,配置IP地址和环…

photoshop绘制网格线的几种办法和重复绘制处理加工

第一种 绘制一个十字 ,然后保存为图案,然后添加图层样式 图案叠加 重复 第二种 显示网格线 编辑-首选项-参考线网格线设置 默认是25, 25*5125毫米 image.png新建125x125毫米会发现非常对称 image.png画笔直接按shift自动锁定网格线进行绘制这样非常标准也很快,比图案还要灵活一…

60. 实战 Kaggle 比赛:图像分类 (CIFAR-10)【在colab上运行】

之前几节中,我们一直在使用深度学习框架的高级API直接获取张量格式的图像数据集。 但是在实践中,图像数据集通常以图像文件的形式出现。 本节将从原始图像文件开始,然后逐步组织、读取并将它们转换为张量格式。 我们之前对CIFAR-10数据集做了…

论文投稿指南——中文核心期刊推荐(生物科学)

【前言】 🚀 想发论文怎么办?手把手教你论文如何投稿!那么,首先要搞懂投稿目标——论文期刊 🎄 在期刊论文的分布中,存在一种普遍现象:即对于某一特定的学科或专业来说,少数期刊所含…

vue开发环境配置Visual Studio Code配置和安装教程查询

方便前端vue开发,使用vs code插件安装详细教程,关于vs code在网络上查询相关的教程,插件安装如下图所示,大家发现常用的,好用的插件可以留言分享,或与我联系。 1 安装Vue语法高亮显示插件:vetur…

63.目标检测数据集

目标检测领域没有像MNIST和Fashion-MNIST那样的小数据集。 为了快速测试目标检测模型,我们收集并标记了一个小型数据集。 首先,我们拍摄了一组香蕉的照片,并生成了1000张不同角度和大小的香蕉图像。 然后,我们在一些背景图片的随机…

MSF弱点扫描

● 根据信息收集结果搜索漏洞利用模块 ● 结合外部漏洞扫描系统对大IP地址段进行批量扫描 ● 误判率、漏判率 VNC密码破解 use auxiliary/scanner/vnc/vnc_login● VNC无密码访问 use auxiliary/scanner/vnc/vnc_none_authRDP远程桌面漏洞 use auxiliary/scanner/rdp/ms12_…

【系统设计】直播架构分析

直播架构 1. 组成 三部分组成, 分别是 客户端(主播端 观众端) 、应用服务器集群 、 CDN 技术 2. 模块间交互方式 主播端 : 直播客户端开启直播间 —— 获取 CDN 推流地址 ——通过 CDN 协议推流到 CDN 服务器上 观众端&#x…

软考信息安全工程师看什么教材?

教材在官网上都有给出具体的版本。 如何复习备考? 首先准备考教材,视频,资料等内容。(可分享) 了解考情: 通过历年真题分析历年考试相关知识内容的考查频度及分值占比,梳理出核心考点内容。第二…

【CANN训练营第三季】Pytorch模型迁移

文章目录第4题第5题首先安装pytorch 参考:https://gitee.com/ascend/pytorch 安到测试这一步,报错,没办法继续了。 RuntimeError: Unsupported soc version: Ascend310 https://gitee.com/ascend/pytorch/issues/I68UDG?fromproject-issue …

微软的AD登录

微软的AD登录最早在1999年出现,,,也就是我们知道的SSO,,具体原理不做过多展开。见官网 AD 官网 安装依赖 "azure/msal-browser": "^2.15.0","azure/msal-react": "^1.0.1",…

XXE - XML外部实体注入攻击

XXE漏洞是什么? xxe(xml External Entity attack),在可以解析XML语言的地方,攻击者提交恶意的XML代码并被执行后,获取服务器中本应被保护的数据。对于XXE漏洞最为关键的部分是DTD文档类型,DTD …

全球 5G RAN 市场呈现强劲增长

根据Future Market Insights的一份新报告,全球对5G RAN的需求预计将在2022年和2032年以28.7%的健康CAGR增长,到2032年达到342亿美元的净值。 5G RAN市场蜂窝设备利用无线电波进行通信。这些设备将用户的语音和移动数据转换成数字化信号,以无…

Vue--》vue3中的计算属性与监视的使用讲解

computed函数与Vue2.x中的computed配置功能一致&#xff0c;只不过在Vue3.x中我们需要按需导入该函数。因为Vue3.x是向下兼容Vue2语法的&#xff0c;所以我们可以写成既有 setup 又有 Vue2中的computed函数&#xff0c;如下代码示例&#xff1a;<template><h1>个人…