Java异常总结

784 阅读3分钟

在谈Java异常的时候,好多其他文章上来就谈Java中异常继承了什么,分什么... 我们不妨先来谈谈,如果Java没有异常怎么办?

怎么不按套路出牌
跟同事讨论这个问题的时候,很多同事的第一反应就是语塞。因为针对异常的处理,大家都太习惯了。习以为常,自然没有了思考这个问题的习惯。

如果不使用Java中的异常机制,其他的语言有些使用error code的方式。在程序异常的时候返回异常码,再有程序员根据异常码手动增加判断来处理。

而Java中则使用try-catch机制,用try-cath机制的一个好处是把异常代码与业务代码分离。当然不好的一个地方就是有很多的模板代码。

public class ReadFile {

    public static void main(String[] args) {
        File file = new File("D://test.txt");
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
            FileChannel channel = fileInputStream.getChannel();
            ByteBuffer byteBuffer = ByteBuffer.allocate(128);
            int i = channel.read(byteBuffer);
            while (i != -1) {
                byteBuffer.flip(); // 切换到读模式
                // byteBuffer操作
                byteBuffer.clear();
                i = channel.read(byteBuffer);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

一个简单的文件操作包含了大量的 try-catch,还要把 FileInputStream 变量的定义抽到 try的外层去定义,否则在 finally不能关闭。在随后的JDK7中引入的try-with-resources使关闭流的操作简单点。但由上可见,try-catch机制还是会导致很多的模板代码。

接下来我们聊一下Java的异常的具体情况: Exception 和 Error是继承了 Throwable 类

Throwable

Throwable类中的注释:

Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java {@code throw} statement. Similarly, only this class or one of its subclasses can be the argument type in a {@code catch} clause.

意思是:只有Throwable,或其子类可以被Jvm抛出。类似,也只有Throwable还有其子类可以作为catch 的参数。

For the purposes of compile-time checking of exceptions, {@code Throwable} and any subclass of {@code Throwable} that is not also a subclass of either {@link RuntimeException} or {@link Error} are regarded as checked exceptions.

基于编译时期检查异常的目的,Java会将Throwable(包括其子类)中,所有非 RuntimeException,或非Error的子类都视为受检异常。 换句话说,Exception的子类除RuntimeException是非受检的异常外。其他均为受检异常。

这里用代码说明一下什么是受检异常,什么是非受检异常。

受检异常

这里的 throwCheckedException为什么提示错误呢? 因为IOException并不是RuntimeException的子类。

正常代码如下

public class ThrowableDemo {

    public void throwError(){
        throw new NoClassDefFoundError();
    }

    public void throwRuntimeException(){
        throw new NullPointerException();
    }

    public void throwCheckedException() throws IOException {
        throw new IOException();
    }

   /* public void throwCheckedException() {
        try {
            throw new IOException();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }*/
    
    public static void main(String[] args) {
        ThrowableDemo throwableDemo = new ThrowableDemo();
        try {
            throwableDemo.throwCheckedException();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

对于受检异常,可以用try-catch处理。也可以选择继续抛出。

非受检异常:Java不在编译期间强制要求你处理 受检异常:Java要求在编译期间强制要求你处理

因此针对以上说的IOException需要你自己处理。

注意: 非受检异常都是RuntimeException(或其子类)。

对于RuntimeException如:NullPointerException,IndexOutOfBoundsException,ClassCastException这些大家比较熟悉的异常有一个共通点,就是都可以由程序员在编程的层面避免的。 NullPointerException: 使用前判断非空。 IndexOutOfBoundsException:检查数组索引是否存在。 ClassCastException: 转换类型前作判断。

而非RuntimeException则是不能通过程序来避免的。 如IOException:读取的文件资源在运行的时候不存在。 所以Java要求你对这些不可避免的异常作强制的处理。