Java中的异常语法知识居然这么好玩!后悔没有早点学习

学习异常后,发现异常的知识是多么的吸引人!不仅可以用来标记错误,还可以自己定义一个异常,用来实现自己想完成的业务逻辑,接下来一起去学习吧

目录

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

1.异常的概念

2.异常的体系结构

3.异常的分类 

二、怎么处理异常

1.防御式编程

2.异常的抛出

3.异常的捕获

4.异常的处理流程

三、自定义异常

1.自定义异常的用途及注意事项

2. 自定义异常实现登录场景


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

1.异常的概念

(1)什么是异常

所谓异常,就是程序在执行的过程中,发生的不正常行为;也可以认为是代码存在bug

(2)常见的异常

  • 算术异常

也就是计算的过程中发生的异常,如分目不能为0,但是硬要写成0

 public static void main(String[] args) {//算术异常System.out.println(10/0);System.out.println("检验这句话是否被打印");}

  • 数组越界异常

常见的如:对数组越界访问

 public static void main(String[] args) {//数组越界异常int[] arr = {1,2,3,4,5};System.out.println(arr[6]);System.out.println("检验这句话是否被打印");}

  • 空指针异常

空指针异常,并不等于Java中存在指针,而是对空引用的变量进行访问

public static void main(String[] args) {//空指针异常int[] arr = null;//此时arr为一个空引用System.out.println(arr[10]);System.out.println("检验这句话是否被打印");}

(3)异常小总结

  • 当程序发生异常之后,程序就被终止了,后面的代码不会再被执行
  • 发生异常前的代码依旧可以正常执行
 public static void main(String[] args) {//空指针异常System.out.println("在异常发生前,是否会打印这句话");int[] arr = null;//此时arr为一个空引用System.out.println(arr[10]);System.out.println("检验这句话是否被打印");}

2.异常的体系结构

(1)每一个异常都是一个类,它们之间的关系为继承

(2)异常的体系结构

  • Error:指的是Java虚拟机无法解决的严重问题,JVM的内部错误、资源耗尽等,如:栈溢出(StackOverflowError)和内存不足错误(OutOfMemoryError)

下面简单举个例子:无限递归致使栈溢出

 public static void main(String[] args) {func();}public static void func() {func();//无限递归}

3.异常的分类 

这里的异常,也就是指Exception,后面产生的两个子类

(1)编译时异常(Checked Exception)--受查异常

编译时异常就是在编写代码的时候就报的错误,下面举一个克隆异常的例子

这是不支持克隆的异常,怎么做?需要声明异常:鼠标放到异常处,Alt+Enter键即可(要想实现克隆,还需要实现克隆接口)

 (2)运行时异常(RutimeException)--非受查异常

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

如:RunTimeException以及其子类对应的异常,都称为运行时异常

二、怎么处理异常

异常处理主要的5个关键字:throw、try、catch、finaly、throws

1.防御式编程

(1)LBYL(事前防御型)

  • 也就是在走每一步前需要先确认有没有错误发生,A->B->C->D的走法,
  • 模板
boolean ret = false;
ret = 登陆游戏();
if (!ret) {处理登陆游戏错误;return;
}
ret = 开始匹配();
if (!ret) {
处理匹配错误;return;
}
ret = 游戏确认();
if (!ret) {
处理游戏确认错误;return;
}
ret = 选择英雄();
if (!ret) {处理选择英雄错误;return;
}
ret = 载入游戏画面();
if (!ret) {
处理载入游戏错误;return;
}

登录游戏时就确认有没有错误,正确才能进入下一步

  • 缺点:正常流程和错误处理流程代码混在一起, 代码整体显的比较混乱

(2)EAFP(事后认错型)-主推

       EAFP是异常处理的核心思想

  • 先把代码给执行了,再去判断有没有异常
  • 模板
try {登陆游戏();开始匹配();游戏确认();选择英雄();载入游戏画面();...
} catch (登陆游戏异常) {处理登陆游戏异常;
} catch (开始匹配异常) {
处理开始匹配异常;
} catch (游戏确认异常) {
处理游戏确认异常;
} catch (选择英雄异常) {
处理选择英雄异常;
} catch (载入游戏画面异常) {
处理载入游戏画面异常;
}
  • 优点: 正常流程和错误流程是分离开的, 程序员更关注正常流程,代码更清晰,容易理解代码

2.异常的抛出

(1)理解throw

  • 异常的抛出需要借助关键字:throw
  • 格式:
    throw new XXXException("异常产生的原因");

    也就是throw一个异常的对象,给对象的传参可以自己定义

  • 作用:一般用于标记可能发生的异常;当异常发生时,程序员可以快速定位发生异常的地方;一般会用于抛出程序员自定义的异常(需要程序员自己定义某个异常的类)
  • 举例说明:
public static void func(int[] arr) {if(arr == null) {throw new NullPointerException("这是自己抛出的空指针异常");}}public static void main(String[] args) {func(null);}

(2)throw的注意事项

  • throw必须写在方法体内部

  • 抛出的对象必须是Exception 或者 Exception 的子类对象
  • 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
  • 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
  • 异常一旦抛出,其后的代码就不会执行

3.异常的捕获

(1)异常的声明

  • 异常的声明需要用到关键字:throws

异常声明的位置:处在方法声明时参数列表之后,可以同时声明多个异常

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

格式:

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

(2)注意事项

  • throws必须跟在方法的参数列表之后
  • 声明的异常必须是 Exception 或者 Exception 的子类
  • 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。

比如:声明的多个异常类型都是运行时异常的子类,那么可以直接声明运行时异常(RuntimeExcepetion),更极端可以直接声明异常(Exception)

  • 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出

举例:

当被调用的方法(func)声明了异常之后:

说明:当方法后面直接声明Exception时,可能是运行时异常。也有可能是编译使异常;当什么都没有时,会默认是编译时异常,所以就报错了。

解决报错的第一种方法:调用者所在的方法也要声明同样的异常

解决报错的第二种方法:对可能发生异常的代码进行捕获,也就是“异常的捕获”;使用try{}catch对异常捕获:

 

也就是下面的内容

(2)try-catch捕获并处理

【简单语法】

正常的对异常进行声明或者抛出,只是简单的介绍了或者只是处理了编译时异常,而未正在的处理过异常;当异常不处理时,最后会把异常交给JAVM处理,则程序便会终止,不再执行

  • 简单try-catch的语法

简单举例:只要捕获到异常,代码就可以继续往下执行

 public static void func(){int[] arr = null;System.out.println(arr.length);}public static void main(String[] args) {try{func();}catch (NullPointerException e) {System.out.println("处理NullPointerException异常成功!");}System.out.println("处理完异常可以继续走完下面的代码!");}

【注意事项】

  • try块内抛出异常位置之后的代码将不会被执行

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

  • 一个catch可以捕获多个异常(不推荐)

 

  • 可以有多个catch捕获异常(推荐)
 public static void main(String[] args) {try{int[] arr = {1,2,3};System.out.println(arr[10]);}catch (NullPointerException e) {e.printStackTrace();System.out.println("成功捕获到空指针异常");}catch(ArrayIndexOutOfBoundsException e) {e.printStackTrace();System.out.println("捕获到了数组越界异常");} System.out.println("后续代码可被执行!");}

  • 可以通过一个catch捕获所有的异常(不推荐)

  • 如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误

做法:子类必须在父类前面(否在永远不会执行到子类,父类在后可以兜底)

【引入finally】

  • 语法特点:
try{// 可能会发生异常的代码
}catch(异常类型  e) {//对捕获到的异常进行处理
}catch(异常类型  e) {//对捕获到的异常进行处理
}finally {//此处的语句无论是否发生异常,都会被执行到
}

【finally特点】

  • finally中的语句一定会被执行,多用于因为某些情况(如:程序异常退出,文件保存等)没有执行到的代码,可以放在finally中,完成一个兜底的作用
  • 自动完成对资源的关闭

  • 直接在try后面接小括号
public static void main(String[] args) {try(Scanner scanner = new Scanner(System.in)){int a = scanner.nextInt();}catch (NullPointerException e){System.out.println("子类在前");}catch (RuntimeException e) {System.out.println("父类在后");}finally {System.out.println("finally被执行了哦!");}}
  • 输入异常

做法: 捕获异常即可

  • finally与return
 public static int func6() {try(Scanner scanner = new Scanner(System.in)){int a = scanner.nextInt();return a;}catch (NullPointerException e){e.printStackTrace();System.out.println("子类在前");}finally {System.out.println("finally被执行了哦!");return -1;}}public static void main(String[] args) {int ret = func6();System.out.println("接收的返回值"+ret);}

输入:20

总结:finally中的语句一定会被执行,即使前面存在return;存在多个return,最终结果以finally中的为准

4.异常的处理流程

(1)简单三部曲

方法中是否有处理异常(未处理则下一步)--->调用该方法有没有处理异常(未处理则交给JVM)--->JVM最后处理异常,程序则会终止

 public static void main(String[] args) {func7();//调用者也未处理该异常}public static void func7() {try{int[] arr = {1,2,3};System.out.println(arr[5]);//数组越界访问异常}catch (NullPointerException e){e.printStackTrace();System.out.println("子类在前");}finally {System.out.println("finally被执行了哦!");}}

做法:

 public static void main(String[] args) {try {func7();//在调用者处处理异常}catch (ArrayIndexOutOfBoundsException e) {e.printStackTrace();}}public static void func7() {try{int[] arr = {1,2,3};System.out.println(arr[5]);//数组越界访问异常}catch (NullPointerException e){e.printStackTrace();System.out.println("子类在前");}finally {System.out.println("finally被执行了哦!");}}

(2)总结流程

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

三、自定义异常

1.自定义异常的用途及注意事项

(1)多用于一些业务逻辑中可能发生的异常,常常是系统中没有的;如账号登录时,输入的账号错误或者密码错误等等

(2)自定义的异常一般继承RuntimeException或者Exception

2. 自定义异常实现登录场景

(1)先实现一个登录逻辑的类

public class Logic {public String userName = "zhangsan";//设置初始账号名字为:zhangsanpublic String password = "123456";//初始密码为1234456public void  loginInfo(String userName,String password) {//该方法用来验证密码是否正确,参数为//比较用户名if(!this.userName.equals(userName)) {System.out.println("用户名错误!");}//比较密码if(!this.password.equals(password)) {System.out.println("密码错误!");}}
}

以上是账户登录的大概逻辑 

(2)实现自定义异常类 

该类用来:当账户或密码错误时,抛出异常信息并定位错误的行号,利于修改

用户名异常类:

public class UerNameException extends RuntimeException{//用户异常类public UerNameException() {super();}public UerNameException(String message) {super(message);}
}

密码异常类:

public class PassWordException extends RuntimeException{//账号密码错异常类//模拟原码实现两个构造方法public PassWordException() {super();}public PassWordException(String s) {super(s);}
}

(3)完成业务逻辑

为加异常类时:

public class Logic {public String userName = "zhangsan";//设置初始账号名字为:zhangsanpublic String password = "123456";//初始密码为1234456public void  loginInfo(String userName,String password) {//该方法用来验证密码是否正确,参数为//比较用户名if(!this.userName.equals(userName)) {System.out.println("用户名错误!");}//比较密码if(!this.password.equals(password)) {System.out.println("密码错误!");}}public static void main(String[] args) {Logic logic = new Logic();logic.loginInfo("lisi","6666");//调用方法输入账号和密码}
}

注入异常后:

public class Logic {public String userName = "zhangsan";//设置初始账号名字为:zhangsanpublic String password = "123456";//初始密码为1234456public void  loginInfo(String userName,String password) {//该方法用来验证密码是否正确,参数为//比较用户名if(!this.userName.equals(userName)) {//System.out.println("用户名错误!");throw new UerNameException("用户名错误!");//抛出异常}//比较密码if(!this.password.equals(password)) {//System.out.println("密码错误!");throw new PassWordException("密码错误!");//抛出异常}}public static void main(String[] args) {try {Logic logic = new Logic();logic.loginInfo("lisi","6666");//调用方法输入账号和密码}catch (UerNameException e) {e.printStackTrace();}catch (PassWordException e) {e.printStackTrace();}System.out.println("捕获异常后,不影响代码继续往下执行!");}
}

这里用户名错误后,不再判断密码 

测试密码错误:

(4)一些小问题

当自定义异常继承Exception需要添加的细节

 因为Exception默认是受查异常/编译时异常,所以需要加上解决掉报错


本次的内容分享到这里就结束了,小伙伴快去试试吧!

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

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

相关文章

华为OD机试真题-API集群负载统计-2023年OD统一考试(C卷)

题目描述: 某个产品的RESTful API集合部署在服务器集群的多个节点上,近期对客户端访问日志进行了采集,需要统计各个API的访问频次,根据热点信息在服务器节点之间做负载均衡,现在需要实现热点信息统计查询功能。 RESTful API的由多个层级构成,层级之间使用 / 连接,如 /A/…

BUUCTF [HBNIS2018]低个头 1

BUUCTF:https://buuoj.cn/challenges 题目描述: 得到的 flag 请包上 flag{} 提交。来源: https://github.com/hebtuerror404/CTF_competition_warehouse_2018 密文: 下载附件,得到一个.txt文件。 解题思路: 1、低头…

Nginx代理https请求的操作过程

理论很简单,过程很曲折,版本适配的问题要小心。 场景: 要和前端进行联调,我本地后端用了https,证书是自制的,主要是页面里面有一些oauth2认证的地方,需要跳转。 比如https://aaa.com/profile.h…

Kafka(二):在WSL搭建Schema Registry

目录 1 Avro与Schema Registry2 搭建Schema Registry2.1 下载Confluent并解压2.2 设置环境变量2.3 修改配置2.4 启动服务 3 API列表 1 Avro与Schema Registry Apache Avro 是一种高效的数据序列化系统,用于在不同的应用程序和平台之间传输和存储数据。它提供了一种…

处理视频的新工具:UniFab 2.0.0.4 Crack

UniFab这是一个用于处理视频的新工具,可以帮助您像专业人士一样获得结果,事实上,它可以确保在项目的任何设备上完美播放,所以,来认识一下 UniFab - 一款功能强大且方便的视频编辑器和转换器,但另一方面&…

不同品牌的手机可以则哪一个你投屏到电视?

如果你使用AirDroid Cast的TV版,苹果手机可以通过airPlay或无线投屏方式,将屏幕同步到电视屏幕;多个品牌的安卓手机可以通过无线投屏投射到电视。而且无线投屏不限制距离,即使是远程投屏也可以实现。 打开AirDroid Cast的TV版&…

Day32| Leetcode 122. 买卖股票的最佳时机 II Leetcode 55. 跳跃游戏 Leetcode 45. 跳跃游戏 II

Leetcode 122. 买卖股票的最佳时机 II 题目链接 122 买卖股票的最佳时机 II 本题目设计的还是比较巧妙的,把最终的利润分为每天的利润就解决了(贪心),每天的利润就是前一天买进,后一天卖出,转化到代码上就…

AcWing 188:武士风度的牛 ← BFS

【题目来源】https://www.acwing.com/problem/content/190/ 【题目描述】 农民 John 有很多牛,他想交易其中一头被 Don 称为 The Knight 的牛。 这头牛有一个独一无二的超能力,在农场里像 Knight 一样地跳(就是我们熟悉的象棋中马的走法&…

【C++干货铺】优先队列 | 仿函数

个人主页点击直达:小白不是程序媛 C系列专栏:C干货铺 代码仓库:Gitee 目录 优先队列(priority_queue )的介绍和使用 priority_queue的介绍 priority_queue的使用 大堆 小堆 priority_queue的模拟实现 仿…

03_MySQL基本SQL语句讲解

#课程目标 能够创建、删除数据表能够对表里的数据记录进行增加、删除、修改、查询操作能够创建、删除用户能够给用户授权并回收权限了解delete和truncate语句的区别 #一、数据库基本操作 ##1、查看数据库相关信息 mysql> show databases; 查看所有数据库 mysql>…

lv11 嵌入式开发 GPIO实验 9

目录 1 简介 1.1 GPIO 2 LED实验步骤 2.1 通过电路原理图分析LED的控制逻辑 2.2 通过电路原理图查找LED与Exynos4412的连接关系 2.3 通过数据手册分析GPIO中哪些寄存器可以控制LED 2.4 通过程序去操控对应的寄存器完成对LED的控制 2.4.1 使用寄存器写入…

***Ubuntu Linux配置方法

目录 1. 查看防火墙状态 2.先把ssh的权限打开 3.打开防火墙 4.添加规则 5.修复docker缺省打开防火墙问题 6.设置无法ping主机 1. 查看防火墙状态 进入root权限 ufw status verbose 状态:不活动 2.先把ssh的权限打开 这么做的目的是防止把自己关在外面了&am…

LeetCode46. Permutations

文章目录 一、题目二、题解 一、题目 Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order. Example 1: Input: nums [1,2,3] Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] Example …

PHP echo和print 语句

PHP 是通过 print 和 echo 语句来动态输出 HTML 内容,虽然 print 和 echo 语句两者的功能几乎是完全一样,但是还是有一点差别的。 在 PHP 中有两个基本的输出方式: echo 和 print。 本章节中我们会详细讨论两个语句的用法,并在实…

过渡曲线的构造之平面PH曲线

平面PH曲线的构造及其相应性质 平面PH曲线的构造及其相应性质PH曲线理论三次PH曲线的构造及性质四次PH曲线的构造及性质五次PH曲线的构造及性质非尖点五次PH曲线尖点五次PH曲线 参考文献 平面PH曲线的构造及其相应性质 过渡曲线常需要满足在连接点处位置连续、曲率连续以及切线…

Docker exec命令

docker exec :在运行的容器中执行命令。 语法: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]OPTIONS说明: -d:分离模式: 在后台运行 -i:即使没有附加也保持STDIN打开 -t:分配一个伪终…

韵达快递查询,韵达快递单号查询,一键筛选出单号中的退回件

批量查询韵达快递单号的物流信息,并将其中的退回件一键筛选出来。 所需工具: 一个【快递批量查询高手】软件 韵达快递单号若干 操作步骤: 步骤1:运行【快递批量查询高手】软件,第一次使用的朋友记得先注册&#xff…

鸿蒙(HarmonyOS)应用开发——装饰器

简介 ArkTS是HarmonyOS优选的主力应用开发语言。它在TypeScript(简称TS)的基础上,扩展了声明式UI、状态管理等相应的能力,让开发者可以以更简洁、更自然的方式开发高性能应用。TS是JavaScript(简称JS)的超…

MySQL 库操作 | 表操作

文章目录 一.MySQL库的操作1.创建数据库2.字符集和校验规则3.操纵数据库 二.MySQL表的操作1.创建表2.操作表3.删除表 一.MySQL库的操作 1.创建数据库 创建数据库 创建数据库的SQL如下: CREATE DATABASE [IF NOT EXISTS] db_name [[DEFAULT] CHARSETcharset_name…

Redis——使用lua脚本模糊删除数据

前言 本文适用于Redis集群,是否适用单机模式未做测试! 一、创建lua脚本: 命名为clear_data.lua redis.call("select",0)--游标的id local cursor 0 --查找删除的key的数量 local keyNum 0 repeat--使用scan搜索,c…