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

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

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语法更优雅的关闭资源,消除板式代码。


相关文章

逆水寒手游龙吟怎么加点-龙吟技能加点攻略

在逆水寒手游中,龙吟是新门派职业,相信有很多玩家都转了该职业,为此有玩家想知道逆水寒手游龙吟怎么加点?对此有疑问的话,下面一起来看看逆水寒手游龙吟技能加点攻略吧!逆水寒手游龙吟加点推荐  一、搭配推荐  1、青龙怒、吟风、诛邪、白虹、听雨、残心、雷龙天翔和止戈为武。  2、斩霜、白虹、辉光、听雨、惊
2024-12-23 10:30:54

《全明星激斗(送满星八神庵)》最强霸榜攻略

游戏简介格斗作品的3D策略卡牌手游《全明星激斗(送满星八神庵)》立足于典角色的独特技能,将一招一式精心雕琢给广大粉丝们还原了原作中的华丽战斗。进入战斗后,将看到每名出战角色展现出自己独有的战斗交果!每一拳挥出都充满震撼且打击感十足,精心打磨后完美传承的角色技能将让广大格斗家们仿佛置身格斗之城,真实体
2024-12-23 10:16:04

战争怒吼什么种族后期最厉害

战争怒吼中,该游戏融入了多种种族选择,使玩家体验更加丰富。而在游戏初期至中期,各个种族的特色虽然明显,但在后期来看,哪个种族最有优势和实力呢?接下来就让我们一起来看看吧。游戏中一共有四个主要种族:人、精灵、矮人和亡灵。每个种族都有自己独特的特色和技能,而这些技能在后期的发展和优势上有着决定性的影响。
2024-12-23 10:01:01

放置江湖如何双开

放置江湖是一款非常受欢迎的游戏,玩家在游戏中可以通过集齐各种武林英雄,来组建自己的江湖团队。利用团队来挑战各种关卡和BOSS,这种体验是一种令人兴奋的战斗。然而对于很多玩家来说,放置江湖的单开模式,已经难以满足他们的需求。他们想要在同一台设备上进入两个不同的游戏账号,以便在游戏中更好地交流和合作,
2024-12-23 09:46:26

《国战来了》细节玩法全解析,新老玩家进阶指南

《国战来了》是一款超燃的三国策略手游。以经典三国为蓝本,逼真重现诸多战役。融合模拟经营、策略对战、角色扮演与副本探秘等多元玩法,让你尽情施展智谋。更有独特灭国战争机制,体验生死较量。内部哲蔻+v:twk697《国战来了》游戏攻略七日任务今日任务:务必完成七日任务中的今日任务,任务难度适中且奖励丰厚。
2024-12-23 09:31:21

《死战骑士团》新手的鬼灭崛起之路,深度剖析玩法细节

《死战骑士团》是基于人气动漫《鬼灭之刃》改编而成的角色扮演类卡牌游戏。踏入此游戏世界,仿若置身热血动漫之境,熟悉经典角色纷至沓来。借抽卡之趣,集角色碎片,终得心仪人物相伴。化身“灭鬼剑士”,无畏未知挑战,踏上探索征程,每一步皆有惊喜,每一战俱是考验,在这奇幻冒险里,书写独属于自己的灭鬼传奇,畅享策略
2024-12-23 09:15:59

热门文章

在村BA附近发现一个好玩的地方

法兰的分类及用途 ,解开我多年的疑惑

湖南省的简称? 湖南省的十四个地区

体验现代悦动:跌至7.5万,外观性能设计,油耗6L,质量不错

最新文章

农学考研有哪些科目(农业硕士8大专业难度分析其实没那么简单)

艺术片电影推荐有哪些(十部经典艺术电影)

漂亮的反义词是什么(三上语文第四单元知识点总结及综合素养评价)

关于教师节的歌有什么(教师之歌 8首)

水晶吊灯坏了怎么修(水晶灯有多美丽就多易损坏的?)

华东五市地图上的位置(华东六省一市座次图)

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