异常
一、异常概述
1.1 什么是异常
在Java编程语言中,异常是指程序运行过程中出现的不正常且严重的错误。这些错误会中断正在运行的程序,因此,对异常的处理称为异常处理,它是程序设计中一个非常重要的方面,也是程序设计的一大难点。
Java中所有的与异常有关的类都继承于Throwable类。Throwable类有两个子类,一个是Error类,另一个是Exception类。具体来说,Error类代表了JVM本身的错误,当这类错误发生时,JVM会做出终止线程的动作,我们通常不需要对这类错误进行处理。而Exception类则分为运行时异常和编译期异常。运行时异常是指可能在程序运行过程中发生的异常,例如空指针异常、数组下标越界等;编译期异常则是指在编译阶段就能发现的错误。
Java中的异常机制是针对正常运行程序的一个必要补充。虽然一般来说没有加入异常机制,程序也能正常运营,但由于入参、程序逻辑的严谨度等因素,总会有期望之外的结果生成。因此,加入异常机制的补充,就是为了更好的处理意料之外的结果。
总的来说,异常处理已经成为衡量一门语言是否成熟的标准之一,增加了异常处理机制后的程序有更好的容错性,更加健壮。
1.2 常见的异常类
在Java编程语言中,异常类主要分为两大类:Checked Exception(非Runtime Exception)和Unchecked Exception(Runtime Exception)。
Checked Exception,也被称为编译时异常,这种类型的异常需要在编写程序时进行处理。例如,文件找不到的FileNotFoundException,试图打开一个不存在的文件时就会触发这个异常;当试图连接到一个不可用的数据库时,会抛出SQLException等。
另一方面,Unchecked Exception,也被称为运行时异常,这种类型的异常不需要在编写程序时显式处理。常见的运行时异常有ArithmeticException(算术异常),当进行非法的算术运算如除以零时,就会抛出这个异常;NullPointerException(空指针异常),试图访问一个空对象的成员时会触发此类异常;ArrayIndexOutOfBoundsException(数组下标越界异常),访问数组时使用的索引超出数组的大小就会引发此异常;以及ClassCastException(类型转换异常),试图将一个对象转换为不兼容的类型时会抛出此类异常等。
此外,还存在一种特殊的异常,即用户自定义异常。这种异常是程序员根据具体需求创建的,以满足特殊场景下的异常处理需求。例如,数据格式转换异常(NumberFormatException),它通常在将一个字符串转换成数字的过程中发生,当字符串的格式无法被正确转换成数字时,就会抛出这个异常。
1.3 异常类的继承体系
Java异常类的继承体系主要包括以下几个层次:
-
1、Throwable(所有异常和错误类的父类):这是Java异常类的基类,它包含了一些基本的方法,如getMessage()、printStackTrace()等。所有的异常类都直接或间接地继承自这个类。
-
2、Error(错误类的父类):这个类表示程序运行时的错误,例如系统崩溃、虚拟机错误等。这些错误通常无法通过正常的程序流程来处理,因此需要立即停止程序的执行。Error类是Throwable类的子类。
-
3、Exception(异常类的父类):这个类表示程序运行时可能出现的问题,例如文件未找到、网络连接失败等。这些问题可以通过正常的程序流程来解决,因此可以使用try-catch语句来捕获和处理这些异常。Exception类是Throwable类的子类。
-
4、RuntimeException(运行时异常类的父类):这个类表示程序在运行过程中可能出现的非受检异常,例如空指针异常、数组越界异常等。这些异常不需要在方法签名中声明,也不需要使用try-catch语句来捕获和处理。RuntimeException类是Exception类的子类。
-
5、IOException(输入输出异常类):这个类表示与输入输出操作相关的异常,例如文件未找到、网络连接失败等。IOException类是Exception类的子类。
-
6、SQLException(数据库异常类):这个类表示与数据库操作相关的异常,例如SQL语法错误、数据库连接失败等。SQLException类是Exception类的子类。
-
7、自定义异常类:用户可以根据需要自定义异常类,继承自Exception类或其子类。自定义异常类可以包含更多的信息,以便在抛出异常时提供更多的上下文信息。
二、异常处理
2.1 什么是异常处理
Java异常处理是Java程序中的一个重要环节,它是为了使程序在遇到Java异常处理是Java程序中的一个重要环节,它是为了使程序在遇到错误或异常情况时能够正确处理并给出有意义的反馈,而不是直接崩溃。在Java中,异常事件的发生可以分为两大类:Error和Exception。
-
1、Error:这是Java虚拟机无法解决的严重问题,如JVM系统内部错误、资源耗尽等严重情况,例如StackOverflowError和OutOfMemoryError。这类问题一般不编写针对性的代码进行处理。
-
2、Exception:这是由于编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界等。对于这类错误,一般有两种解决方法:一种是遇到错误就终止程序的运行;另一种是由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
- 1、检查性异常:这是最具代表的检查性异常,由用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 2、运行时异常:这是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 3、错误:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译时也检查不到。
2.2 try-catch块
Java中的try-catch块用于处理程序运行过程中可能出现的异常。它的基本结构如下:
try {
// 可能抛出异常的代码块
} catch (异常类型1 变量名1) {
// 处理异常类型1的代码块
} catch (异常类型2 变量名2) {
// 处理异常类型2的代码块
} finally {
// 无论是否发生异常,都会执行的代码块
}
例如,下面的代码演示了如何使用try-catch块处理除数为0的情况:
public class TryCatchDemo {
public static void main(String[] args) {
int a = 10;
int b = 0;
try {
int result = a / b;
System.out.println("结果是:" + result);
} catch (ArithmeticException e) {
System.out.println("发生了除数为0的异常");
} finally {
System.out.println("无论是否发生异常,都会执行这里的代码");
}
}
}
2.3 访问异常信息
在Java中,可以通过捕获异常对象来访问异常信息。以下是一个示例:
public class ExceptionDemo {
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println("结果是:" + result);
} catch (Exception e) {
System.out.println("发生异常:" + e.getMessage());
}
}
public static int divide(int a, int b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException("除数不能为0");
}
return a / b;
}
}
在这个示例中,我们定义了一个divide方法,该方法接受两个整数参数并执行除法运算。如果除数为0,我们抛出一个ArithmeticException异常。在main方法中,我们使用try-catch语句调用divide方法,并在catch块中捕获异常对象,然后通过e.getMessage()方法获取异常信息并打印出来。
2.4 finally块
Java中的finally块是一个可选的代码块,它总是在try和catch语句之后执行。无论try和catch语句是否成功执行,finally块中的代码都会被执行。通常用于释放资源,如关闭文件、数据库连接等。
示例:
public class FinallyExample {
public static void main(String[] args) {
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("捕获到异常: " + e.getMessage());
} finally {
System.out.println("finally块中的代码总是会被执行");
}
}
}
三、声明和抛出异常
3.1 声明异常-throws
在Java中,声明异常是通过throws关键字来实现的。当一个方法可能抛出异常时,需要在方法签名后面加上throws关键字,后面跟异常类型。例如:
public void myMethod() throws IOException {
// 方法体
}
在这个例子中,myMethod方法可能会抛出IOException异常。调用这个方法的代码需要处理这个异常,可以使用try-catch语句来捕获和处理异常,或者继续向上抛出。
3.2 抛出异常-throw
在Java中,抛出异常是通过throw关键字来实现的。当程序遇到错误情况时,可以使用throw关键字抛出一个异常对象。例如:
public void myMethod() throws IOException {
if (condition) {
throw new IOException("发生了IO异常");
}
// 其他代码
}
在这个例子中,如果condition为真,那么会抛出一个IOException异常,并附带一条错误信息。调用这个方法的代码需要处理这个异常,可以使用try-catch语句来捕获和处理异常,或者继续向上抛出。
3.3 throws和throw的区别
throws和throw都是Java中用于抛出异常的关键字,但它们的作用和使用方式不同。
- 1、throws关键字:用于声明方法可能抛出的异常类型。当一个方法可能抛出异常时,需要在方法签名后面加上throws关键字,后面跟异常类型。例如:
public void myMethod() throws IOException {
// 方法体
}
在这个例子中,myMethod方法可能会抛出IOException异常。调用这个方法的代码需要处理这个异常,可以使用try-catch语句来捕获和处理异常,或者继续向上抛出。
- 2、throw关键字:用于手动抛出一个异常对象。当程序遇到错误情况时,可以使用throw关键字抛出一个异常对象。例如:
public void myMethod() throws IOException {
if (condition) {
throw new IOException("发生了IO异常");
}
// 其他代码
}
在这个例子中,如果condition为真,那么会抛出一个IOException异常,并附带一条错误信息。调用这个方法的代码需要处理这个异常,可以使用try-catch语句来捕获和处理异常,或者继续向上抛出。