认识Java中的异常

认识Java中的异常

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

异常不同于编译错误,语法错误

算数异常:

数组下标越界异常:

空指针异常:

异常其实就是一个一个的类,所有异常都继承了Throwable类,其派生出两个重要的子类, Error 和 Exception

其中Exception又分为两大类,一类是运行时异常/非受查异常,另一类为编译时异常/受查异常。

(1)运行时异常就是代码还没运行就报错了

对于这种异常有一个很明显的特点就是要处理掉异常才能继续运行

(2)编译时异常指的是Java虚拟机无法解决的严重问题

这种情况需要程序员手动去处理错误

总结:

1. Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception

 2. Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表: StackOverflowError和OutOfMemoryError,一旦发生回力乏术。

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

2. 异常的处理

防御式编程

(1)LBYL: Look Before You Leap. 在操作之前就做充分的检查. 即:事前防御型

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

(2)EAFP: It's Easier to Ask Forgiveness than Permission. "事后获取原谅比事前获取许可更容易". 也就是先操 作, 遇到问题再处理. 即:事后认错型

        优势:正常流程和错误流程是分离开的, 程序员更关注正常流程,代码更清晰,容易理解代码 异常处理的核心思想就是 EAFP。

在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws。

异常的抛出

        在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测。 在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者

        程序员可以通过throw来抛出异常,也可以在抛出的异常创建构造方法

      

异常的捕获

        异常的捕获,也就是异常的具体处理方式,主要有两种:异常声明throws 以及 try-catch捕获处理

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

        以克隆为例:

        当加上throws CloneNotSupportedException后,声明当前方法可能会出现异常,但是这里的异常并没有被处理,只是交给了JVM处理,一旦交给JVM处理,程序就立即终止了。(即告诉JVM这里会出现异常)

【注意事项】

1. throws必须跟在方法的参数列表之后

2. 声明的异常必须是 Exception 或者 Exception 的子类

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

4. 调用声明抛出异常的方法时,调用者必须对该异常进行处理(需要用到try-catch),或者继续使用throws抛出。(此处以克隆为例,当方法抛出异常时,在main调用这个方法需要处理该异常,或者也抛出该异常)

try-catch捕获并处理

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

以克隆为例:当方法中存在异常会进入catch部分,执行catch中的语句,当不抛出异常时会继续执行,不会进入catch部分。

进一步理解

当处理完异常,想定位异常的准确位置可以通过printStackTrace()

当想处理两个不同类型的异常时,可以用多个catch()

也可以用分隔符|来进行类型的分割(但分隔符不好确定异常的种类,通常是使用多个catch())

但当有多个异常时,只会打印出一个异常。

不能任何异常都用Exception,Exception是所有异常的父类,此时从上到下过滤的

【注意事项】

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

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

3. try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获。如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误:

4. 可以通过一个catch捕获所有的异常,即多个异常,一次捕获(不推荐)

由于 Exception 类是所有异常类的父类. 因此可以用这个类型表示捕捉所有异常.

finally

        finally用于资源的释放,是否出现异常finally都可以实现断后操作。close()可以写入finally当中(try后面也可以跟小括号,可实现输入)

注意:finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作

关于finally的执行是最后,需要特殊记一下(理论上返回的值是10,但不是立刻返回的,底层将其修改为20)

一般我们不建议在 finally 中写 return (被编译器当做一个警告)

异常的处理流程

        处理异常的顺序:从方法本身来处理异常,到谁调用此方法谁处理异常,最后交给JVM来处理异常

如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理, 程序就会异常终止(和我们最开始未使用 try catch 时是一样的)

【异常处理流程总结】

程序先执行 try 中的代码

如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.

如果找到匹配的异常类型, 就会执行 catch 中的代码

如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.

无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).

如果上层调用者也没有处理的了异常, 就继续向上传递.

一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

3.throw 和 throws 的区别

throw是扔出一个程序,而throws是在方法声明的地方声明一个异常

4. 自定义异常类

Java 中虽然已经内置了丰富的异常类, 但是并不能完全表示实际开发中所遇到的一些异常,此时就需要维护符合我 们实际情况的异常结构.

以用户登录为例

此时我们想自定义用户账户和密码异常,可以定义一个异常类继承​​​​​​​RuntimeException,并且重写

两个构造方法

继承RuntimeException和Exception的不同

