Java异常浅析

342 阅读4分钟

什么是异常

异常都继承于Throwable类,其有两个继承类,Error和Exception,Error是指发生了程序无法处理的严重错误,程序无法处理,当然也无法catch,只能尽力保证程序安全终止;Exception是我们应该关注的层次结构,其又划分出两个分支——RuntimeException和其他异常,如IO类的错误

如果方法中可能产生异常就可以在方法标签后显式声明出来,如果一个方法可能抛出多个异常,那么必须在方法首部列出所有的异常类,每个类之间用逗号隔开

public Image loadImage(Stirng s) throws IOException,FileNotFoundException {}

如果父类方法中声明了异常,那么子类覆盖父类方法时候声明的已检查异常不能比父类更通用,也就是说子类可以抛出更特定的异常,或者根本不抛出异常。

需要特别注意的是,如果超类方法没有声明抛出任何异常,那么子类方法也不能声明抛出异常

创建自定义的异常类

总有标准API不能满足需求的时候,这时候自定义异常类就变得顺理成章。

class FileFormatException extends IOException {
    public FileFormatException() {
    }

    public FileFormatException(String s) {
        super(s);
    }
}

public static String readData(BufferedReader in) throws FileFormatException {
        //do sth
        throw new FileFormatException();
    }

捕获异常

try/catch组合捕获异常

  1. 如果在try语句块中发生了能被catch语句捕获的异常,则会跳过try语句块中剩余的语句,进入catch语句块。
try {
            System.out.println("do sth 1");
            throw new IOException();
            //System.out.println("do sth 2");
        } catch (IOException e) {
            System.out.println("do sth 3");

            System.out.println("do sth 4");
        }
        System.out.println("do sth 5");

以上输出
do sth 1
do sth 3
do sth 4
do sth 5

以上输出do sth 1 do sth 3 do sth 4 do sth 5

注: 同一个catch语句可以捕获多个异常,形式为catch(FileNotFoundException | UnKnownHostException),只有当捕获当异常类型之间不存在子类关系时才需要这个特性。

  1. 如果抛出了不能被catch的异常,方法将退出,程序如果没有更上层的方法处理异常,程序也将退出
try {
            System.out.println("do sth 1");
            int[] arrays = {1,2};
            System.out.println(arrays[2] > 4);
            throw new IOException();
            //System.out.println("do sth 2");
        } catch (IOException e) {
            System.out.println("do sth 3");

            System.out.println("do sth 4");
        }
        System.out.println("do sth 5");

以上输出
do sth 1
  1. 如果catch语句块中也抛出了异常,那么方法将会在catch语句块抛出异常处退出
try {
            System.out.println("do sth 1");
            throw new IOException();
            //System.out.println("do sth 2");
        } catch (IOException e) {
            System.out.println("do sth 3");
            throw new ArrayIndexOutOfBoundsException();
            //System.out.println("do sth 4");
        }
        //System.out.println("do sth 5");
        
以上输出
do sth 1
do sth 3

总结一下,程序抛出了异常,如果能被catch处理,就在处理后继续运行,否则就会在抛出异常处中断

finally语句

但是我们很多时候需要考虑资源释放,靠上述的模型是无法处理一些资源释放的工作的,finally语句就是一个很好的解决方案,它的作用在于,无论是否抛出异常,finally中的语句都会执行。看下面一个例子

try{
	//1
    code that might throw Exception
    //2
}catch {
	//3
    show throw message
    //4
}finally {
	//5
    do some close job
}
//6

  1. 如果程序没有抛出异常,执行1、2、5、6
  2. 如果程序抛出了被catch并正确处理的异常,则执行1、3、4、5、6
  3. 如果catch住了异常但catch语句中也抛出了异常,则执行1、3、5
  4. 如果抛出了不能被catch但异常,则执行1、5

我们也可以使用不带catch的try/finally组合,无论是否抛出异常,finally语句块都会被执行,当然方法调用者要考虑处理可能抛出的异常

try{
}finally {
}

这里强烈建议使用try/catch和try/finally的组合,提高代码的清晰度,比如

try {
	try {
    }finally{
    do some close job
    }
}catch(Exception e) {
show some excption message
}

内部try/finally组合保证一定能关闭资源,外部try/catch组合保证异常能被捕获,关键是能捕获finally语句块中的内容

这里注意return语句带来的影响,如果方法有返回值,则在return语句执行之前,会先执行finally中的内容,如果finally语句中也有return语句,则其会直接覆盖方法中的return

 try {
            n = n * n;
            return n;
        } catch (Exception e) {

        } finally {
            return 0;
        }
        
以上输出
0

如果是在catch语句中return,那就把catch想象成另一个try即可。