Java中的异常处理

目录

前言:

异常简介: 

Error类:

Exception类:

Exception异常:

运行异常: 

编译异常:

throw和throws关键字: 

throw:

throws:

try-catch关键字:

finally:

为啥叫受查异常?

throw和throws的区别:

总结:


前言:

        应该都听说过Java中的异常处理,其实不止Java中有异常处理,我们学过的其他语言中的报错是不是也是异常呢?对,是,但是Java中的异常为什么要学习?因为它可以当程序真的报错而崩溃时,我们进行异常处理可以使其跳过,程序还能正常的往下执行。

        像C语言,如果程序报错就会崩溃,而Java可以得知这个异常,可以继续正常运行。

异常简介: 

        异常就是异于常态,和正常情况不一样,有错误。在Java中,阻止当前方法或作用域的情况,称之为异常。

        Java中所有异常类都继承于Throwable类,对,你没听错,这些异常都是类。

        Java中,所有异常都有一个公共祖先Throwable(可抛出)。Throwable指定代码中可用传播机制通过Java应用程序传输的任何问题的共性。

        Throwable主要包括两个大类,一个是Error类,另一个是Exception类。

        注意,异常和错误是两种东西。不是同一概念。

Error类:

        其中Error类中包括虚拟机错误和线程死锁,一旦Error出现了,程序就彻底的挂了,被称为程序终结者。

Exception类:

        也就是通常所说的“异常”。主要指编码、环境、用户操作输入出现问题,Exception主要包括两大类,非检查异常(RuntimeException)和检查异常(其他的一些异常)。 

        因为Error类出现,程序就会挂掉,所以讲的意义不大。这一篇我们就来重点了解Exception类这个异常。 

Exception异常:

        Java中异常是分种类的,异常分为两大类:运行异常和编译异常(下图只是简单分类)。

        比如算数异常: 

        当发生异常时,不会再执行异常后面的代码。 

        比如空指针异常和数组越界异常:

         此时我们来观察运行异常和编译异常的区别,比如我们利用递归来观察:

public static void func() {func();
}
public static void main(String[] args) {func();
}

         执行上面的代码会执行,之后报错。

         Error指的是Java虚拟机无法解决的严重问题,比如JVM的内部错误、资源耗尽等,比较典型的是:StackOverflowError 和 OutOfMemoryError(上面的死递归就是栈溢出)。

        Exception:异常产生后程序员可以通过代码进行处理,是程序继续执行。

运行异常: 

        异常分为运行时异常和编译时异常,我们刚才说的空指针异常,算术异常,数组下标越界访问异常都是运行异常。运行异常又称非受查异常。这个名字我们一会了解。

        注意此时程序至少运行了。

编译异常:

        那么接下来我们就来看看编译异常是什么。还记不记得我们之前讲的克隆方法,声明克隆接口表示能被克隆,要通过向下转型,还有要扔出异常才能被克隆。

class Person implements Cloneable{public String name;public Person(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException {Person person1 = new Person("zhansan");Person person2 = (Person) person1.clone();System.out.println(person2);}
}

        是否还记得我们当时一直出现红色标志,我们进行了向下转型,重写了该方法并且使用了Cloneable接口,但是还是不能运行,必须抛出异常才可以运行。其实这里就发生了编译异常。

         编译异常在编译期间一定要处理,否则代码不能编译通过。

        那么我们有时候写出的会标红,这叫做语法错误。

throw和throws关键字: 

throw:

         如何让程序抛出异常?Java中抛出异常通常是使用throw和throws关键字来实现的。

        throw是将产生的异常抛出,是一个抛出异常的动作(一般是指定的异常)。

int a = 10;
if (a == 10) {throw new NullPointerException("hahaha");
}

        通过throw来手动抛出异常。 我们之前的算数等等异常是JVM抛出的,因为我们没有处理。

        throw关键字一般抛出我们自定义的异常。 

throws:

        throws一般使用在方法之后。

         告诉方法调用者,调用这个方法可能会抛出这个异常。也就是说,如果在方法中有这个编译时异常(受查异常),那么这个编译时异常一定要进行处理。目前我们的处理方式是在方法定义的时候,通过throws关键字声明异常。最后这个异常是交给JVM处理的。

        throws必须跟在方法的参数列表之后,声明异常必须是Exception 或者Exception 的子类。

        如果方法内存抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开;如果抛出的异常类型有父子关系,直接声明父类即可。

//public void OpenConfig(String filename) throws IOException, FileNotFoundException{
//FileNotFoundException 继承于 IOException
public void OpenConfig(String filename) throws IOException{}//这两种方式是一样的

