目录
前言
对程序而言,发生各种各样的异常是很正常的,异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。今天我们来学习Java异常处理。
异常
在Java中,当程序执行中发生错误,错误事件对象可能导致的程序运行错误称为异常(Exception),异常可以分为3类:编译错误、运行错误和逻辑错误。
编译错误包括:大小写混淆、数据类型与变量类型不符合、使用未声明的变量。
运行错误包括:数组下标越界、除数为零。
逻辑错误包括:超出数据类型的取值范围、语句体忘记加花括号。
异常发生的原因有很多,通常包含以下几大类:
- 用户输入了非法数据;
- 要打开的文件不存在;
- 网络通信时连接中断,或者JVM内存溢出。
异常产生的后果:系统中若无相应的机制处理,则会产生死机、死循环或其他对操作系统的损害。
异常处理机制
程序运行所导致的异常发生后,怎么处理异常呢?Java语言提供的异常处理机制,由捕获异常和处理异常两部分组成。
在Java程序的执行过程中,如果出现了异常事件,就会生出一个异常对象。生出的异常对象将传递给Java运行时系统,这一异常的产生和提交过程称为抛弃(Throw)异常。找到能够处理这种类型的异常的方法后,运行时系统把当前异常对象交给这个方法进行处理,这一过程称为捕获(Catch)异常。
异常的表示
Java中,所有的异常都由类来表示,例如ArithmeticException。所有异常都使用对象来描述,如ArithmeticException e。
Java中定义了很多异常类,每个异常类都代表了一种运行错误,这些异常类都是Throwable类的直接或间接子类。
异常类的继承结构
Exception类和Error类都继承与java.lang.Object中的java.lang.Throwable类。
Throwable类
Throwable是java.lang包中一个专门用来处理异常的类。只有当对象是此类或其子类的实例时,才能通过Java虚拟机或Java throw语句抛出。类似地,只有此类或其子类才可以是catch的参数类型。它有两个子类,即Error和Exception,它们分别用来处理两组异常。
Error类
一般是指与虚拟机相关的问题,表示仅靠程序本身无法恢复的严重错误,也可以理解为Error用来处理程序运行环境方面的异常。例如:系统崩溃、虚拟机出错、动态链接失败等,这一类错误无法恢复且不可捕获,这将导致应用程序中断。
Exception类
是指一些可以被捕获且可能恢复的异常情况,如数组下标越界、数值被零除、输入/输出异常等。
Exception类可以分为
- 隐式异常:是指当程序运行时产生的异常,例如程序中除数为0引起的错误、数组下标越界错误等,这种异常是由程序本身引起的异常,但不是程序主动抛出的,而是在程序运行中产生的;
- 显示异常:是指Java程序中的非运行时异常的处理,它们都是在程序中用语句抛出,并且也是用语句进行捕获。例如:文件没找到的异常、类没找到的引起的异常等。
Exception子类对应的处理异常方法
子类 | 对应异常 |
---|---|
ArithmeticException | 除数为0引起的异常 |
ArrayIndexOutOfBoundsException | 访问数组元素下标越界的异常 |
ArrayStoreException | 数组存储空间不够引起的异常 |
ClassCastException | 当将对象强制转换为不是实例的子类时的异常 |
IllegalArgumentException | 方法传递不合法或者不正确参数的异常 |
IllegalMonitorStateException | 监控器状态出错引起的异常 |
IllegalStateException | 在非法或不适当的时间调用方法时产生的信号的异常 |
IllegalThreadStateException | 线程没有处于请求操作所要求的适当状态时的异常 |
IndexOutOfBoundsException | 数组下标越界或字符串访问越界引起的异常 |
NegativeArraySizeException | 数组长度是负数的异常 |
NullPointerException | 当应用程序试图在需要对象的地方使用 null 时的异常 |
NumberFormatException | 字符的UTF代码数据格式有错引起的异常 |
SecurityException | 安全管理器抛出的异常,指示存在安全侵犯 |
StringIndexOutOfBoundsException | 访问字符串序号越界的异常 |
UnsupportedOperationException | 当不支持请求的操作时的异常 |
ClassNotFoundException | 未找到指定名字的类或接口引起的异常 |
CloneNotSupportedException | 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常 |
IllegalAccessException | 拒绝访问一个类的时候抛出的异常 |
InstantiationException | 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException | 一个线程被另一个线程中断,抛出该异常 |
NoSuchFieldException | 请求的变量不存在 |
NoSuchMethodException | 请求的方法不存在 |
ThreadDeath类
调用Thread类中带有零参数的stop方法时,受害线程将抛出一个ThreadDeath实例。仅当应用程序在被异步终止后必须清除时才应该捕获这个类的实例。
如果ThreadDeath被一个方法捕获,那么将它重新抛出非常重要,因为这样才能该线程真正终止,否则顶级错误处理程序不会输出消息。
虽然ThreadDeath类是“正常出现”的,但它只能是Error的子类而不是Exception的子类,因为许多应用程序捕获所有出现的Exception,然后又将其放弃。
RuntimeException类
RuntimeException是那些可能在Java虚拟机正常运行期间抛出的异常的超类。Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获它,也没有用throws语句声明抛出它,还是会编译通过,这种异常可以通过改进代码实现来避免。
异常处理
Java的异常处理是通过5个关键字来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时可以通过其类型来捕获(catch)它,或最后(finally)由默认处理器来处理。
try-catch语句
异常处理的核心是try和catch,这两个关键字要一起使用。当try代码块发生异常并抛出一个异常是,异常会由相应的catch语句捕获并处理。只有在有异常抛出时,才会执行catch语句。语法格式为:
try{ //监视
//可能发生异常的代码
}catch(异常类型异常对象名){ //捕获并处理异常
//异常处理代码块
}
例如:
public class myfirst {
public static void main(String[] args) {
int i,a;
try { //监视可能发生异常的代码块
i=0;
a=36/i;
}catch(ArithmeticException e) { //捕获一个被零除异常
System.out.print("除数为零");
}
}
}
运行结果为:
除数为零
多个catch子句
当代码块
可能引起多个异常,处理这种情况时,就需要定义两个或更多的catch子句,每个子句捕获一种类型的异常。当异常被引发时,每个catch子句依次被检查,第一次匹配异常类型的子句被执行,其他的子句将被忽略。如果没有抛出异常,那么try代码块就会结束。语法格式为:
try{
//可能发生异常的代码块
}catch(异常类型1 异常对象名1){
//异常处理代码块1
}
catch(异常类型2 异常对象名2){
//异常处理代码块2
}
...
catch(异常类型n 异常对象名n){
//异常处理代码块n
}
例如:
public class myfirst {
public static void main(String[] args) {
try {
int i=args.length; //初始化i变量
System.out.println("i="+i); //输出i的值
int j=5/i;
int k[]= {1,2,3}; //声明一个数组k
k[5]=0; //数组k的下标为5的元素为0
}catch(ArithmeticException e) {
System.out.println("被零除"+e); //若被0除则捕获并处理
}catch(ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界异常:"+e); //若发生数组越界则捕获并处理
}
System.out.println("执行catch块后的语句块");
}
}
运行结果为:
i=0
被零除java.lang.ArithmeticException: / by zero
执行catch块后的语句块
finally子句
为了确保一段代码不管发生什么异常都要被执行,可以使用关键词finally来标出这样的代码块。语法格式为:
try{
//可能发生异常的代码块
}catch(异常类型 异常对象名){
//异常处理代码块
}
...
finally{
//无伦是否抛出异常都要执行的代码
}
例如:
public class myfirst {
public static void main(String[] args) {
int i,a;
try { //监视可能发生异常的代码块
i=0;
a=36/i;
}catch(ArithmeticException e) { //捕获一个被零除异常
System.out.print("除数为零");
}
finally { //不管发不发生异常都会被执行
System.out.println("最后执行的语句!!");
}
}
}
运行结果为:
除数为零
最后执行的语句!!
可嵌入的try块
一个try代码块可以嵌入到另一个try代码块中,语法格式为:
try{ //外部try代码块
try{ //内部try代码块
//可能发生异常的代码
}catch(异常类型异常对象名){ //捕获并处理异常
//异常处理代码块
}
}catch(异常类型异常对象名){ //捕获并处理异常
//异常处理代码块
}
抛出异常
对于处理不了的异常或要转型的异常,在方法声明除通过throws语句声明抛出异常,而throw语句是用于在方法体内抛出一个异常。
使用throws抛出异常
对于方法中可能出现的异常,如果不想在方法中进行捕获,可以在声明时利用throws语句抛出异常。声明抛出异常是在一个方法声明中的throws子句中指明。语法格式为:
[修饰符] 返回类型 方法名(参数1 参数2,...)throws异常列表{
...
}
例如,下面的代码声明了一个read()方法,该方法可能会抛出IOException异常。
public int read() throws IOException{
...
}
在throws子句中同时可以指说明该方法将不对这些异常进行处理,只是声明抛出它们,由调用此方法的其他方法负责捕获和处理。例如:下面的方法声明抛出IOException异常和IndexOutOfBoundsException异常,而不是在方法内部处理这些异常。
public static void main(String args[]) throws IOException,IndexOutOfBoundsException{
...
}
使用throw抛出异常
throw语句是用于在方法体中手动抛出异常,语法格式为:
throw 异常名;
throw关键字主要是用在try块中来用来说明已经发生的异常情况,throw关键字后面跟随一个从类Throwable中派生的异常对象,用来说明抛出的异常类型。例如:
import java.io.IOException;
public class myfirst {
public static void main(String[] args) {
try {
System.out.println("正在运行程序.....");
throw new IOException(); //用户自行产生异常,使用throw关键字抛出异常用户自行产生异常
}catch(IOException e) { //捕获异常并处理
System.out.println("已经捕获异常");
}
}
}
运行结果为:
正在运行程序.....
已经捕获异常
注意:只有使用throw才会真正抛出异常,而前面将的关键字throws仅是用于方法声明,说明“可能”会产生异常,在throw语句中是如何使用new创建IOException的。这里throw抛出一个对象,所以必须为类型创建一个对象来抛出。
异常类常有方法
Exception类自己定义任何方法,它继承了Throwable提供的一些方法,所以所有异常(包括自己自定义异常),都可以获得Throwable定义的方法。同时还可以在创建的自定义异常类中覆盖一个或多个这样的方法。例如:
方法声明 | 方法作用 |
---|---|
public String getMessage() | getMessage()方法返回描述当前异常类的消息字符串 |
public String toString() | toString()方法返回描述当前异常类的消息的字符串 |
public void printStackTrace() | printStackTrace()方法没有返回值,程序先后调用执行了哪些对象或类的哪些方法,使得运行过程中产生了这个异常对象 |
自定义异常
通过继承Exception类或它的子类,实现自定义异常类。对于自定义异常,必须采用throw语句抛出异常,这种类型的异常不会自行产生。
创建自定义异常
用户自定义异常类都必须由Exception类或Exception类的子类派生,所以必须显示指明异常类的基类。语法格式为:
class 自定义异常 extends 父异常类名{
类体;
}
注意:
- 自定义异常类必须是Throwable的直接或间接子类;
- 一个方法抛出的异常是作为这个方法与外界交互的一部分而存在的;
- 用异常代表错误,而不要在使用方法返回值。
自定义异常类的使用步骤:
- 创建自定义异常类;
- 在方法中通过throw关键字抛出异常对象;
- 可以使用try-catch语句捕获并处理,否则在方法声明处通过throws关键字指明要抛出给方法调用者的异常;
- 在出现异常方法的调用者中捕获并处理异常。
最后
好了,关于Java异常处理的知识讲到这里,谢谢观看!!!
我们下篇文章再见!!!
成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。