网站首页 > java教程 正文
“这里是云端源想IT,帮你轻松学IT”
嗨~ 今天的你过得还好吗?
别让别人的想法左右你的情绪
你的人生由你自己掌控
- 2023.09.08 -
Java中的异常处理机制可以帮助程序在出现异常情况时进行错误处理,保证程序的稳定性和健壮性。今天我们来详细讲解一下Java中的异常,帮助大家了解异常产生的原因以及如何处理异常。
一、异常的概念
在Java中,异常(Exception)是指在程序运行过程中发生的不可预期的错误或意外情况,它可以是由代码引起的,也可以是由系统环境或其他程序引起的。异常可以是系统异常(如数组下标越界)或程序异常(如类不存在异常)。
当Java程序运行过程中发生异常时,程序会抛出一个异常对象,然后在程序中定义了异常处理程序的地方执行异常处理程序,从而使程序能够更加健壮和可靠。
在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行。
Java语言中异常是以什么形式存在的呢?
异常在java中以类的形式存在,每一个异常类都可以创建异常对象。
例如:
public class ExceptionTest02 {
public static void main(String[] args) {
// 通过“异常类”实例化“异常对象”
NumberFormatException nfe = new NumberFormatException("数字格式化异常!");
// java.lang.NumberFormatException: 数字格式化异常!
System.out.println(nfe);
}
}
二、异常体系
异常机制其实是帮助我们找到程序中的问题,异常的根类是 java.lang.Throwable ,其下有两个子类:java.lang.Error 与 java.lang.Exception ,平常所说的异常指 java.lang.Exception 。
Throwable体系:
Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒、阑尾炎。
Throwable中的常用方法:
- public void printStackTrace() :打印异常的详细信息。
包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
- public String getMessage() :获取发生异常的原因。
提示给用户的时候,就提示错误原因。
- public String toString() :获取异常的类型和异常描述信息(不用)。
出现异常,不要紧张,把异常的简单类名,拷贝到API中去查。
三、异常产生的原因和分类
异常产生的原因:
1)编写程序代码中的错误产生的异常,比如数组越界、空指针异常等,这种异常叫做未检查的异常,一般需要在类中处理这些异常;
2)Java内部错误发生的异常,Java虚拟机产生异常;
3)通过throw(抛出异常)语句手动生成的异常,这种异常叫做检查的异常,一般是用来给方法调用者一些必要的信息。
异常的产生过程解析:
先运行下面的程序,程序会产生一个数组索引越界异常ArrayIndexOfBoundsException。我们通过图解来解析下异常产生的过程。
工具类:
public class ArrayTools {
// 对给定的数组通过给定的角标获取元素。
public static int getElement(int[] arr, int index) {
int element = arr[index];
return element;
}
}
测试类:
public class ExceptionDemo {
public static void main(String[] args) {
int[] arr = { 34, 12, 67 };
intnum = ArrayTools.getElement(arr, 4)
System.out.println("num=" + num);
System.out.println("over");
}
}
上述程序执行过程图解:
异常的分类:
我们平常说的异常就是指Exception,因为这类异常一旦出现,我们就要对代码进行更正,修复程序。
异常(Exception)的分类:根据在编译时期还是运行时期去检查异常?
编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)
运行时期异常:runtime异常。在运行时期,检查异常。在编译时期,运行异常编译器不会检测(不报错)。(如数学异常)
编译时异常和运行时异常的区别:
- 编译时异常一般发生的概率比较高。
- 运行时异常一般发生的概率比较低。
- 编译时异常发生概率较高,需要在运行之前对其进行 预处理。
- 运行时异常发生概率较低,没必要提前进行预处理。
四、异常的处理
1、防御式编程
错误在代码中是客观存在的, 所以要让程序出现问题的时候快速通知程序猿。
通知有两种方式:
(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 先操作遇到问题再处理
private static int divide() {
int a = 0, b = 0;
try (Scanner scanner = new Scanner(System.in)) {
a = scanner.nextInt();
b = scanner.nextInt();
return a / b;
} catch (ArithmeticException exception) {
System.out.println("除数为0");
return 0;
}
}
优点:正常流程和错误流程是分离开的, 程序员更关注正常流程,代码更清晰,容易理解代码。
2、异常的抛出(throw)
在编写程序时,如果程序中出现错误,这就需要将错误的信息通知给调用者
这里就可以借助关键字throw,抛出一个指定的异常对象,将错误信息告知给调用者。
比如写一个运行时异常:
public static void func2(int a) {
if(a == 0) {
//抛出的是一个指定的异常,最多的使用方式是,抛出一个自定义的异常
throw new RuntimeException("a==0");
}
}
public static void main(String[] args) {
func2(0);
}
注意:
- throw必须写在方法体内部;
- 如果抛出的是编译时异常,用户就必须要处理,否则无法通过编译;
- 如果抛出的运行时异常,则可以不用处理,直接交给JVM来处理;
- 一旦出现异常,后面的代码就不会执行。
3 、异常的捕获
throws异常声明
throws处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛 给方法的调用者来处理。
格式:
修饰符 返回值类型 方法名(参数列表) throws 异常类型 {
}
如果说方法内部抛出了多个异常,throws之后就必须跟多个异常类型,用逗号进行分隔。
public static void func2(int a) throws CloneNotSupportedException, FileNotFoundException {
if(a == 0) {
throw new CloneNotSupportedException("a==0");
}
if(a == 1) {
throw new FileNotFoundException();
}
}
如果抛出多个异常类型有父子关系,直接声明父类
public static void func2(int a) throws Exception {
if(a == 0) {
throw new CloneNotSupportedException("a==0");
}
if(a == 1) {
throw new FileNotFoundException();
}
}
调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出。
public static void main(String[] args) throws FileNotFoundException, CloneNotSupportedException {
func2(0);
}
try-catch捕获异常并处理
当程序抛出异常的时候,程序员通过try-each处理了异常。
public static void main(String[] args) {
try {
int[] array = null;
System.out.println(array.length);
}catch (NullPointerException e) {
System.out.println("捕获到了一个空指针异常!");
}
System.out.println("其他程序!");
}
如果程序抛出异常,不处理异常,那就会交给JVM处理,JVM处理就会把程序立即终止。并且,即使用了try-each 也必须捕获一个对应的异常,如果不是对应异常,也会让JVM进行处理。
如果try抛出多个异常,就必须用多个catch进行捕获。
这里注意,用多个catch进行捕获,不是同时进行捕获的,因为不可能同时抛不同的异常。
public static void main(String[] args) {
try {
int[] array = null;
System.out.println(array.length);
}catch (NullPointerException e) {
System.out.println("捕获到了一个空指针异常!");
}catch (ArithmeticException e) {
System.out.println("捕获到了一个算术异常!");
}
System.out.println("其它代码逻辑!");
}
也可以简写一下:
public static void main(String[] args) {
try {
int[] array = null;
System.out.println(array.length);
}catch (NullPointerException | ArithmeticException e) {
System.out.println("捕获到了一个空指针或算术异常!");
}
System.out.println("其它代码逻辑!");
}
如果异常之间具有父子关系,那就必须子类异常在前,父类异常在后catch,不然会报错 。
public static void main(String[] args) {
try {
int[] array = null;
System.out.println(array.length);
}catch (NullPointerException e) {
System.out.println("捕获到了一个空指针异常!");
}catch (Exception) {
System.out.println("捕获到了一个算术异常!");
}
System.out.println("其它代码逻辑!");
}
finally
finally用来进行资源回收,不论程序正常运行还是退出,都需要回收资源。并且异常会引发程序的跳转,可能会导致有些语句执行不到。
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int[] array = null;
System.out.println(array.length);
}catch (NullPointerException e) {
System.out.println("捕获到了一个空指针异常!");
}catch (ArithmeticException e) {
System.out.println("捕获到了一个算术异常!");
}finally {
scanner.close();
System.out.println("进行资源关闭!");
}
System.out.println("其它代码逻辑!");
}
如果不为空,那么finally还会被执行吗?
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
int[] array = {1,2,3};
System.out.println(array.length);
}catch (NullPointerException e) {
System.out.println("捕获到了一个空指针异常!");
}catch (ArithmeticException e) {
System.out.println("捕获到了一个算术异常!");
}finally {
scanner.close();
System.out.println("进行资源关闭!");
}
System.out.println("其它代码逻辑!");
}
所以,不管程序会不会抛出异常,finally都会执行。
如果将资源写在try中会自动帮助,关掉资源的。
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
int[] array = {1, 2, 3};
System.out.println(array.length);
} catch (NullPointerException e) {
System.out.println("捕获到了一个空指针异常!");
} catch (ArithmeticException e) {
System.out.println("捕获到了一个算术异常!");
} finally {
System.out.println("进行资源关闭!");
}
System.out.println("其它代码逻辑!");
}
下面看这一段代码:
public static int func(int a) {
try{
if(a == 0) {
throw new ArithmeticException();
}
return a;
} catch (ArithmeticException e) {
System.out.println("算术异常!");
} finally {
return 20;
}
}
public static void main(String[] args) {
System.out.println(func(10));
}
可以发现即使有return,finally也会被执行。
总结一下:
- throw抛出异常,throws声明异常;
- finally语句一定会执行。
五、自定义异常类
虽然Java中有很多异常类,但是在实际开发中所遇到的一些异常,不能完全表示,所以这就需要我们自定义异常类。
举一个例子:
先自定义一个运行时异常:
//自定义了一个运行时异常
public class MyException extends RuntimeException{
public MyException() {
}
public MyException(String message) {
super(message);
}
}
写一个类来捕获这个自定义异常:
public class Test04 {
public static void func(int a ) {
throw new MyException("呵呵!");
}
public static void main(String[] args) {
try {
func(20);
}catch (MyException myException) {
myException.printStackTrace();
}finally {
System.out.println("sadasdasd");
}
}
}
下面写一个用户登录的自定义异常类:
class UserNameException extends RuntimeException {
public UserNameException() {
}
public UserNameException(String message) {
super(message);
}
}
class PasswordException extends RuntimeException {
public PasswordException() {
}
public PasswordException(String message) {
super(message);
}
}
public class LogIn {
private static String uName = "admin";
private static String pword = "1111";
public static void loginInfo(String userName, String password) {
if ( !uName.equals(userName)) {
throw new UserNameException("用户名错误!");
}
if ( !pword.equals(password)) {
throw new RuntimeException("密码错误!");
}
System.out.println("登录成功!");
}
public static void main(String[] args) {
try {
loginInfo("admin","1111");
} catch (UserNameException e) {
e.printStackTrace();
} catch (PasswordException e) {
e.printStackTrace();
}
}
}
注意:
自定义异常默认会继承 Exception 或者 RuntimeException。
- 继承于 Exception 的异常默认是受查异常;
- 继承于 RuntimeException 的异常默认是非受查异常。
在Java中,异常处理是程序开发中的一个重要概念,正确处理异常可以提高程序的健壮性和可靠性。程序员应该尽可能避免使用不可预期的情况,并使用异常处理机制来处理可能出现的异常情况。
我们下期再见!
END
文案编辑|云端学长
文案配图|云端学长
内容由:云端源想分享
猜你喜欢
- 2025-01-03 聊一聊java中的异常
- 2025-01-03 学习Java异常,吃透这篇足够
- 2025-01-03 轻松搞定 JAVA 异常面试,25 道题及答案全掌握!
- 2025-01-03 Java全局异常处理,你不知道的骚操作(含hotspot源码分析)
- 2025-01-03 Android应用编程基础第19篇:Java语言中的异常处理
- 2025-01-03 Java中异常处理机制的详细解析及其优化示例代码
- 2025-01-03 java异常处理try/catch/finally
- 2025-01-03 Java异常详细介绍
- 2025-01-03 java安全编码指南之:异常处理
- 2025-01-03 Java8学习-Lambda表达式中处理异常的解决方案
你 发表评论:
欢迎- 04-24Java Collections 工具类集合框架中常用算法解析
- 04-24桶排序的简单理解
- 04-24Java集合框架底层实现原理大揭秘
- 04-24Java 集合框架全面解析:选对数据结构,提升开发效率
- 04-24c#集合排序
- 04-24Java面试中常被问到的集合类深度解读
- 04-24VBA技术资料MF278:对集合进行排序
- 04-24Spring 最常用的 7 大类注解,史上最强整理
- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)