【JavaSE篇】——异常(一万字让你了解异常的全方位知识)

目录

🎈什么是异常

🚩算术异常

🚩数组越界异常

🚩空指针异常

 🚩输入不匹配异常

🎈异常的体系结构

🎈异常的分类

🚩运行时异常(非受查异常)

🚩编译时异常(受查异常) 

🎈异常的处理

🚩防御式编程

🚩事后认错型 

🌈printStackTrace

🎈异常的捕获

🚩try-catch捕获并处理 

🎈异常的抛出

🚩异常声明throws

🎈finally

🎈异常的处理流程

🎈自定义异常类

🚩使用throw抛出自定义异常

🚩使用try..catch抛出自定义异常


🎈什么是异常

Java中,将程序执行过程中发生的不正常行为称为异常

🚩算术异常

ArithmeticException

public class Abnormal {public static void main(String[] args) {int a=10;int b=0;System.out.println(a/b);}
}Exception in thread "main" java.lang.ArithmeticException: / by zeroat Abnormal.main(Abnormal.java:5)
// 实际上JVM在执行到此处的时候,会new异常对象:new ArithmeticException("/ by zero");// 并且JVM将new的异常对象抛出,打印输出信息到控制台了。

🚩数组越界异常

ArrayIndexOutOfBoundsException

    public static void main(String[] args) {//数组越界异常int[] arr={0,1,2};System.out.println(arr[100]);}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100at Abnormal.main(Abnormal.java:5)

🚩空指针异常

NullPointerException

   public static void main(String[] args) {int[] arr=null;System.out.println(arr.length);}Exception in thread "main" java.lang.NullPointerExceptionat Abnormal.main(Abnormal.java:4)

 🚩输入不匹配异常

InputMismatchException

 

从上述过程中可以看到, java 中不同类型的异常,都有与其对应的类来进行描述
  • ArithmeticException   算术异常
  • ArrayIndexOutOfBoundsException  越界异常
  • NullPointerException   空指针异常
  • InputMismatchException    输入不匹配异常

🎈异常的体系结构

第一个图展示的只是部分的子类异常,第二个图是所有包含的。

从上图中可以看到:
  • 1. Throwable是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception
  • 2. Error指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表: StackOverflowErrorOutOfMemoryError,一旦发生回力乏术。

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

🎈异常的分类

异常可能在编译时发生,也有可能在程序运行时发生,根据发生时机不同,可以分为:

🚩运行时异常(非受查异常)

  • RuntimeException:运行时异常。(非受查异常)(在编写程序阶段程序员可以预先处理,也可以不管,都行。)

 运行时异常都是 RuntimeException 类及其子类异常,如 NullPointerException、IndexOutOfBoundsException 等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般由程序逻辑错误引起,程序应该从逻辑角度尽可能避免这类异常的发生。

 


🚩编译时异常(受查异常) 

  •  Exception的直接子类:编译时异常,(受查异常)(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。)。

编译时异常是指 RuntimeException 以外的异常,类型上都属于 Exception 类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如 IOException、ClassNotFoundException 等以及用户自定义的 Exception 异常,一般情况下不自定义检查异常。

class Person implements Cloneable{@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
public class Abnormal {public static void main(String[] args) {Person person=new Person();Person person1=(Person) person.clone();}
}

我们按住alt加ctrl就显示了。 

class Person implements Cloneable{@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
public class Abnormal {public static void main(String[] args) throws CloneNotSupportedException {Person person=new Person();Person person1=(Person) person.clone();}
}

注意:编译时出现的语法性错误,不能称之为异常。例如将 System.out.println 拼写错了 , 写成了system.out.println. 此时编译过程中就会出错 , 这是 "编译期" 出错。而运行时指的是程序已经编译通过得到 class 文件了 , 再由 JVM 执行过程中出现的错误 .

🎈异常的处理

🚩防御式编程

错误在代码中是客观存在的. 所以要让程序出现问题的时候快速通知程序猿. 

(1)LBYL 在操作之前就做充分的检查

private static int divide() {int a = 0, b = 0;Scanner scanner = new Scanner(System.in);a = scanner.nextInt();b = scanner.nextInt();if (b == 0) {System.out.println("除数为0");return 0;} else {return a / b;}}

缺点:正常流程和错误处理流程代码混在一起, 代码整体条理不清晰。


🚩事后认错型 

(2)EAFP 先操作遇到问题再处理

  • try…catch捕捉异常之后,后续代码可以执行(但是哪里出错误了那么跳到catch捕捉那里错误后面的代码就不执行了)。

处理异常的核心思想就是EAFP 


    public static void main(String[] args) {try {System.out.println(10/0);}catch (NullPointerException e){System.out.println("你这里出现算术异常了");}System.out.println("执行代码");}


异常处理的核心思想就是 EAFP
Java 中, 异常处理主要的 5 个关键字: throw try catch final throws

🌈printStackTrace

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

   public static void main7(String[] args) {try{int[] arr={1,2,3};// arr=null;// System.out.println(arr.length);System.out.println(arr[10]);}catch (NullPointerException|IndexOutOfBoundsException|ArithmeticException e){e.printStackTrace();}System.out.println("执行后续代码");
}

 我们可以 三者用|来拼接,然后调用printStackTrace方法进行检查哪一行出错误。


🎈异常的捕获

🚩try-catch捕获并处理 

我们看到上述俩者报错的方式,第一种我们try catch后续代码执行,第二种直接交给JVM处理直接终止程序。

有些同学问:一次抛出俩个或者多个异常怎么办?

—————这个是错误的,不会同时抛出俩个及以上的异常。

try…catch捕捉异常之后,后续代码可以执行(但是哪里出错误了那么跳到catch捕捉那里错误后面的代码就不执行了)。

 1.try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获

    public static void main7(String[] args) {try{int[] arr={1,2,3};System.out.println(10/0);//算法异常arr=null;//空指针异常System.out.println(arr[10]);//越界异常} catch (NullPointerException e){e.printStackTrace();System.out.println("空指针异常");}catch (IndexOutOfBoundsException e){e.printStackTrace();System.out.println("栈溢出异常");}catch (ArithmeticException e){e.printStackTrace();System.out.println("你这里出现算法异常");}System.out.println("执行后续代码");
}

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

我们可以看到执行19行之后直接跳到catch()算术异常那去了。所以20行的空指针异常和21行的下标越界异常都没有执行直接跳过到catch捕捉了,所以我们不会出现俩个或者多个异常。

3. 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到 JVM收到后中断程序----异常是按照类型来捕获的
public static void main(String[] args) {
try {
int[] array = {1,2,3};
System.out.println(array[3]); // 此处会抛出数组越界异常
}catch (NullPointerException e){ // 捕获时候捕获的是空指针异常--真正的异常无法被捕获到
e.printStackTrace();
}
System.out.println("后序代码");
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at day20210917.ArrayOperator.main(ArrayOperator.java:24)
4.异常之间具有父子关系

5.一定是子类异常在前catch,父类异常在后catch,否则语法错误:

 

此时Exception是所有类的父类,那么 现在都交给他了,后面子类就没有意义了。所以我们需要让子类放在前面,如果子类没有,后面有个父类保底的。


🎈异常的抛出

在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测。
Java 中,可以借助 throw 关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:
throw new XXXException("异常产生的原因");
public class Abnormal {public static void test(int a){if(a==10){throw new NullPointerException();}}public static void main(String[] args) {test(10);}
}


还有一种是try..catch捕获

public class Abnormal {public static void test(int a){if(a==10){throw new NullPointerException();}}public static void main(String[] args) {try{test(10);}catch (NullPointerException e){e.printStackTrace();System.out.println("你这里出现异常了");}}
}


  • 1. throw必须写在方法体内部
  • 2. 抛出的对象必须是Exception 或者 Exception 的子类对象

受查异常就是编译异常,默认是编译异常。

  • 3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理

  • 4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译

🚩异常声明throws

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

语法格式:
修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}public static void test3(int a) throws CloneNotSupportedException,OutOfMemoryError,StackOverflowError 

 

注意事项
1. throws必须跟在方法的参数列表之后
2. 声明的异常必须是 Exception 或者 Exception 的子类
3. 方法内部如果抛出了多个异常,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(){}
}

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


🎈finally

在写程序时, 有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源 :网络连接、数据库 连接、IO 流等, 在程序正常或者异常退出时,必须要对资源进进行回收 。另外,因为 异常会引发程序的跳转,可能 导致有些语句执行不到 finally 就是用来解决这个问题的。
语法格式:
try{
// 可能会发生异常的代码
}catch(异常类型 e){
// 对捕获到的异常进行处理
}finally{
// 此处的语句无论是否发生异常,都会被执行到
}
// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行

就拿scanner来做比较吧,我们System.in进行输入,然后scanner需要一个scanner.close()进行关闭输入。

    public static void main(String[] args) {Scanner scanner=new Scanner(System.in);try {int n=scanner.nextInt();}catch (InputMismatchException e){e.printStackTrace();System.out.println("输入的内容和类型不匹配");}finally {System.out.println("执行了finally代码,finally一般是用来关于资源的");scanner.close();}System.out.println("如果没有抛出异常,或者异常被处理了,try-catch后的代码也会执行");}

如果不想写scanner.close()我们需要再try里面声明。

    public static void main(String[] args) {try( Scanner scanner=new Scanner(System.in)) {int n=scanner.nextInt();}catch (InputMismatchException e){e.printStackTrace();System.out.println("输入的内容和类型不匹配");}finally {System.out.println("执行了finally代码,finally一般是用来关于资源的");//  scanner.close();}System.out.println("如果没有抛出异常,或者异常被处理了,try-catch后的代码也会执行");}


问题:既然 finally 和 try-catch-finally 后的代码都会执行,那为什么还要有finally呢?
public static  int getDate(){Scanner sc=null;try {sc=new Scanner(System.in);int n=sc.nextInt();return n;}catch (InputMismatchException e){e.printStackTrace();}finally {System.out.println("执行了finally");}System.out.println("try-catch-finally后面的代码");if(sc!=null){sc.close();}return 0;}public static void main(String[] args) {int date=getDate();System.out.println(date);}

此时我们可以证明肯定是会执行finally的代码。所以我们给sc.close()放到finally。上述程序,如果正常输入,成功接收输入后程序就返回了,try-catch-finally之后的代码根本就没有执行,即输入流就没有被释放,造成资源泄漏。所以给sc.close()写到finally中。


注意: finally 中的代码一定会执行的,一般在 finally 中进行一些资源清理的扫尾工作
// 下面程序输出什么?
public static void main(String[] args) {System.out.println(func());
}
public static int func() {
try {return 10;
} finally {return 20;}
}
A: 10 B: 20 C: 30 D: 编译失败

我们上面知道肯定会执行finally,所以结果是20.

finally 执行的时机是在方法返回之前 (try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果finally 中也存在 return 语句 , 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.
一般我们不建议在 finally 中写 return ( 被编译器当做一个警告 )

🎈异常的处理流程

1.如果本方法中没有合适的处理异常的方式, 就会沿着调用栈向上传递
    public static void func1() {int[] arr = {1, 2, 3};System.out.println(arr[100]);}public static void main(String[] args) {try {func1();} catch (ArrayIndexOutOfBoundsException e) {e.printStackTrace();}System.out.println("after try catch");}

2.如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理, 程序就会异常终止(和我们最开始未使用 try catch 时是一样的).
   public static void func2() {int[] arr = {1, 2, 3};System.out.println(arr[100]);}public static void main(String[] args) {func();System.out.println("after try catch");}


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

🎈自定义异常类

Java 中虽然已经内置了丰富的异常类 , 但是并不能完全表示实际开发中所遇到的一些异常,此时就需要维护符合我们实际情况的异常结构.
例如 , 我们实现一个用户登陆功能 .
class Login{private String name="chenle";private int age=20;public  void loginInfo(String name, int age){if(!this.name.equals(name)){System.out.println("用户名错误");return;}if(this.age!=age){System.out.println("年龄错误");return;}}
}
public class Abnormal {public static void main(String[] args) {Login login=new Login();login.loginInfo("chenle1",201);}
}

我们代码输出“用户名错误”。我们如果用异常来写呢?因为再异常中没有密码错误异常和年龄错误异常,所以这时候我们需要自定义异常。

我们对照空指针异常的源代码继承的是RuntimeException,里面有一段定义和构造。

1. 自定义异常类,然后继承自Exception 或者 RunTimeException
2. 实现一个带有String类型参数的构造方法,参数含义:出现异常的原因

我们创建一个类叫 UserNameErrorException和ageErrorException继承运行异常类,然后写上俩个构成重载的构造方法。

public class UserNameErrorException extends RuntimeException{public UserNameErrorException() {super();}public UserNameErrorException(String s){super(s);}
}public class ageErrorException extends RuntimeException{public ageErrorException() {super();}public ageErrorException(String s){super(s);}
}

🚩使用throw抛出自定义异常

class Login{private String name="chenle";private int age=20;public  void loginInfo(String name, int age){if(!this.name.equals(name)){throw new UserNameErrorException("用户名错误");}if(this.age!=age){throw new ageErrorException("年龄错误");}}
}
public class Abnormal {public static void main(String[] args) {Login login=new Login();login.loginInfo("chenle1",201);}
}

由于上述俩个是个单独的执行,再实际的开发中,我们需要将俩者都放在一起,这时候用到的是try..catch。


🚩使用try..catch抛出自定义异常

class Login{private String name="chenle";private int age=20;public  void loginInfo(String name, int age){try{if(!this.name.equals(name)){throw new UserNameErrorException("用户名错误");}if(this.age!=age){throw new ageErrorException("年龄错误");}System.out.println("登录成功");}catch (UserNameErrorException e){e.printStackTrace();}catch (ageErrorException e){e.printStackTrace();}}
}
public class Abnormal {public static void main(String[] args) {Login login=new Login();login.loginInfo("chenle1",201);}
}


注意事项
  • 自定义异常通常会继承自 Exception 或者 RuntimeException
  • 继承自 Exception 的异常默认是受查异常
  • 继承自 RuntimeException 的异常默认是非受查异常

 新的开始了。

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

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

相关文章

什么样的分类器才是最好的

我们总想要一个最好的算法,分类也同样如此 但对某一个任务,某个具体的分类器不可能同时满足或提高所有上面介绍的指标。 如果一个分类器能正确分对所有的实例,那么各项指标都已经达到最优,但这样的分类器往往不存在。比如之前说…

数据结构第十六天(二叉树层序遍历/广度优先搜索(BFS)/队列使用)

目录 前言 概述 接口 源码 测试函数 运行结果 往期精彩内容 前言 从前的日色变得慢,车,马,邮件都慢,一生,只够爱一个人。 概述 二叉树的层序遍历可以使用广度优先搜索(BFS)来实现。具体步骤如下&…

循环、数组、match

for循环 循环:周而复始 For(临时变量;循环条件;腰间变更){ 循环体 } For循环可以嵌套 while循环 声明变量 While(条件){ 循环体 变量的变化} do while循环 do{ 执行语句; …

自然语言编程系列(二):自然语言处理(NLP)、编程语言处理(PPL)和GitHub Copilot X

编程语言处理的核心是计算机如何理解和执行预定义的人工语言(编程语言),而自然语言处理则是研究如何使计算机理解并生成非正式、多样化的自然语言。GPT-4.0作为自然语言处理技术的最新迭代,其编程语言处理能力相较于前代模型有了显…

ChatGPT高效提问—prompt实践(文案助手)

ChatGPT高效提问—prompt实践(文案助手) 下面我们再赋予它一个角色——专业的文案助手。 1.1 广告文案撰写 ​ 假设我们正在为一款新的环保型运动鞋撰写广告文案,可以使用文案助手生成有趣且引人入胜的广告内容,如下所示。 ​…

001kafka源码项目gradle报错UnsupportedClassVersionError-kafka-报错-大数据学习

1 报错提示 java.lang.UnsupportedClassVersionError: org/eclipse/jgit/lib/AnyObjectId has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0 如…

C++面向对象程序设计-北京大学-郭炜【课程笔记(二)】

C面向对象程序设计-北京大学-郭炜【课程笔记(二)】 1、结构化程序设计结构化程序设计的不足 2、面向对象的程序设计2.1、面向对象的程序设计2.2、从客观事物抽象出类2.3、对象的内存分配2.4、对象之间的运算2.5、使用类的成员变量和成员函数用法1&#x…

【开源】SpringBoot框架开发创意工坊双创管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员端2.2 Web 端2.3 移动端 三、系统展示四、核心代码4.1 查询项目4.2 移动端新增团队4.3 查询讲座4.4 讲座收藏4.5 小程序登录 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的创意工坊双创管理…

使用redis-insight连接到服务器上的redis数据库

一、安装redis: 安装 Redis。你可以通过运行下面的命令来使用 yum 安装 Redis: sudo yum install redis 启动 Redis 服务。安装完成后,你可以通过运行下面的命令来启动 Redis 服务: sudo systemctl start redis 设置 Redis 服务…

【测试】JUnit

目 录 一.注解二.断言三.用例的执行顺序四.参数化五.测试套件 自动化就是 selenium 脚本来实现的 junit 是 java 的单亓测试工具&#xff0c;只不过我们在实现自动化的时候需要借用一下下 junit 库里面提供的一些方法 引入依赖 Junit 5 <!-- https://mvnrepository.com/a…

事务管理 及 AOP

一、事务管理 1.1 事务回顾 1.2 Spring事务管理 1.3 事务进阶 1.3.1 rollbackfor 1.3.2 propagation 控制台日志过滤插件&#xff1a; 查看事务管理日志是JdbcTrsactionManager类&#xff1a; 在控制台找到JdbcTrsactionManager——右击——add highlight——红色——所有事…

【教学类-19-11】20240214《ABAB式-规律黏贴18格-手工纸15*15CM-复制空表 有边框》(中班)

背景需求 利用15*15CM手工纸制作AB色块手环&#xff08;手工纸自带色彩&#xff09;&#xff0c;一页36格子&#xff0c;空的&#xff0c;本代码就是复制有边框的模板&#xff0c;5份 素材准备 用常用的方法word转PDF&#xff0c;太慢了 改用import shutil复制 代码展示 # #…