        此时我们再来看一个代码: 

public static void func() throws CloneNotSupportedException{int a = 10;if (a == 10) {throw new CloneNotSupportedException("hahaha");}
}public static void main(String[] args) throws CloneNotSupportedException {func();//因为 func 方法的声明里面有编译异常处理,所以主方法也要写
}

        但此时我们确实抛出异常了,但是程序还是崩溃了,那么异常实际上没有被程序员处理,实际上还是交给了JVM处理。

try-catch关键字:

        程序员要解决这些异常,就需要使用这些语句:

public static void func() throws CloneNotSupportedException{int a = 10;if (a == 10) {throw new CloneNotSupportedException("hahaha");}
}public static void main(String[] args) throws CloneNotSupportedException {try {func();} catch (CloneNotSupportedException e) {throw new RuntimeException(e);}
}

        try语句中,可能会有异常发生,我们写这些语句是,是可以预测会发生那些异常的(身为程序员,我不信你看不出来),那么catch语句中,就是捕获这些可能发生的异常。 如果捕获到了就执行catch当中的内容。

        如果我们预测失败,就是catch中没有捕获到发生的异常,还是会交给JVM处理,程序崩溃。 

        我们可以通过catch捕获多个异常(就是多写几个catch语句): 

try {System.out.println(10/0);
} catch (ArithmeticException e) {System.out.println("我来处理 ArithmeticException 了");
} catch (NullPointerException e) {System.out.println("我来处理 NullPointerException 了");
}

        这里处理的异常都是在try语句中的。我们也可以通过 | 连接多个异常,充当“或”。 

/*try {System.out.println(10/0);
} catch (ArithmeticException e) {System.out.println("我来处理 ArithmeticException 了");
} catch (NullPointerException e) {System.out.println("我来处理 NullPointerException 了");
}*///以下方式等同上面,但是少用
try {System.out.println(10/0);
} catch (ArithmeticException | NullPointerException e) {System.out.println("我来处理 ArithmeticException 了");
}

        也就是说,在try里面发生的异常,就不会执行里面的代码了。 

        虽然可以捕获多种类型异常,但是同一时刻只能抛出一个异常。

        我们不能先捕获所有父类异常之后捕获子类异常,否则出错。

        但是可以先捕获子类异常,之后捕获父类异常,此时就充当了垫后的角色。

        还有就是,那几个运行异常的类型最好记住,如果我们使用了try-catch语句,就一定要知道可能会出现哪些异常。当然,我们知道所有异常都继承Exception类,刚才说可以使用父类来指代。但是最好不要使用父类,否则这样就失去了意义。

finally:

        你猜它为啥叫finally?我们先看代码:

public static int func() {try {int[] array = null;System.out.println(array.length);} catch (NullPointerException e) {System.out.println("捕获到了一个空指针异常");} finally {System.out.println("这里执行了finally");}return 10;
}
public static void main(String[] args) {System.out.println(func());
}

        可以看到, finally最后无论如何都会被执行。

        这里面会有很多抗,也就是说我们必须知道执行顺序,比如以下代码结果:

        因为try里面直接返回了,但是finally还是被执行了。那么既然如此,我们就不能乱返回了:         因为可能不止一条语句执行,所以返回值不能有多个。

        执行顺序:try中的语句正常,就先执行finally,之后执行try;try中的语句有异常,就先执行catch里面捕获到的异常语句,之后执行finally。

        所以try里面没有异常,要先执行finally:

public static int func1() {try {return 10;} finally {return 100;}
}
public static void main(String[] args) {System.out.println(func1());
}

        这个结果为100,此时直接执行finally,并不在执行try里面的代码。 

        我们先来执行一个异常代码:

