当前位置: 首页 > 科技观察

教女生学Java:异常处理机制

时间:2023-03-20 13:55:52 科技观察

“二哥,今天要学异常吗?”三姐问道。“是的。只有妥善处理异常,才能保证程序的可靠性,所以学习异常还是很有必要的。”我说。“那什么是异常?”三姐问道。”异常是指中断程序正常执行的不确定事件。当异常发生时,程序的正常执行流程就会被打断。一般来说,一个程序会有很多条语句。如果没有异常处理机制,上一条语句一旦发生异常,后面的语句就不能继续执行。”“有了异常处理机制,程序在发生异常时不会中断。我们可以捕获异常,然后改变程序执行的流程。”“另外,异常处理机制可以保证我们为用户提供友好的提示信息而不是程序原来的异常信息——用户根本看不懂。”“不过,站在开发者的角度,我们更希望看到原始的异常信息,因为这样可以帮助我们更快地找到bug的根源,但是过度包装的异常信息会干扰我们的视线。”“Java语言从一开始就提供了比较完善的异常处理机制,这种机制大大降低了编写可靠程序的门槛,这也是Java受欢迎的原因之一。”“那程序抛出异常的原因是什么?”三妹问道。例如:程序试图打开一个不存在的文件;该程序遇到网络连接问题;用户输入了错误数据;该程序在进行算术运算时不考虑除数为0;等等。让我们选择最简单的原因。publicclassDemo{publicstaticvoidmain(String[]args){System.out.println(10/0);}}这段代码运行时抛出的异常信息如下:Exceptioninthread"main"java.lang.ArithmeticException:/byzeroatcom.itwanger.s41.Demo.main(Demo.java:8)"三妹你看,这个原始的异常信息对于用户来说显然不容易看懂,但是对于我们开发者来说,简直不要太直白——很容易定位异常的来源。”“哦,我知道了,下一个问题,我经常看到一些文章中提到Exception和Error,兄弟,你能给我解释一下吗,你想知道它们之间的区别吗?”三姐问道。“这个问题问得好,三妹!”从词的定义来看,error是error,exception是exception,error的级别明显高于exception。从程序上看,确实如此。Error的出现说明程序存在严重的问题,这些问题不应该交给Java的异常处理机制。程序应该直接崩溃,比如OutOfMemoryError,内存溢出,这意味着程序在运行时申请的内存大于系统能够提供的内存时,就会发生错误,这对程序来说是致命的。Exception的出现说明程序在可控范围内出现了一些问题,我们应该采取措施挽救。比如前面提到的ArithmeticException,很明显是因为除数有0,我们可以选择捕获异常,然后提示用户不要除以0,当然直接判断除数更好。如果为0,则不进行除法运算,而是告知用户换成非零数进行运算。“三姐,你能想到别的问题吗?”“嗯,二哥你别想了,我已经提前做好了预演工作。”三妹自信道:“异常分checked和unchecked两种,它们有什么区别?”“哇,三姐,这还真是个好问题。”检查异常(checkedexceptions)必须在源代码中显式捕获或抛出,否则编译器会提示你执行相应的操作;非检查异常(non-checkedexceptions)就是所谓的运行时异常,通常可以通过什么来规避coding不需要显式捕获或者抛出。”我画个思维导图给大家感受一下。”首先,Exception和Error都继承了Throwable类。换句话说,只有Throwable类(或子类)的对象才能使用throw关键字抛出,或者作为catch的参数类型。面试中经常被问到的一个问题是,NoClassDefFoundError和ClassNotFoundException有什么区别?“三姐你知道吗?”“我不知道,请二哥解释一下。"都是系统在运行时找不到要加载的类引起的,只是触发原因不同。NoClassDefFoundError:程序在编译时可以找到自己依赖的类,但是指定的类文件找不到运行时发现,可能会抛出错误;原因可能是缺少jar包或调用了初始化失败的类。loaded;原因可能是要加载的类不存在或者类名错误。其次,像IOException、ClassNotFoundException、SQLException都是checked异常;像RuntimeException及其子类ArithmeticException、ClassCastException、ArrayIndexOutOfBoundsException、NullPointerException,都是unchecked异常.uncheckedexceptions不能在程序中显示,就像之前提到的ArithmeticException只是那;但必须显式处理已检查的异常。例如下面这行代码:Classclz=Class.forName("com.itwanger.s41.Demo1");如果不处理,比如在IntellijIDEA环境下,会提示你这行代码可能会抛出java。郎。类未找到异常。推荐使用try-catch捕获:try{Classclz=Class.forName("com.itwanger.s41.Demo1");}catch(ClassNotFoundExceptione){e.printStackTrace();}注意printStackTrace()方法,该方法会将异常的堆栈信息打印到标准控制台,如果是测试环境,这种写法是可以的,如果是生产环境,这种写法不可取,必须使用日志框架将异常打印堆栈信息输出到日志系统,否则可能没有办法追踪。要么在方法签名上使用throws关键字来抛出:publicclassDemo1{publicstaticvoidmain(String[]args)throwsClassNotFoundException{Classclz=Class.forName("com.itwanger.s41.Demo1");}}'需要将异常捕获并处理,只需要交给Java虚拟机处理即可;缺点是没有办法对这种情况进行相应的处理。”二哥,对于checked异常,我在知乎上看到一个帖子说在Java中checked是不需要的,这种异常要么是try-catch,要么是编译时throw,但不一定会引发异常,你是不是觉得这样设计有意义吗?”三妹问了一个很尖锐的问题。“哇,这是个好问题。”我不禁对三姐产生了敬佩之情。“确实,checkedexception在业界是有争议的,它假设我们已经捕获到异常并进行相应的处理,但有时根本无法处理。”我说,“以上面提到的ClassNotFoundException为例。我们假设我们已经尝试过捕获它,但是在ClassNotFoundException真正发生之后,我们没有太多的可操作性。然后Class.forName()一次?另外,checkedexception不兼容函数式编程,以后写Lambda/Stream代码的时候就会体验到这种苦涩。当然checkedexception也不是没用,尤其是遇到IO或者网络异常的时候,比如Socket连接,我大致写了一段:publicclassDemo2{privateStringmHost;privateintmPort;privateSocketmSocket;privatefinalObjectmLock=newObject();publicvoidrun(){}privatevoidinitSocket(){while(true){try{Socketsocket=newSocket(mHost,mPort);同步(mLock){mSocket=socket;}break;}catch(IOExceptione){e.printStackTrace();}}}}当出现IOException时,socket会重新尝试连接,否则会跳出循环。也就是说如果IOException不是checkedexception,这种写法有点生硬,因为IOException不能使用if语句判断除数是否为0,避免类似ArithmeticException。也就是说,强制检查异常让我们有时间思考如何更优雅地处理这种异常。显然,在Socket编程中,肯定会遇到IOException。如果IOException是一个未经检查的异常,则意味着开发人员可以忽略它。直接跳过,交给Java虚拟机去处理,但我觉得肯定是比较不合适的。三姐:“好了,二哥你去休息吧。”“对了,三姐,我给姑姑叫个外卖,晚上我们一起喝粥。”“好,我要两个豆沙包。”》本文转载自微信公众号“沉默王二”,您可以通过以下二维码关注。转载本文请联系沉默王二公众号。