本固枝荣 —— Java关键字之异常处理

Java异常机制

Java有自己一套处理异常的框架,框架内的继承关系也很清晰,Throwable是所有的Error类和所有的Exception类的基类,Error可能是编译期间出现错误或者系统错误,程序并不去处理这种错误;Exception可能是运行时出现的异常或者检查程序时出现的异常,运行时出现的异常(RuntimeException,如NullPointerException、ArrayIndexOutOfBoundsException等)在编程时可以去捕获也可以不去捕获,而检查程序时出现的异常(如SQLException、IOException、ConcurrentModificationException等)则必须要去捕获,因此Java中处理不同类型的错误或异常都有对应的异常实现类。

检查程序时出现的异常,这类异常是发生在编译期间的,编译器会去强制捕获异常,所以编码的时候必须要捕获异常。捕获异常的方式大致有两种:使用关键字throw或throws、使用try-catch-finally语句,使用这些关键字处理异常还是挺简单的,但捕获异常过程中的一些细节也值得注意。

throw或throws关键字

throw关键字:

用于方法体中,用来抛出一个实际的异常对象。使用throw抛自定义异常时,需要使用try-catch语句块 或者 throws关键字捕获异常,举例说明:

// throw定义异常 + throws捕获异常
public class TestController1 {
    public static void main(String[] args) throws CustomException {
        String strSpace = "123 4569";
        if (strSpace.contains(" "))
            throw new CustomException("自定义异常----->字符串不能有空格");
    }
}

// throw定义异常 + try-catch捕获异常
public class TestController2 {
    public static void main(String[] args){
        try {
            String strNull = null;
            if (strNull == null)
                throw new CustomException("自定义异常----->字符串不能为null");
        } catch (CustomException e) {
            e.printStackTrace();
        }
    }
}

// 自定义异常类
class CustomException extends Exception {
    public CustomException() {}
    public CustomException(String msg) {super(msg);}
    public CustomException(Throwable cases) {super(cases);}
}

throws关键字:

用于方法声明处,用来声明该方法可能发生的异常类型,可以有多个异常类型,用来强制调用该方法时处理这些异常。

try-catch-finally语句

一个完整的try-catch-finally结构如下:

try{
    //try语句块
}catch(SomeException1 e){
    //catch语句块1
}catch(SomeException2 e){
    //catch语句块2
}catch(SomeExceptionN e){
    //catch语句块n
}finally{
    //finally语句块
}

try语句块:

  • try块的作用是监控代码执行,当它捕获到异常后,跳转到catch块并交由catch块处理异常;
  • try块未捕获异常,会跳转到finally块继续执行代码。

catch语句块:

  • catch块处理try块捕获的异常,要么自己处理该异常,要么往上抛该异常;
  • catch块中根据异常对象e中的printStackTrace() 或 fillInStackTrace() 可以打印异常信息;
  • 可以有多个catch块,处理异常时按顺序匹配到哪一个就使用哪一个,一个catch块可以有多个异常,用|分割(Java7之后);
  • 多个catch块要注意异常子类间有无继承关系,可以合并为1个,合并后的异常可以代表所有的异常子类,如合并为Expection;
  • 对于运行时异常,可以省略不写catch块,但受检异常必须要写catch块。

finally语句块:

  • finally块无论是否发生异常均可省略不写,但写的话,里面的代码一定会得到执行(除非此时发生了系统退出 或 JVM退出);
  • finally块通常用于清理和释放资源,如关闭IO流,关闭JDBC连接等。

try-with-resource(带资源的try语句):

  • 它是Java7支持的功能,语法格式如下,注意要求的资源对象本身实现了AutoCloseable 接口,这样系统在执行时就会调用它们的close()方法关闭资源连接,代替了finally块关闭资源的功能;
try(资源语句){ 
    //监控执行代码; 
}catch{  
    //处理异常语句;
}
  • 带资源的try语句本质上也是一颗语法糖,通过下面的一个文件复制的例子来分析:
// TestController.java源码,使用文件输入输出流实现文件内容的复制
public class TestController {

    public static void main(String[] args) {
        // 文件内容复制
        copyFile("E:\\test\\today-log.txt","E:\\test\\yesterday-log.txt");
    }

    public static void copyFile(String src, String dst) {
        try (InputStream in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(dst)) {
            byte[] buf = new byte[1024];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用这种try带资源的语句可以简化原先冗长而又各种关闭资源的try-catch-finally语句,整个世界看着干净多了!经过编译的字节码文件是什么样的呢,来看下:

// TestController.class
public class TestController {
    public TestController() {
    }

    public static void main(String[] args) {
        copyFile("E:\\test\\today-log.txt", "E:\\test\\yesterday-log.txt");
    }

    public static void copyFile(String src, String dst) {
        try {
            InputStream in = new FileInputStream(src);
            Throwable var3 = null;

            try {
                OutputStream out = new FileOutputStream(dst);
                Throwable var5 = null;

                try {
                    byte[] buf = new byte[1024];

                    int n;
                    while((n = in.read(buf)) >= 0) {
                        out.write(buf, 0, n);
                    }
                } catch (Throwable var31) {
                    var5 = var31;
                    throw var31;
                } finally {
                    if (out != null) {
                        if (var5 != null) {
                            try {
                                out.close();
                            } catch (Throwable var30) {
                                var5.addSuppressed(var30);
                            }
                        } else {
                            out.close();
                        }
                    }

                }
            } catch (Throwable var33) {
                var3 = var33;
                throw var33;
            } finally {
                if (in != null) {
                    if (var3 != null) {
                        try {
                            in.close();
                        } catch (Throwable var29) {
                            var3.addSuppressed(var29);
                        }
                    } else {
                        in.close();
                    }
                }

            }
        } catch (Exception var35) {
            var35.printStackTrace();
        }

    }
}

从Java编译器编译生成的字节码可以看出:输入输出流的资源需要用finally块关闭,流先定义后关闭(先进后出)。整个用于处理业务的代码就几行,底层把精力主要放在了流的异常处理和资源释放上。

相比较try-catch-finally语句而言,带资源的try语句显得更加简洁和实用。


小结一下

本篇重点分析了Java中捕获异常的方式,及捕获过程中注意的一些细节,实际上更重要的是要懂得如何自定义异常。学以致用,想一想在项目中怎么去统一声明异常,有了这些基础就想必很简单了。关于异常使用的指导原则,推荐去看一看《Effective Java 中文版(原书第3版)》,这本书描述的还不错,不积硅步无以至千里,点滴付出终将有所收获,共同进步~

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值