在代码时,即使尽可能的避开错误,但在程序的与运行过程中难免会出现问题,它可能会导致程序中断或出现不可预料的结果。本篇文章主要对异常出现时如何进行处理,以及如何尽量将异常实现可控化,进行概述。
目录
导言:
正文:
一.异常的概念和体系结构
二.异常的分类
三.异常的处理
四.自定义异常
总结:
在Java中,将程序执行过程中发生的不正常行为或者在程序执行过程中出现的问题或意外情况称为异常。在Java中,异常是以对象的形式表示的,它们都是Throwable类的子类。Java的异常体系结构主要包括Throwable、Exception和Error三个关键类。
Throwable类:Throwable是所有异常类的基类,它有两个重要的子类:Exception和Error。Throwable类定义了一些重要的方法,比如getMessage()用于获取异常的描述信息、printStackTrace()用于打印异常堆栈信息等。
Exception类:Exception是程序中可以捕获并处理的异常的基类。它又分为两种:编译时异常(Checked Exception)和运行时异常(Unchecked Exception)。编译时异常是在编译时期就会被检查到的异常,需要在代码中显式地进行处理,比如IOException和SQLException等。而运行时异常是在程序运行时才会被检查到的异常,可以选择捕获处理也可以不处理,比如NullPointerException和ArrayIndexOutOfBoundsException等。
Error类:Error是程序中无法处理的严重问题的表示,通常表示虚拟机无法恢复的错误,比如OutOfMemoryError和StackOverflowError等。与Exception不同,Error类通常不需要程序员进行处理,因为它们表示的是程序无法继续执行的严重问题。
来看几个异常的例子:
1. 算术异常:
System.out.println(10 / 0);
// 执行结果
Exception in thread "main" java.lang.ArithmeticException: / by zero
2. 数组越界异常
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
// 执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
3. 空指针异常
int[] arr = null;
System.out.println(arr.length);
// 执行结果
Exception in thread "main" java.lang.NullPointerException
从上述过程中可以看到,java中不同类型的异常,都有与其对应的类来进行描述。
异常可能在编译时发生,也可能在程序运行时发生,根据发生的时机不同,可以将异常分为:运行时异常和编译时异常。
Checked Exception(编译时异常):
具体代码实例:
public class Person {
private String name;
private String gender;
int age;
// 想要让该类支持深拷贝,覆写Object类的clone方法即可
@Override
public Person clone() {
return (Person)super.clone();
}
}
编译时报错:
Error:(17, 35) java: 未报告的异常错误java.lang.CloneNotSupportedException; 必须对其进行捕获或声明以便抛出
Unchecked Exception(运行时异常):
具体代码实例:
public class RuntimeExceptionExample {
public static void main(String[] args) {
try {
int[] arr = new int[5];
int value = arr[10]; // 这里会抛出ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("捕获到数组越界异常:" + e.getMessage());
}
// 模拟空指针异常
String str = null;
try {
System.out.println(str.length()); // 这里会抛出NullPointerException
} catch (NullPointerException e) {
System.out.println("捕获到空指针异常:" + e.getMessage());
}
// 模拟类型转换异常
try {
Object obj = "Hello";
Integer num = (Integer) obj; // 这里会抛出ClassCastException
} catch (ClassCastException e) {
System.out.println("捕获到类型转换异常:" + e.getMessage());
}
}
}
在上面的示例中,我们故意制造了几种运行时异常的情况,分别是数组越界、空指针引用和类型转换异常。然后使用try-catch块来捕获这些异常,并输出异常信息。这样可以避免程序因为这些异常而终止,同时也可以根据具体的异常信息来进行相应的处理。
异常处理是指在程序运行过程中,当程序发生异常时,如何进行相应的处理。异常处理的目的是保证程序的稳定性和可靠性,避免程序因为异常而崩溃或者出现不可预料的错误。Java提供了一套完整的异常处理机制,程序员可以使用这些机制来捕获和处理异常。
Java中的异常处理机制主要包括以下几个方面:
1.抛出异常:
当程序出现异常时,可以使用throw关键字手动抛出一个异常对象,这个异常对象可以是Java内置的异常类,也可以是自定义的异常类。
在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测。
在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:
throw new XXXException("异常产生的原因");
具体代码实例:
public static int getElement(int[] array, int index){
//实现一个获取数组中任意位置元素的方法。
if(null == array){
throw new NullPointerException("传递的数组为null");
}
if(index < 0 || index >= array.length){
throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
}
return array[index];
}
public static void main(String[] args) {
int[] array = {1,2,3};
getElement(array, 3);
}
上述代码会对传进来的数组进行判断,如果出现了空数组或者越界的情况会抛出指定的异常信息提醒程序员。
注意事项:
- throw必须写在方法体内部
- 抛出的对象必须是Exception 或者 Exception 的子类对象
- 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
- 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
- 异常一旦抛出,其后的代码就不会执行
2.捕获异常:
异常的捕获,也就是异常的具体处理方式,主要有两种:异常声明throws 以及 try-catch捕获处理。在程序中使用try-catch块来捕获异常,如果try块中的代码出现了异常,就会跳转到catch块中进行异常处理。catch块中可以根据异常类型来进行相应的处理,比如输出异常信息、重新抛出异常、返回默认值等。
处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常。
语法格式:
修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}
throws对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。如果真正要对异常进行处理,就需要try-catch。
语法格式:
try{
// 将可能出现异常的代码放在这里
}catch(要捕获的异常类型 e){
// 如果try中的代码抛出异常了,此处catch捕获时异常类型与try中抛出的异常类型一致时,或者是try中抛出异常的基类
时,就会被捕获到
// 对异常就可以正常处理,处理完成后,跳出try-catch结构,继续执行后序代码
}[catch(异常类型 e){
// 对异常进行处理
}finally{
// 此处代码一定会被执行到
}]
// 后序代码
// 当异常被捕获到时,异常就被处理了,这里的后序代码一定会执行
// 如果捕获了,由于捕获时类型不对,那就没有捕获到,这里的代码就不会被执行
注意:
1. []中表示可选项,可以添加,也可以不用添加
2. try中的代码可能会抛出异常,也可能不会
finally块:finally块中的代码无论是否发生异常,都会被执行。通常在finally块中进行一些资源释放的操作,比如关闭文件、关闭数据库连接等。
具体代码实例:
public class ExceptionHandlingExample {
public static void main(String[] args) {
try {
int a = 10 / 0; // 这里会抛出ArithmeticException
} catch (ArithmeticException e) {
System.out.println("捕获到算术异常:" + e.getMessage());
} finally {
System.out.println("finally块中的代码被执行");
}
}
}
在上面的示例中,我们故意制造了一个除以0的异常,然后使用try-catch块来捕获这个异常,并输出异常信息。同时,在finally块中输出一条信息,无论是否发生异常,这条信息都会被输出。这样可以保证程序的稳定性和可靠性。
流程总结:
注意事项:
- try块内抛出异常位置之后的代码将不会被执行
- 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后中断程序----异常是按照类型来捕获的
- try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获,如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误:
在Java中,除了使用Java内置的异常类来处理异常外,还可以自定义异常类来处理特定的异常情况。自定义异常类可以继承自Exception或者RuntimeException类,也可以实现Throwable接口,以实现我们自己的异常处理逻辑。
自定义异常通常包括以下几个方面:
继承Exception或者RuntimeException类:自定义异常类通常继承自Exception或者RuntimeException类,这样可以让我们的自定义异常类具有和Java内置异常类相同的特性和行为。
添加构造方法:自定义异常类通常需要添加构造方法,用于初始化异常信息。通常情况下,我们会添加一个带有字符串参数的构造方法,用于传递异常信息。
添加异常信息:自定义异常类通常需要添加异常信息,用于描述异常的原因和情况。我们可以通过重写getMessage()方法来实现这个功能。
具体代码实例:
public class MyException extends Exception {
//自定义的异常类,用于对异常进行自定义的处理
public MyException(String message) {
super(message);
}
@Override
public String getMessage() {
return "自定义异常:" + super.getMessage();
}
}
public class CustomExceptionExample {
//当出现指定异常时,引用指定的异常对象
public static void main(String[] args) {
try {
throw new MyException("这是一个自定义异常");
} catch (MyException e) {
System.out.println(e.getMessage());
}
}
}
在这段代码中,我们通过throw关键字手动抛出了一个MyException类型的异常,并在catch块中捕获了这个异常。最终,我们输出了异常信息,这里的异常信息包括了我们自定义的文本和传递进来的异常信息。
注意事项:
- 自定义异常通常会继承自 Exception 或者 RuntimeException
- 继承自 Exception 的异常默认是受查异常
- 继承自 RuntimeException 的异常默认是非受查异常
在实际编程中,合理地处理异常可以提高程序的健壮性和可靠性。正确地处理异常不仅可以避免程序崩溃,还可以提供更好的用户体验。正确的使用异常还能即使告诉程序员出错的原因,不必花大量的时间进行调试,加快开发的效率。