您的位置: 首页 > 生活常识 >

程序员必须掌握的异常处理技巧和原则

748次浏览     发布时间:2024-03-19 19:19:16    

Java中的异常机制是指:当程序在运行过程中遇到意外情况时会自动抛出一个Exception对象来通知程序,程序收到这个异常通知后可以采取各种处理措施,这种机制能使程序更加健壮,可读性更高。本文就来讲讲异常处理的相关知识。


异常分类#

Java中的异常分为RuntimeException和CheckedException。

  • RuntimeException:程序运行过程中出现错误,才会被检查的异常。例如:类型错误转换,数组下标访问越界,空指针异常、找不到指定类等等。
  • CheckedException:来自于Exception且非运行时异常都是检查异常,编译器会强制检查并通过try-catch块来对其捕获,或者在方法头声明抛出该异常,交给调用者处理。常见的checked异常有FileNotFoundException和InterruptedException等。

Error和Exception的区别#

在谈到Exception时,经常会涉及到Error。Error和Exception存在如下的区别:

Error是指系统中的错误,程序员是不能改变和处理的,是在程序运行时出现的错误,只能通过修改程序才能修正。Java中的Error一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止,调整代码或者虚拟机参数再重新启动程序;

Exception(异常)是程序可以处理的。遇到这类异常,程序员应该尽可能捕获处理异常,使程序恢复运行,而不应该随意终止异常。实在不知道如何处理就向上抛出该异常留给调用者处理。

总结下:异常(Exception)是一种非程序原因的操作失败(Failure),而错误(Error)则意味着程序有缺陷(Bug)。

异常处理的原则#

1. 能处理的异常尽早处理
对于能明确知道要怎么处理的异常要第一时间处理掉。对于不知道要怎么处理的异常,要么直接向上抛出,要么转换成RuntimeException再向上抛出,让调用者处理。

2. 具体明确原则
尽量不要用Exception捕获抛出所有异常。抛出异常时需要针对具体问题来抛出异常,抛出的异常要足够具体详细;在捕获异常时需要对捕获的异常进行细分,这时会有多个catch语句块,这几个catch块中间泛化程度越低的异常需要越放在前面捕获,泛化程度高的异常捕获放在后面,这样的好处是如果出现异常可以近可能得明确异常的具体类型是什么。

抛出时:

Copy
public FileInputStream(File file) throws FileNotFoundException {
    String name = (file != null ? file.getPath() : null);
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkRead(name);
    }
    //根据不同的情况抛出不同的异常
    if (name == null) {
        throw new NullPointerException();
    }
    if (file.isInvalid()) {
        throw new FileNotFoundException("Invalid file path");
    }
    ...
}

捕获时:

Copypublic void foo1(String fileName){
    File file = new File(fileName);
    InputStream in = null;
    try {
        in = new FileInputStream(file);
        Integer num = in.read();
        in.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e){
        e.printStackTrace();
    } catch(Exception e){
        e.printStackTrace();
    }
}

3. 系统需要有统一异常处理机制
系统需要有自己的一套统一异常处理的机制,如果系统使用Spring等框架的话可以非常简单地引入统一异常处理框架。另外在统一异常处理时一定要打出异常堆栈,不然的话问题可能就无从查起了。

4. 一些其他注意点

  • try语句块内要分清稳定代码和非稳定代码,对于稳定的不会出现异常的代码不要放到try语句块中;
  • catch捕获的异常一定要处理,吃掉异常不处理的话将是灭顶之灾;
  • finally中不要使用return语句,因为finally语句块最后一定会执行,这里的return语句会覆盖之前的return语句。

如何自定义异常#

在复杂业务环境下,java自带的异常可能满足不了我们业务的需求, 这个时候我们可以自定义异常来进行对业务异常的处理。