继承RuntimeException,异常为编译时异常或者受查异常。

继承Exception,异常为运行时异常或者非受查异常。

解决方法如下

(1)自己用try和catch处理异常,同时注意不要再去main中去解决异常

(2)声明异常

注意事项

自定义异常通常会继承自 Exception 或者 RuntimeException

继承自 Exception 的异常默认是受查异常

继承自 RuntimeException 的异常默认是非受查异常

Read more

JDK 17 + Spring Boot 3.5.8:企业级开发技术栈全景

JDK 17 + Spring Boot 3.5.8:企业级开发技术栈全景

飞鱼系统技术栈全景解析 💡 摘要: 本文深入剖析飞鱼管理系统的完整技术栈架构,涵盖 Spring Boot 3.5.8、Spring Security 6、MyBatis、Redis 等核心技术选型。通过 RuoYi-Vue、RuoYi-Vue3 和飞鱼系统三个版本的详细对比,揭示技术升级背后的决策逻辑。包含 5 个技术选型误区、3 套性能优化方案 (启动速度提升 73%、内存占用降低 75%),以及企业级架构设计经验。适合 Java 后端开发工程师、系统架构师阅读,助你构建高性能、可扩展的企业级应用。 📊 一、技术架构总览 1.1 整体架构图 后端架构 用户层 前端应用 Vue 2.6 + Element UI 网关层

By Ne0inhk
【Java 开发日记】阻塞队列有哪些?拒绝策略有哪些?

【Java 开发日记】阻塞队列有哪些?拒绝策略有哪些?

目录 阻塞队列有哪些? 拒绝策略有哪些? 面试回答 阻塞队列有哪些? 在Java的java.util.concurrent包里面,阻塞队列的实现挺多的,我们可以根据它的功能和结构来记,主要分这么几类: 1. 按容量划分: * 有界队列: 就是队列有固定的容量。 * ArrayBlockingQueue: 最经典的一个,底层是数组,创建时必须指定大小。它的生产和消费用同一把锁,性能相对稳定。 * LinkedBlockingQueue: 底层是链表,它既可以是有界的(构造时指定容量),也可以默认是无界的(默认是Integer.MAX_VALUE,几乎相当于无界)。它的生产和消费用了两把锁,在高并发场景下吞吐量通常比ArrayBlockingQueue更高。 * 无界队列: 理论上是无限的,只要内存够就能一直放。 * PriorityBlockingQueue: 一个支持优先级排序的无界队列。元素必须实现Comparable接口,或者构造时传入Comparator。它出队的顺序是按优先级来的,不是先进先出 * DelayQueue: 一个很特殊的队

By Ne0inhk
【Java Web学习 | 第1篇】前端 - HTML

【Java Web学习 | 第1篇】前端 - HTML

文章目录 * Java Web概览 * HTML核心知识点总结 * 一、HTML基础概念🥝 * 1.1 HTML文档基本结构 * 1.2 HTML标签特点 * 二、常用HTML标签🧾 * 2.1 文本标签 * 2.2 链接与图像 * 综合示例 * 2.3 列表标签 * 2.4 表格标签 * 2.5 表单标签 * 三、HTML5新增特性🤔 * 3.1 语义化标签 * 3.2 媒体标签 * 3.3 其他新增特性 * 四、学习资源推荐🐦‍🔥 Java Web概览 HTML核心知识点总结 一、HTML基础概念🥝 1.1

By Ne0inhk
JavaSE基础-Java字符串转整数与拼接实战指南

JavaSE基础-Java字符串转整数与拼接实战指南

目录 Java 核心知识点:字符串转整数与字符串相加 一、 字符串转整形数字 1.1 精简版(快速上手) 1.2 详细版(机制与陷阱) 1.3 关键陷阱总结表 二、 字符串相加 2.1 精简版(性能核心) 2.2 详细版(编译器优化与陷阱) 2.3 性能选择决策树 💡 一句话总结 本文总结Java中字符串转整数和字符串相加的核心知识点:1. 字符串转整数:推荐使用Integer.parseInt()方法,需注意处理NumberFormatException异常,对带空格的字符串要先trim(),大数值可使用Long.parseLong()或BigInteger。2. 字符串相加:编译期常量可使用+运算符(会被优化),但循环中必须使用StringBuilder以避免性能问题(性能差距可达200倍),多线程场景用StringBuffer,

By Ne0inhk