        此时func2里面没有处理这个异常,main方法中处理了这个异常,那么这个func2方法就最好声明一下,让使用者知道会有异常并处理。 

public static void func2() throws ArrayIndexOutOfBoundsException{//声明会有这个异常让使用者处理int[] arr = {1,2,3};System.out.println(arr[100]);
}
public static void main(String[] args) {//此时因为是 main 方法调用,所以要在 main 方法中处理//使用 try-catchtry {func2();} catch (ArrayIndexOutOfBoundsException e) {} finally {}}

         所以调用的方法中也没有处理,最终就会交给JVM来处理。

为啥叫受查异常?

        为了更好的理解,我们来举一个例子,我们来自定义异常,因为异常本来就是类,所以我们利用继承来自己定义异常。

        我们可以看到,像ArrayIndexOutOfBoundsException这个异常是继承于IndexOutOfBoundsException的,所以我们自己写异常时,就需要用到继承于Exception中的类来写。

        此时我们定义一个LogIn类,并在里面对比用户名和密码,如果对照错误,则抛出自己定义的异常。 

public class LogIn {private String userName = "admin";private String password = "123455";private static void loginInfo (String userName, String password) {if (!userName.equals(userName)) {//此时用户名输入错误,就需要报异常//此时抛出自定义异常throw new UserNameException("用户名有问题");}if (!password.equals((password))) {//此时密码错误throw new PasswordException("密码有问题");}System.out.println("登陆成功");}public static void main(String[] args) {loginInfo("admin","123456");}
}//extends Exception          编译时异常
//extends RuntinmeException  运行时异常class UserNameException extends Exception {//自定义异常一定要继承于异常//此时为编译时异常public UserNameException (String message) {super(message);}
}class PasswordException extends Exception {public PasswordException (String message) {super(message);}
}

         此时报红是因为这是一个受查异常,所以需要通过try-catch包起来。但是我们之前讲过,可以通过调用它的函数try-catch,有异常的函数只需要通过声明可能有异常即可。

public static void main(String[] args) {//此时通过throws已经声明了异常//调用方法里面需要通过 try-catch 来解决try {loginInfo("admin","123456");} catch (UserNameException e) {} catch (PasswordException e) {} finally {}}
private static void loginInfo (String userName, String password) throws UserNameException,PasswordException{if (!userName.equals(userName)) {//此时用户名输入错误,就需要报异常//此时抛出自定义异常throw new UserNameException("用户名有问题");}if (!password.equals((password))) {//此时密码错误throw new PasswordException("密码有问题");}System.out.println("登陆成功");
}

        因为这是受查异常,因为我们继承了编译异常的类,所以必须解决。所以运行异常(非受查异常)就不用解决了。

        注:因为空指针异常,算数异常不是编译异常,所以你在主方法后面不加上throws也可以通过,由JVM来帮你处理。

        我们见过一个最典型的受查异常(编译异常)就是克隆方法了,我们当时主方法调用克隆方法,这个主方法就必须使用throws关键字,否则无法完成编译。

throw和throws的区别:

        1、throws出现在方法函数头;而throw出现在函数体。
        2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
        3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

总结:

        多去刷题,多去使用,就会领悟。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

编译 FastDFS 时报错 fatal error: sf/sf_global.h: No such file or directory 解决办法

编译 FastDFS 时,报错如下 gcc -Wall -D_FILE_OFFSET_BITS64 -D_GNU_SOURCE -g -O1 -DDEBUG_FLAG -c -o ../common/fdfs_global.o ../common/fdfs_global.c -I../common -I/usr/local/include In file included from ../common/fdfs_global.c:21:0: ../common/fdf…

教你用五步让千年的兵马俑跳上现代的科目三?

以下是一张我上月去西安拍的兵马俑照片: 使用通义千问,5步就能它舞动起来,跳上现在流行的“科目三”舞蹈。 千年兵马俑跳上科目三 全民舞王 第1步 打开通义千问App,我使用的是华为手机,苹果版的没试; 在…

OpenCV-22高斯滤波

一、高斯函数的基础 要理解高斯滤波首先要直到什么是高斯函数,高斯函数是符合高斯分布的(也叫正态分布)的数据的概率密度函数。 高斯函数的特点是以x轴某一点(这一点称为均值)为对称轴,越靠近中心数据发生…

单例模式实现最好的方式即枚举实现

单例类作为23种设计模式当中最常用的设计模式,实现方式有很多种,比较流行的是DCL(DoubleCheckLock)双重检查的实现,线程安全,又比较好,除了存在序列化的问题之外,还算不错,如果对DCL模式还不熟悉…

U-Boot学习(3):.config、defconfig文件对比及图形化配置Kconfig

在上一节中,我们介绍了U-Boot编译和.config配置文件生成分析,我们可以通过make xxx__defconfig来进行一些配置,其中xxx__defconfig对应config目录下的基于不同开发板的一些配置,指令执行完后会根据对应的配置在根目录下生成一个.c…

排序算法6---快速排序(非递归)(C)

回顾递归的快速排序,都是先找到key中间值,然后递归左区间,右区间。 那么是否可以实现非递归的快排呢?答案是对的,这里需要借助数据结构的栈。将右区间左区间压栈(后进先出),然后取出…

详细讲解Python连接Mysql的基本操作

目录 前言1. mysql.connector2. pymysql 前言 连接Mysql一般有几种方法,主要讲解mysql.connector以及pymysql的连接 后续如果用到其他库还会持续总结! 对于数据库中的表格,本人设计如下:(为了配合下面的操作) 1. mysql.connector mysql.connector 是一…

C#,入门教程(19)——循环语句(for,while,foreach)的基础知识

上一篇: C#,入门教程(18)——分支语句(switch-case)的基础知识https://blog.csdn.net/beijinghorn/article/details/124039953 一、for循环 当老师进入教室,从门口开始分别按行、列点名,看看哪位翘课&…

Xcode15 升级问题记录

这里写自定义目录标题 新版本Xcode15升级问题1:rsync error: some files could not be transferred (code 23) at ...参考 新版本Xcode15升级 下载地址:https://developer.apple.com/download/all/ 我目前使用的版本是Xcode15.2 我新创建了一个项目&…

transfomer中Decoder和Encoder的base_layer的源码实现

简介 Encoder和Decoder共同组成transfomer,分别对应图中左右浅绿色框内的部分. Encoder: 目的:将输入的特征图转换为一系列自注意力的输出。 工作原理:首先,通过卷积神经网络(CNN)提取输入图像的特征。然…

开发需求总结9-el-tree获取选中节点,节点全选时返回被全选子级的父节点,未全选则返回被选中的节点

目录 需求描述 代码实现: 需求描述 需要获取树组件选中的节点,假如父节点被选中(该节点全选),即只返回父节点的数据,如父节点未被全选,则正常返回被选中节点的数据。 示例一: 如上图…

Python展示 RGB立方体的二维切面视图

代码实现 import numpy as np import matplotlib.pyplot as plt# 生成 24-bit 全彩 RGB 立方体 def generate_rgb_cube():# 初始化一个 256x256x256 的三维数组rgb_cube np.zeros((256, 256, 256, 3), dtypenp.uint8)# 填充立方体for r in range(256):for g in range(256):fo…

编曲混音FL Studio21.2对电脑有什么配置要求

FL Studio 21是一款非常流行的音乐制作软件,它可以帮助音乐人和制作人创作出高质量的音乐作品。然而,为了保证软件的稳定性和流畅性,用户需要知道FL Studio 21对电脑的配置要求。本文将介绍FL Studio 21的配置要求,以帮助用户选择…

32 二叉树的定义

之前的通用树结构 采用双亲孩子表示法模型 孩子兄弟表示法模型 引出二叉树 二叉树的定义: 满二叉树和完全二叉树 对此图要有印象 满二叉树一定是完全二叉树,但是完全二叉树不一定是满二叉树 小结

RabbitMQ交换机(2)-Direct

1.Direct 直连(路由)交换机,生产者将消息发送到交换机,并指定消息的Routing Key(路由键)。交换机会将Routing Key与队列绑定进行匹配,如果匹配成功,则将该消息路由到对应的队列中。如果没有匹配成功,该消息…

小程序中使用微信同声传译插件实现语音识别、语音合成、文本翻译功能----语音识别(一)

官方文档链接:https://mp.weixin.qq.com/wxopen/plugindevdoc?appidwx069ba97219f66d99&token370941954&langzh_CN#- 要使用插件需要先在小程序管理后台的设置->第三方设置->插件管理中添加插件,目前该插件仅认证后的小程序。 语音识别…

JS | JS调用EXE

JS | JS调用EXE 网上洋洋洒洒一大堆文章提供,然我还是没找打合适的方案: 注册表方案做了如下测试(可行但是不推荐?): 先,键入文件名为 myprotocal.reg 的注册表,并键入一下信息: Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\openExe] //协议名…

Redis相关命令详解及其原理

Redis概念 Redis,英文全称是remote dictionary service,也就是远程字典服务。这是kv存储数据库。Redis,包括所有的数据库,都是请求-回应模式,通俗来说就是数据库不会主动地要给前台推送数据,只有前台发送了…

MySQL/Oracle 的 字符串拼接

目录 MySQL、Oracle 的 字符串拼接1、MySQL 的字符串拼接1.1 CONCAT(str1,str2,...) : 可以拼接多个字符串1.2 CONCAT_WS(separator,str1,str2,...) : 指定分隔符拼接多个字符串1.3 GROUP_CONCAT(expr) : 聚合函数,用于将多行的值连接成一个字符串。 2、Oracle 的字…

广州市生物医药及高端医疗器械产业链大会暨联盟会员大会召开,天空卫士数据安全备受关注

12月20日,广州市生物医药及高端医疗器械产业链大会暨联盟会员大会在广州举办。在本次会议上,作为大会唯一受邀参加主题分享的技术供应商,天空卫士南区技术总监黄军发表《生物制药企业如何保护数据安全》的主题演讲。 做好承上启下“连心桥”…