加入异常处理操作,会增加程序的容错性,会减少程序因为某些状况的发生突然退出的情况。
异常机制可以使程序中的异常处理代码和正常业务代码分离,保证程序代码更加优雅,并可以提高程序的健壮性。
五个关键词:try、catch、finally、throw和throws
Java的异常可以分为两种:Checked异常和Runtime异常。
Checked异常都是可以在编译阶段被处理的异常,所以它强制程序处理所有的Checked异常。
Checked异常要么显式声明抛出;要么显式捕获并处理它。
而Runtime异常则无需处理。
Error为错误,Exception为异常。Exception中RuntimeException为Runtime异常,其他都为Checked异常。
Checked异常至少存在两大不便: ①对于程序中的Checked异常,java要求必须显式捕获并处理该异常或者显式声明抛出该异常; ②如果在方法中显式的声明抛出Check异常,将会导致方法签名与异常耦合,如果该方法是重写父类的方法,则该方法抛出的异常还会受到被重写方法的所抛出异常的限制。
大部分时候推荐Runtime异常,而不使用Check异常。
runtime异常优点:正常代码和错误处理代码分离;保证程序具有较好健壮性;避免checked的繁琐性。
checked异常优点:能在编译时体型程序员代码可能存在的问题,体型程序员必须注意处理该异常。
if(用户输入不合法)
{
alert 输入不合法
goto retry
}
else
{
业务实现代码
}
常见的代码中如果单纯使用if else语句实现错误处理机制,主要存在两个缺点:
①无法穷举所有的异常情况②错误处理代码和业务实现代码混杂。
异常处理机制的过程:
当程序运行出现意外情况时,系统会自动生成一个Exception对象来通知程序。 可以采用try...catch捕获异常; 或者使用throws抛出异常到上一层,但目标还是寻找catch来捕获异常,如果一直没有找到对应的catch块,那么异常就会一直向抛出,直到处于JVM层次时,JVM处理,JVM处理异常的方式是,打印异常的跟踪栈信息,并终止运行(这就是一般程序遇到异常后自动结束的原因)。
1.try...catch捕获异常
try
{
业务实现代码
}
catch (Exception e)
{
alert 输入不合法
goto retry
}
如果try块里的业务逻辑代码出现异常,系统自动生成一个异常对象,该异常对象被提交给Java运行环境时,这个过程被称为抛出异常,此时Java运行环境寻找能处理该异常对象的catch块,找到并交给catch块处理成为捕获异常。
每次try以后,catch块至多只能运行一个。如图,Exception放在最后,也是因为如果它在最前面,那么它必将捕获异常,而它的子类也就没有用了;而放到最后,能够为其子类增加了一个捕获未知异常的功能。也即“先处理小异常,再处理大异常”。
异常处理的嵌套:在try块、catch块或finally块中包含完整的异常处理流程的情形被称为异常处理的嵌套。通常没有必要使用两次以上的必要,会降低程序可读性。
Java7新增的多异常捕获:
①捕获多种类型的异常时,多种异常类型之间用竖线(|)隔开。
②捕获多种类型的异常时,异常变量有隐式的final修饰,以此程序不能对异常变量重新赋值。
如图所示:
访问异常信息:所有的异常对象都包含了如下几个常用方法:
①getMessage():返回该异常的详细描述字符串
②printStackTrance():将该异常的跟踪栈信息输出到标准错误输出。
③printStackTrance(PrintStream s):将该异常的跟踪栈信息输出到指定输出流。
④getStackTrance():返回该异常的跟踪栈信息
2.finally回收资源
有些时候,程序在try块里打开了一些物力资源(例如数据库连接、网络连接和磁盘文件等),这些物力资源都必须显示回收。
异常处理语法结构,只有try块是必须的;catch和finally至少出现其中之一。
try
{
//return 在调用return以前会先调用finally块,再调用return;
//System.exit(1); 退出虚拟机,不再执行finally块
}
finally
{
}
注意:一旦在finally块中使用了return和throw语句,将会导致try块、catch块中的return、throw语句失效。
java7增强的自动关闭资源的try语句(隐式的finally块):
它允许在try关键字后紧跟一对圆括号,圆括号里可以声明、初始化一个或多个资源,此处的资源指的是那些必须在程序结束时显式关闭的资源。
为了保证try语句可以正常关闭资源,这些资源实现类必须实现AutoCloseable(抛出了Exception)或Closeable接口(抛出了IOException),实现这两个接口就必须实现close()方法。
java9再次增强了try语句块:
不再需要圆括号声明,只需要自动关闭的资源有finally修饰或者是有效的final(effectively final)(虽然没有final修饰,但是不再对该变量重新赋值)。
3.使用throws声明抛出异常
思路:当前方法不知道如何处理这种类型的异常,该异常应该由上一级使用者处理;如果main方法一不知道如何处理这种类型的异常,也可以使用throws声明抛出异常,该异常将交给JVM处理。
public void main(String []args)
throws IOException
{
}
使用throws声明抛出异常时有一个限制,就是重写时“两小”中的一条规则: ①子类方法声明抛出的异常类型应该是父类方法声明抛出的异常类型的子类或相同。 ②子类方法声明抛出的异常不允许比父类方法声明抛出的异常多。
4.使用throw抛出异常
当程序出现错误时,系统会自动抛出异常;除此之外,java也允许程序自行抛出异常,自行抛出异常使用throw语句来完成。
throw抛出的不是一个异常类,而是一个异常实例。
public class ThrowTest{
public static void main(String []args) {
try {
//调用声明抛出Checked异常的方法,要么显式捕捉该异常,要么在main方法中再次声明抛出
throwChecked(-3);
}
catch(Exception e) {
System.out.println(e.getMessage());
}
//调用声明抛出Runtime异常的方法既可以显式捕获该异常,也可以不理会该异常
throwRuntime(3);
}
public static void throwChecked(int a)throws Exception{
if(a>0) {
//自行抛出Exception异常,该代码必须处于try块里,或者处于带throws声明的方法里
throw new Exception("a的值大于0,不符合要求");
}
}
public static void throwRuntime(int a) {
if(a>0) {
//自行抛出RuntimeException异常,既可以显式捕获该异常,
//也可以完全不理会该异常,把该异常交给方法处理器调用
throw new RuntimeException("a的值大于0,不符合要求");
}
}
}