前言要创建一个健壮的系统,它的每个组件都必须是健壮的。应用程序能够在正常情况下正确运行是程序的基本要求。但是一个健壮的程序需要考虑很多会使程序失效的因素,即必须能够在异常情况下进行必要的处理。程序是程序员写的,程序员有思维盲区。一个合格的程序员可以保证Java程序不会出现编译错误,但不能“考虑完整性”来保证程序运行时不会出现错误,而这些运行时错误对于Java来说是一种“异常”。异常处理的目的是通过使用少于当前数量的代码来简化大型、可靠程序的生成,这样,您可以更加确信您的应用程序中没有未处理的错误。异常并不难学,它是可以立即明确地使您的项目受益的特性之一。异常的概念就像“天有不测风云,人有祸福”。在特定的环境中,Java程序代码中会出现一些意想不到的情况。即使安排了专业的软件测试人员,也只能减少错误,不能避免错误。也就是说,理论上,在使用软件的过程中,不可预知的异常是不可避免的。因此,在编写代码的过程中,程序员应该做两件事:第一,尽量减少错误。二是发挥主观能动性,事后考虑如何处理,事前防患于未然。前者依赖于程序员的积累,而后者则是更成熟的“灾后处理”范式。所谓异常,是指一切可能导致计算机无法正常处理的情况。如果事先没有妥善安排,严重时会导致电脑死机。异常分类在Java中,异常可以分为两类:java.lang.Exception和java.lang.Error。这两个类都继承自java.lang.Throwable类。下图是Throwable类的继承关系图。Error类和Exception类统称为异常类,但两者在本质上还是有区别的。Error类通常是指Java虚拟机(JVM)中的一个错误,用户在程序中无法处理。如果程序启动时出现Error,则启动失败。Exception类包含一般的异常,捕获后可以进行适当的处??理,以保证程序继续运行。这么多不同的异常类,并没有比Throwable基类多多少属性和方法。大多数类在继承父类后只定义了几个构造方法,而这些构造方法只是调用了父类的构造方法,并没有额外的操作。那么为什么要定义这么多不同的类呢?主要是叫法不同。异常类的名称本身就代表了异常的关键信息。无论是抛出异常还是捕获异常,使用合适的名称都有助于代码的可读性和可维护性。异常处理在编程的过程中,有一个80/20的原则,即80%的精力花在20%的事情上,这20%的事情就是处理各种可能出现的错误或异常。异常处理有两种方法:抛出异常和使用trycatch语句块捕获并处理异常。抛出异常时,遇到异常时不进行具体处理,而是将异常抛给调用者,调用者根据情况进行处理。可能直接捕获处理,也可能继续向上层抛异常。抛出异常有三种形式:throws、throw、系统自动抛出异常。其中throws作用于方法,用于定义方法可能抛出的异常;throw作用于方法,表示显式抛出异常。具体使用方法如下:在throw方法中抛出异常代码示例privatestaticbase64toInt(charc,byte[]alphaToInt){intresult=alphaToInt[c];if(result<0){thrownewIllegalArgumentException("非法字符"+c);}返回结果;}在throws方法上抛出异常代码示例publicvoidconfigure(HttpSecurityhttp)throwsException{http.csrf().disable();http.headers().frameOptions().disable();authorizeConfigManager.config(http.authorizeRequests());}throw和throws的区别如下。位置不同:throws作用于方法,后面是异常类;whilethrow作用于方法,后跟异常对象。功能不同:throws用于声明方法运行过程中可能发生的异常,以便调用者根据不同的异常类型预先定义不同的处理方法;throw用于抛出封装了异常信息的对象,当程序执行throw后,后面的代码将不再执行,而是跳转到调用者处,将异常信息抛给调用者。也就是说throw之后的语句块不会被执行(finally语句块除外)。捕获异常使用trycatch捕获并处理异常:使用trycatch捕获异常有针对性地处理每一种可能出现的异常,捕获到异常后根据不同的情况做不同的处理。它的使用过程比较简单:将可能引发异常的代码用一个trycatch语句块包裹起来。其中,只有一抓。实际上,可以有多个catch,每个catch对应一个异常类型。具体使用方法如下:try{//可能触发异常的代码}catch(NumberFormatExceptione){System.out.println("notvalidnumber");}catch(RuntimeExceptione){System.out.println("运行时异常"+e.getMessage());}catch(Exceptione){e.printStackTrace();}}异常机制的一个重要部分是finally。catch后面可以跟一个finally语句,语法如下:try{//可能会抛出异常}catch(Exceptione){//捕获异常}finally{//不管是否有异常都执行}的finally中的代码如果发生异常,就会执行。具体来说:如果没有异常发生,会在try中的代码执行完后执行。如果异常发生并被catch捕获,则在catch中的代码执行完毕后执行。如果发生异常但没有被捕获,则在向上层抛出异常之前执行。由于finally的这个特性,一般用来释放资源,比如数据库连接、文件流等。在try/catch/finally语法中,不需要catch,即可以只有try/finally,表示不捕获异常,自动向上传递异常,但是finally中的代码也是在之后执行发生异常。finally语句有一个执行细节。如果try或catch语句中有return语句,则在执行完finally语句后会执行return语句,但finally不能改变返回值。对于一些使用资源的场景,比如文件、数据库连接等,典型的使用流程是先打开资源,最后在finally语句中调用资源的关闭方法。针对这种场景,Java7开始支持一种新的语法,叫做Fortry-with-resources,这种语法针对的是实现了java.lang.AutoCloseable接口的对象,定义为:publicinterfaceAutoCloseable{voidclose()throwsException;}当没有try-with-resources时,使用形式如下:publicstaticvoiduseResource()throwsException{AutoCloseabler=newFileInputStream("hello");//创建资源try{//使用资源}finally{r.close();}}使用try-with-resources语法,形式如下:publicstaticvoiduseResource()throwsException{try(AutoCloseabler=newFileInputStream("hello")){//创建资源//使用资源}}的资源r的声明和初始化都放在try语句中,不需要再调用finally,语句执行完try语句后,会自动调用资源的close()方法。可以定义多个资源,用分号分隔。在Java9之前,必须在try语句块中声明和初始化资源。Java9取消了这个限制。资源可以在try语句之外声明和初始化,但它们必须是final或事实上的final。处理逻辑知道如何处理异常,就可以处理;如果可以通过程序自动解决,就可以自动解决;如果异常可以自行解决,则无需上报。如果自己不能完全解决,应该向上报告。如果你有额外的信息可以提供来帮助分析和解决问题,你应该提供它,你可以重新抛出一个以原来的异常为原因的异常。总是有一层代码负责异常,它可能是知道如何处理异常的代码,可能是面向用户的代码,也可能是主程序。若无法自动解决异常,则应根据异常信息为用户提供用户能够理解且对用户有帮助的信息;对于运维和开发人员来说,应该将详细的异常链和异常栈输出到日志中。这个逻辑类似于公司处理问题的逻辑。每个级别都有自己需要解决的问题。能自己解决的,就应该自己处理。如果解决不了,就向上级汇报,下级把知道的告诉他。同时告诉你的上级,到头来,所有的问题都得由公司老大负责。各级既不能隐瞒问题,也不能推卸责任。异常准则应该在以下情况下使用异常:尽可能使用try-with-resource。在适当的级别处理问题。(仅当您知道如何处理异常时才捕获异常。)解决问题并重新调用引发异常的方法。稍微修复一下,然后在发生异常的地方继续。使用其他数据而不是方法期望返回的值执行计算。尽可能在当前的运行环境下做,然后在更高层重新抛出相同的异常。尽可能在当前的运行环境下做,然后往更高的层次抛出不同的异常。终止程序。简化。(如果您的异常模式使事情变得过于复杂,使用起来可能会很痛苦和烦人。)使库和程序更安全。(这是对调试的短期投资,也是对程序健壮性的长期投资。)总结异常是Java编程不可或缺的一部分,如果不了解如何使用它们,您能做的事情也就那么多了。异常处理的一个优点是它可以让你在一个地方专注于你试图解决的问题,而在另一个地方处理你编写的代码中出现的错误。因此,可以大大减少处理异常的代码,也可以提高代码的可读性、可靠性和可维护性。最后一篇为初学者提供学习指南,对从业者有参考价值。我坚信编码员也有能力产生洞察力。关注【码农洞察】,一起学习交流!
