Java中的异常处理机制
为什么需要异常处理机制呢?
在使用编程语言开发过程中,程序员总希望把代码写的尽善尽美,但是在系统的运行过程中,仍会遇到一些问题,而这些问题又不是靠代码能够避免的,比如:客户不按照提示输入相应格式的数据,在读取文件时文件不存在等情况。所以需要使用异常来处理。
1.概念
异常:在java语言中,将程序执行中的不正常情况称为“异常”。(注意:语法错误和程序的逻辑错误不是异常)
2.异常的分类
Java语言中,异常分为两大类:
①Error :Java虚拟机无法解决的严重问题。例如JVM系统内部错误、资源耗尽等严重情况。例如:StackOverflowError和 OOM(OutOfMemoryError)错误。一般不编写针对性的代码进行处理。
②Exception:其他因变成错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如
- 空指针异常
- 下标越界异常
- 类转换错误异常
- 数据格式异常
- 算数运算异常
对于异常处理最理想的情况是在编译时间,但是有些异常在编译期间没有问题,在运行期间才会发生,例如数组下标越界、除数为0等。所以根据异常捕获的时间将异常分为两类:编译时异常和运行时异常。
Java的Exception类中包含一个RuntimeException(运行时异常)和IOException、ClassNotFoundException、CloneNotSupportException(三个编译时异常)。
2.1 常见的编译时异常
- EOFException
- FileNotFoundException
- UnknownHostException
…
2.2 常见的运行时异常
- ArithmeticException 算术运算异常
- ClassCastException 类转换异常
- IndexOutOfBoundsEXception 下标越界异常
- NullPointerException 空指针异常
…
3.异常的产生
当程序执行过程中出现异常时,都会抛出一个异常对象,Java中异常对象的抛出有两种情况:
- 系统自动生成的异常对象
- 编程人员手动抛出(
throw
)的异常对象
3.1 throw的使用
-
抛出运行时异常对象
throw new RuntimeException("异常提示信息");
-
抛出编译时异常对象
//可以抛出Exception类对象,也可以抛出其他非运行时异常的子类对象
throw new Exception("异常提示信息");
注意:当抛出编译时异常时,一般会通过throws Exception的方式将异常抛给方法调用者处理。
如果没有throws Exception,编译器会报错!
4.异常的处理方式
对于上述两种异常的解决办法:一种是遇到异常就终止程序运行。另一种方法是由程序员在编写程序时,就考虑到错误的检测、错误信息的提示、以及错误的处理。
4.1 try-catch-finally
try{
可能会产生异常代码;
} catch(Exception1 e) {
异常处理1;
} catch(Exception2 e) {
异常处理2;
} catch(Exception3 e) {
异常处理3;
}
......
finally{
//finally是可选;
将一定要执行的代码放在这;
}
说明:
- 使用
try
将可能会产生异常的代码包裹起来,在执行过程中,一旦出现代码异常,就会抛出一个对象异常类的对象,根据跑出的异常类对象,去catch
中 进行匹配。 try
中的异常类对象匹配到某一个catch
时,就会进入catch
中进行异常的处理,一旦异常处理完成且没有在catch
块中产生其他异常时(没有finally结构时),将会自动执行try-catch
结构之后的代码。如果catch
中再一次产生异常,需要在catch
中嵌套try-catch-finally
结构或者throws
的方式处理。<有finally结构第9小点提到了>- catch中的异常类型,如果没有子父类关系时,声明顺序没有要求,如果有子父类的关系时,则要求子类声明在父类前。
- catch中常用的异常处理方法:
4.1e.getMessage(); //获取异常的信息
4.2e.printStackTrace(); //控制台打印出异常信息
- 在
try{}
结构中定义的变量,只能在try{}
中使用,出了try{}
将不能使用。 - 使用
try-catch-finally
处理编译时异常时,使得程序在编译时就不再报错了,但在运行时仍可能报错。相当于我们使用try-catch-finally
将一个编译时异常,延迟到运行时异常。 try-catch-finally
结构可以嵌套使用 ;- 开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了(修改代码的逻辑来避免运行时异常),针对编译时异常,我们一定要考虑异常处理。
try-catch-finally
中finally
的使用:
9.1finally
是可选的结构 。
9.2finally
中生命的结构是一定会被执行的代码,即使cathc
中出现一张,try
有return
语句,catch
中有return
语句。也会在异常或者return
语句之前执行完finally
有语句。
9.3 一般什么情况适用finally
?
像数据库连接、输入输出流、网络编程中的资源,JVM是不能自动回收的,我们需要自己手动的进行资源的释放,此时的资源释放,就需要声明在finally
中。
4.2 throws
throws + 异常类型
写在方法声明处,指定此方法执行时,可能抛出的异常类型。当方法体执行时如果出现异常,会在异常代码处生成一个异常类对象,如果此异常类对象满足throws
后异常类型时,就会将异常抛给方法的调用者。出现异常后的代码将不再执行。try-catch-finally
:真正的将异常解决了
throws
的方式只是将异常抛给了方法调用者,并没有真正的将异常解决。- 在类的继承中,子类重写的方法抛出的异常要不大于父类被重写的方法所抛出的异常。
- 开发中如何选择
try-catch-finally
还是使用hrows
?
4.1 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws
,意味着如果子类重写的方法中有异常的话只能通过try-catch-finally
的方式处理。
4.2 如果执行的方法中,先后又调用了其他几个方法,这几个方法之间递进执行的,我们建议这几个方法使用throws
的方式将异常抛出去,在调用这几个方法的方法A可以考虑用try-catch-finally
来处理。
4.3 自定义异常类
步骤:
1. 继承于现有的异常类结构 RuntimeException、Exception;
2. 提供全局常量:serivalVersionUID 唯一的标识这个类;
3. 提供多个重载的构造器;
4. 只有异常体系的对象才能throw,其他对象都不能使用throw。例如 throw new String("123");//错误
public class MyException extends RuntimeException{
static final long serialVersionUID = -8034897190745766939L;
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}