专业的JAVA编程教程与资源

网站首页 > java教程 正文

Java中的异常及处理方法

temp10 2025-01-03 19:04:31 java教程 12 ℃ 0 评论

“这里是云端源想IT,帮你轻松学IT”

嗨~ 今天的你过得还好吗?

Java中的异常及处理方法

别让别人的想法左右你的情绪

你的人生由你自己掌控

- 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

文案编辑|云端学长

文案配图|云端学长

内容由:云端源想分享

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表