目录
- 一、异常
- 1.1 什么是异常
- 1.2 异常机制的作用
- 1.3 常见的异常
- 2.3 异常的分类
- 1. Error
- 2. Exception
- ① 运行时异常
- ② 编译期异常
- 总结:
- 二、异常的处理
- 2.1 抛出异常
- 3.1 抛出异常语法
- 3.2 试图捕获异常
- 3.3 捕获异常与抛出异常的区别
- 1. 抛出异常
- 2.捕获异常
- 三、finally
- 四、throw 异常的产生(创建)
- 四、异常类的API
- 五、自定义异常
- 最后
一、异常
1.1 什么是异常
- 异常(exception),就是程序中出现的错误
- 就好比你在工作的时候,突然家里出了点情况,中断了你工作,这就是出现了异常情况。
1.2 异常机制的作用
- 通过打印出的异常信息,我们可以知道是哪里的代码出现了问题,并大概了解为什么会出现这个问题,方便我们去解决问题
1.3 常见的异常
-
算术异常 ArithmeticException
-
数组下标越界异常 ArrayIndexOutOfBoundsException
-
空指针异常 NullPointerException
-
类转换异常ClassCastException
-
解析异常 ParseException
-
堆栈内存溢出错误 StackOverflowError
2.3 异常的分类
- 异常也是一种类,Throwable 类是 Java 语言中所有错误或异常的超类。
1. Error
- Error 是 Throwable 的子类,用于指示合理的应用程序不应该试图捕获的严重问题。
- 也就是Error一般都是严重问题,遇到必须里面立马解决,所以不应该捕获…
- 例如:堆栈内存溢出错误 StackOverflowError
2. Exception
可以抛出,也可以捕获的异常
Exception子类:RuntimeException(运行时异常)和其他异常类
① 运行时异常
- 这些异常在编译时不强制要求处理,通常是由程序中的错误引起的,例如 NullPointerException、ArrayIndexOutOfBoundsException 等,这类异常可以选择处理,但并非强制要求。
- 这些类是: RuntimeException及其子类的都是运行时异常
② 编译期异常
- 编译期异常是用户错误或问题引起的异常,这些异常在编译时强制要求程序员处理。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
总结:
二、异常的处理
我们知道异常的基本概念了,那异常如何处理呢?
-
异常的处理只有两种方式
- 抛出异常
- 捕获异常
2.1 抛出异常
-
什么是抛出异常?
目前为止任何异常,默认的处理方式都是抛出
所谓抛出异常就是直接将错误信息打印到控制台
3.1 抛出异常语法
- 前面讲到运行时异常,可以不用处理,通过上图也发现系统会默认自动抛出
- 如果是编译期异常,想要抛出异常,语法是
- 位置: 在方法参数列表后,{}前
- 写:throws 异常类名,类名2,…
public static void main(String[] args)
throws ArithmeticException,ArrayIndexOutOfBoundsException {
}
- 声明抛出异常后,什么效果?
- 如果代码一切正常,虽然声明的有抛出异常,也不会在控制台打印异常信息
- 如果代码真的有异常,声明了抛出异常,
- 1错误信息就会在控制台打印
- 2抛出异常后,后续代码不再执行
// 以下代码中19行报错,4,5,6,7.7都不再执行
public static void main(String[] args) {
System.out.println(0.0 );
m1();
System.out.println(7.7 );
}
public static void m1() {
System.out.println(1);
System.out.println(2);
m2();
System.out.println(5);
System.out.println(6);
}
public static void m2(){
System.out.println(3);
System.out.println(4 / 0);
System.out.println(4 );
}
- 但是如果方法抛出的是编译期异常,谁调用这个抛出编译期异常的方法,那么哪个方法就得处理!!
- 特殊的 ,关于抛出异常在重写方法时也有要求…
- 子类的重写后,方法抛出的异常范围要 <= 父类的异常范围
- 特殊的,如果父类方法没有抛出异常,那么子类方法内有异常也不能抛出,只能捕获
总结抛出异常
- 哪里抛出的?
- 方法签名上只是声明可能抛出的异常类型
- 方法体内,执行到某一行代码,如果有错才会抛出
- 抛给谁?
- 如果有异常抛出,谁调用抛给哪个方法
- 最终抛出到main
- main方法抛出到jvm,jvm打印到控制台
- 抛到哪?
- 抛出到控制台
- 抛出了啥?
- 异常的线程信息
- 异常类型
- 异常原因
- 异常位置
3.2 试图捕获异常
试图捕获(try-catch)
- 语法
try{
// 可能会有报错的代码
} catch (异常类名 对象名){
// 如果抓到异常,执行这里
}
执行流程:
- 代码正常运行,执行try后
- 进入try执行,如果try内一切正常,catch就跳过不执行
- 如果try内有异常
- try内异常处后续不执行
- catch捕获到异常后,就会执行
- 无论try内有无异常,try-catch后的代码都能运行
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
try {
System.out.println(3);
System.out.println(4.1);
System.out.println(4/0);
System.out.println(4.2);
}catch (ArithmeticException ae){
System.out.println("捕获到了异常:"+ae );
}
System.out.println(5);
}
- 特殊情况1: 出现的异常和捕获的异常不一致,try-catch失效
- 特殊情况2: 允许同时声明catch多个异常
- 特殊情况3: 如果真需要catch写多个异常,用于去捕获多种异常的话,建议这么写,写一个Exception,直接捕获最大类异常,这样所有异常子类都可以捕获
3.3 捕获异常与抛出异常的区别
1. 抛出异常
通过上图发现程序中断,后续代码无法执行
2.捕获异常
通过上图发现,当try代码块中出现异常后,catch代码块捕获到异常,执行catch中的语句,而且try-catch外面的语句正常执行
三、finally
- finally配合try-catch使用
- 作用: 最终一定会执行,即放在finally里面的代码,之前无论有无报错,无论是抛出还是捕获,fianlly里面的代码最终一定能执行
语法:
try{
// ...
}catch(Exception e){
}finally{
}
或者
try{
// ...
}finally{
}
finally一般用在 关闭流/通道的场景中,用于及时释放资源
例如: IO流,jdbc连接
四、throw 异常的产生(创建)
- 之前说的是抛出异常,捕获异常都是异常出现了之后的解决方案.
如何制造产生异常呢?
产生异常,两个步骤:
- 创建异常对象
- 通过throw 抛出异常对象
- 需要注意,throw抛出的是编译期异常,需要强制处理
- throw抛出的是运行时异常,可以默认不用处理
public class Demo5 {
public static void main(String[] args) throws ParseException {
String s = parseBirthday2("41011120240101123");
System.out.println(s );
}
/**
需求: 解析身份证号中的生日,要求身份证号长度18位,
否则抛出异常
*/
public static String parseBirthday(String idNumber) throws ParseException{
if (idNumber.length() == 18) {
return idNumber.substring(6,14);
}
// 抛出异常(编译期异常,需要处理)
throw new ParseException("生日解析出错,格式不正确",1);
}
public static String parseBirthday2(String idNumber) {
if (idNumber.length() == 18) {
return idNumber.substring(6,14);
}
// 抛出异常(运行时异常,不用管)
throw new RuntimeException("2222生日解析出错,格式不正确");
}
}
- 总结throw和throws…
throw | throws | |
---|---|---|
位置 | 方法体内 | 方法签名上 |
后面写什么 | 跟1个异常对象 | 跟异常类名,且允许多个 |
作用 | 一定会抛出异常的 | 声明抛出的异常类型,但是不一定有异常 |
四、异常类的API
查阅API,发现所有异常,方法基本上都直接继承自Throwable类,常用的方法
- 构造方法 Throwable(String message)
- 所有的异常对象,都通过有参构造将异常信息传递给Throwable父类
- 且这个message就是异常信息
- String getMessage()
- void printStackTrace()
- String toString()
public class Demo6 {
public static void main(String[] args) {
try {
System.out.println(1/0 );
}catch (ArithmeticException ae) {
// String message = ae.getMessage( );
// System.out.println("message = " + message);
//
// String string = ae.toString( );
// System.out.println("string = " + string);
// 获得这些异常信息,将来将其输出到本地磁盘日志文件
ae.printStackTrace();
}
}
}
五、自定义异常
自定义异常实现步骤
- 创建一个异常类,命名规范XxxxException
- 继承一个合适的父类
- 继承编译期异常
- 继承运行时异常
- 设计一个有参构造,将异常信息参数通过super传递给父类异常
如何使用自定义异常类:
- 在需要使用的方法内部,通过throw+该异常对象,将其抛出
// 异常类
public class AgeOutOfBoundsException extends Exception{
public AgeOutOfBoundsException(String msg){
super(msg);
}
}
// 使用异常
public class Dog {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws Exception{
if (age <= 0 || age >= 100) {
throw new AgeOutOfBoundsException("年龄"+age+"越界!");
}
this.age = age;
}
}
最后
如果感觉有收获的话,点个赞 👍🏻 吧。
❤️❤️❤️本人菜鸟修行期,如有错误,欢迎各位大佬评论批评指正!😄😄😄
💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