Copypublic class MyException extends RuntimeException {

    private static final long serialVersionUID = 6958499248468627021L;
   
    private String errorCode;
    
    private String errorMsg;

    public MyException(String errorCode,String errorMsg){
        super(errorMsg);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

    public MyException(String errorCode,String errorMsg,Throwable throwable){
        super(errorCode,throwable);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
}

在使用MyException时,最好定义一个枚举类用来枚举错误代码和错误详情。

return和finally的执行顺序#

在Java中,异常处理主要由try、catch、throw、throws和finally5个关键字处理。Java程序中如果出现了异常,而且没有被try-catch块捕获的话,系统会把异常一直往上层抛,直到遇到处理代码。如果一直没有处理块,就抛到最上层,如果是多线程就由Thread.run()抛出,如果是单线程就被main()抛出。抛出之后,如果是子线程,这个子线程就退出了。如果是主程序抛出的异常,那么这整个程序也就退出了。

一个比较常见的异常处理流程如下:

Copy    try {
        //step1
        System.out.println("try...");
        throw new RuntimeException("异常1...");
    }catch (Exception e){
        //step2
        System.out.println("catch。。。");
    }finally {
        //step3
        System.out.println("finally。。。");
    }
    //step4
    System.out.println("end...");

上述的代码中由于抛出的异常被顺利catch住了,所以当前线程不会结束,程序会继续往下执行,step4这步代码会被打印出来。

Copy    try {
        System.out.println("try...");
        throw new RuntimeException("异常1...");
    }catch (Exception e){
        System.out.println("catch。。。");
        throw new RuntimeException("异常2...");
    }finally {
        System.out.println("finally。。。");
    }
    System.out.println("end...");

上面的代码中,由于catch块中又抛出了一个异常,而这个异常没有相应的catch块处理,所以系统会向上抛这个异常,最后的打印语句也就的不到执行。

try、catch、finally、throw和throws使用归纳

  • try、catch和finally都不能单独使用,只能是try-catch、try-finally或者try-catch-finally。
  • try语句块监控代码,出现异常就停止执行下面的代码,然后将异常移交给catch语句块来处理,catch块执行完之后代码还会继续往下执行
  • finally语句块中的代码一定会被执行,常用于回收资源 。
  • throws:声明一个异常,告知方法调用者。
  • throw :抛出一个异常,至于该异常被捕获还是继续抛出都与它无关。

还有一个比较重要的是return和finally的执行关系,可以参考下这篇博客

异常链#

在平时的开发中,常常会在捕获一个异常后抛出另外一个自定义异常,并且希望把异常原始信息保存下来,这被称为异常链。我们在自定义异常时,只要提供一个接收throwable参数的构造函数即可:

Copypublic MyException(String errorCode,String errorMsg,Throwable cause){
    super(errorCode,cause);
    this.errorCode = errorCode;
    this.errorMsg = errorMsg;
}

try-with-resources#

我们知道,在Java编程过程中,如果打开了外部资源(文件、数据库连接、网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们。因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在编程时确保在正确的时机关闭外部资源,就会导致外部资源泄露,紧接着就会出现文件被异常占用,数据库连接过多导致连接池溢出等诸多很严重的问题。

  1. 传统的资源关闭方式
Copy//这种方式关闭资源,代码显得比较臃肿
public static void main(String[] args) {
    FileInputStream inputStream = null;
    try {
        inputStream = new FileInputStream(new File("test"));
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    }
}
  1. try-with-resources方式关闭资源
Copypublic static void main(String[] args) {
    try (FileInputStream inputStream = new FileInputStream(new File("test"))) {
        System.out.println(inputStream.read());
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

将外部资源的句柄对象的创建放在try关键字后面的括号中,当这个try-catch代码块执行完毕后,Java会确保外部资源的close方法被调用。代码是不是瞬间简洁许多!当一个外部资源的句柄对象实现了AutoCloseable接口,JDK7中便可以利用try-with-resource语法更优雅的关闭资源,消除板式代码。


相关文章

平台逾期有什么影响(信用卡、网贷逾期后的四个后果)

信用卡、网贷逾期会不会坐牢?"信用卡、网贷逾期后面临的"四个"后果。信用卡网贷逾期会不会坐牢?收到一个粉丝的私信很有意思,问我信用卡逾期的后果是什么?催收说要进行刑事诉讼,是不是真的?我会不会因为这样子而坐牢?在这里今天小猪哥完完整整的整理了一个视频,帮助需要的朋友了解一下,逾期后会面临的情况及问题
2025-06-07 02:08:53

养老年金是什么保险种类(有社保了还有必要买养老年金吗?)

一、什么是养老年金险?什么是什么是养老年金险?简单来说,就是按照一定年限或者金额给保险公司交保费,保险公司到了合同约定的年龄,按照合同约定的金额给付被保险人生存金的一个险种,可以是按月给付,也可以是按年给付。由于它的生存保险金的给付,通常采取的是按年度周期给的方式,而且主要是用来养老的,因此称为养老
2025-06-07 00:04:31

蓝筹股是什么股票(什么是蓝筹股和红筹股?)

也许你经常会看到一些股市牛人每天更新大盘,用上的各种术语你是看得一头雾水,这样的话,即使再好再准的股评你也是看不明白的,等于这些信息将于你无关,所以作为新手,一定要入乡随俗把一些基本的股市术语理解了,才能在股市里有更好的交流。下面就全面的给大家讲解一些基本的术语。蓝筹股:蓝筹股是指稳定的现金股利政策
2025-06-06 14:34:43

利息1分是什么意思(民间借贷利息约定几分以下,才能受法律保护?)

利息和利率,是在民间借贷中很多朋友关心但又不清楚相关计算方法及法律规定的问题。1、利率和利息是如何计算的呢?利率与利息是两个不同的概念。利率,是“利息率”的简称,就是指一定期限内利息额与本金的比率。利率通常分为三种表示方式,年利率、月利率、日利率。年利率以本金的百分之几(%)表示,月利率以千分之几(
2025-06-06 14:07:53

兰花为什么没开就干了(兰花叶夹过早干枯通常由以下原因导致)

空气干燥或浇水不及时,叶甲会因缺水干枯发黄。需保持环境湿度(如喷雾、放湿盆),避免过度干旱。高湿环境下通风差,叶甲易闷湿腐烂变黑。需加强空气流通,避免闷湿环境。茎腐病等病菌从根部蔓延,叶甲最先变黑枯萎。需剪除病叶,喷杀菌剂(如多菌灵),并检查根部是否腐烂。老苗叶甲自然退化属正常现象,但新苗若过早干枯
2025-05-09 11:26:51

如何实现黑神话悟空如何全物品全通关

随着《黑神话:悟空》这款国产游戏巨作的横空出世,它不仅以其精美的画面、深厚的文化底蕴以及极具挑战性的游戏玩法赢得了广大玩家的热烈追捧,更在全社会范围内掀起了一股西游文化的热潮。更令人兴奋的是,这股热潮还巧妙地与文旅产业相结合,为玩家们带来了前所未有的福利——通关《黑神话:悟空》即可享受多地景区的免费
2025-05-08 03:36:47

网站内容来自网络,如有侵权请联系我们,立即删除!
Copyright © 阔百科 琼ICP备2023010365号-4