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

深入探讨Java中的异常和错误处理

时间:2023-03-18 00:30:36 科技观察

Java中的异常处理机制已经比较成熟,我们的Java程序处处充斥着异常。如果不提前处理这些异常,那么以后程序就崩溃了,无法调试,很难找到异常所在的位置。本文将讨论Java中异常和错误的处理,一起来看看吧。异常与错误:异常:Java中的程序错误主要是语法错误和语义错误。程序编译运行时出现的错误统称为异常。这是VM(虚拟机)通知您的一种方式,通过这种方式VM让您知道您(开发人员)犯了一个错误,现在有机会修复它。Java中用异常类来表示异常,不同的异常类代表不同的异常。但是Java中的所有异常都有一个名为Exception的基类。Error:指的是合理的应用程序无法拦截的严重问题。大多数是异常情况。错误是VM的故障(尽管它可能是任何系统级服务)。因此,错误很难处理,普通开发人员(当然不是你)无法处理这些错误,例如内存溢出。和异常一样,错误在Java中也是用错误类来表示的,不同的错误类代表不同的错误。但是Java中的所有错误都有一个名为Error的基类。综上所述,我们可以知道,异常和错误最本质的区别是,异常可以由开发人员来处理,而错误是系统本身自带的,一般是无法处理的,不需要我们程序员来处理。1.异常是程序执行过程中发生的打断正常指令执行的事件2.错误,偏离可接受的代码行为的动作或实例异常的结构分类:1.运行时异常(Uncheckedexception)2.编译时异常(checkedexception)运行异常是RuntimeException;其余的都是编译异常。在Java中,异常Exception和errorError有一个共同的父类Throwable。ErrorExceptionruntimeException的几个子类1.java.lang.ArrayIndexOutOfBoundsException数组索引越界异常。当数组的索引为负数或大于或等于数组大小时抛出。2.java.lang.ArithmeticException运算条件异常。例如:整数除以零等。3.java.lang.NullPointerException空指针异常。当应用程序尝试在需要对象的地方使用null时,会抛出此异常。例如:调用null对象的实例方法,访问null对象的属性,计算null对象的长度,使用throw语句抛出null等。4.java.lang.ClassNotFoundException找不到类异常.当应用程序试图根据字符串形式的类名构造类,但遍历CLASSPAH后找不到对应名称的类文件时抛出该异常。异常处理:不管有没有异常,try{}catch{}try{}catch{}finally{}都会执行。try{}finally{}也可以组合使用,但catch{}finally{}不能注意:在继承关系中,子类重写了父类的方法,抛出异常的范围不能比父类。异常的使用主要是一个演示代码,也就是我们平时在写代码的过程中写的。遇到过(当然只是一小部分),抛砖引玉!例1.本例主要通过两种方法的对比,演示异常发生后代码的执行过程。publicstaticvoidtestException1(){int[]ints=newint[]{1,2,3,4};System.out.println("异常发生前");try{System.out.println(ints[4]);System.out.println("我还是幸运地执行了它");//发生异常后无法执行下面的代码}catch(IndexOutOfBoundsExceptione){System.out.println("数组越界错误");}System.out.println("异常发生后");}/*输出:异常发生前,经常出现数组越界错误*/publicstaticvoidtestException2(){int[]ints=newint[]{1,2,3,4};系统。out.println("异常发生前");System.out.println(整数[4]);System.out.println("我还幸运地执行了吗?");//发生异常后,他后面的代码不能执行是不需要try...catch...来显示捕获的,但是我的目的是对同一个异常使用不同的处理方式,看看会有怎样的不同和结果(只能用在这里)。当异常发生时,第一个方法只是跳出try块,但后面的代码仍然会被执行。但是第二个不同,直接跳出法门,更加硬朗。从第一个方法我们可以看出try...catch...是一种“事务性”的保证。其目的是保证程序在异常情况下运行完成,同时也会将程序中的错误告知程序员。详细信息(这种详细信息有时取决于程序员的设计)。示例2.重新抛出异常tknowhowtohandle异常可能根本不处理,但是不处理也不合适,这是重新抛出异常,交给上层");//重新抛出异常throwe;}}publicstaticvoidprintFile(Stringfile){try{readFile(file);}catch(FileNotFoundExceptione){e.printStackTrace();}}publicstaticvoidmain(String[]args){printFile("D:/file");}}异常的用意是好的,让我们尝试修复程序,但实际上,我们修复的机会很小,我们经常用它来记录错误信息。如果您厌倦了一直处理异常,那么重新抛出异常可能会让您松一口气。把这个异常原封不动的扔给上层,扔给调用这个方法的人,让他多花点脑筋。由此看来,java异常(当然是指checkedexceptions)给我们带来了很多麻烦,虽然它的出发点是好的。例3.异常链的使用和异常丢失ExceptionA,ExceptionB,ExceptionC(Stringstr)str);}}异常缺失:publicclassNeverCaught{staticvoidf()throwsExceptionB{thrownewExceptionB("exceptionb");}staticvoidg()throwsExceptionC{try{f();}catch(ExceptionBe){ExceptionCc=newExceptionC("exceptiona");throwc;}}publicstaticvoidmain(String[]args){try{g();}catch(ExceptionCe){e.printStackTrace();}}}/*exception.ExceptionCateexception.NeverCaught.g(NeverCaught.java:12)atexception.NeverCaught.main(NeverCaught.java:19)*/为什么只打印ExceptionC,不打印ExceptionB?这个大家自己分析吧!上面的情况相当于漏掉了一种异常,这在我们排查过程中是非常不利的。那么遇到上述情况我们应该怎么办呢?这就是异常链派上用场的地方:保存异常信息,在不丢失原来异常的情况下抛出另一个异常。publicclassNeverCaught{staticvoidf()throwsExceptionB{thrownewExceptionB("exceptionb");}staticvoidg()throwsExceptionC{try{f();}catch(ExceptionBe){ExceptionCc=newExceptionC("exceptiona");//异常连接c.initCause(e);throwc;}}publicstaticvoidmain(String[]args){try{g();}catch(ExceptionCe){e.printStackTrace();}}}/*exception.ExceptionCateexception.NeverCaught.g(NeverCaught.java:12)atexception.NeverCaught.main(NeverCaught.java:21)Causedby:exception.ExceptionBatexception.NeverCaught.f(NeverCaught.java:5)atexception.NeverCaught.g(NeverCaught.java:10)...1more*/这个异常chain具备所有异常的特性,因为initCause()方法继承自Throwable。Example4.Cleanupwork清理工作对我们来说是必不可少的,因为如果是一些比较耗资源的操作,比如IO,JDBC。如果我们在用完之后不及时适当的关闭它,后果会很严重,那就是内存泄漏。异常的出现需要我们设计一种机制,在任何情况下都能及时正确的清理资源。这是终于。publicvoidreadFile(Stringfile){BufferedReaderreader=null;try{reader=newBufferedReader(newInputStreamReader(newFileInputStream(file)));//dosomeotherwork}catch(FileNotFoundExceptione){e.printStackTrace();}finally{try{reader.close();}catch(IOExceptione){e.printStackTrace();}}}例子很简单,就是一个读取文件的例子。这样的例子在JDBC操作中也很常见。(所以,我认为及时正确的清理资源是一个程序员的基本素质之一。)Try...finally结构也是保证资源正确关闭的一种手段。如果不知道代码执行过程中会出现什么异常情况,无法清理资源,那么可以用try包裹这段“可疑”的代码,然后在finally中清理资源。举个例子:publicvoidreadFile(){BufferedReaderreader=null;try{reader=newBufferedReader(newInputStreamReader(newFileInputStream("file")));//dosomeotherwork//closereaderreader.close();}catch(FileNotFoundExceptione){e.printStackTrace();}catch(IOExceptione){e.printStackTrace();}}我们要注意这个方法和前面方法的区别。下一个人可能会更好地习惯它并尽早关闭阅读器。但往往事与愿违,因为在reader.close()之前随时可能发生异常,这样的代码结构并不能阻止任何异常的发生。因为程序会在异常发生的地方跳出,所以下面的代码是不能执行的(这个应该可以通过上面的例子来证明)。这时候我们可以使用try...finally来改造一下:}最后{读者。close();}}catch(FileNotFoundExceptione){e.printStackTrace();}catch(IOExceptione){e.printStackTrace();}}早点关闭资源是一个好的行为,因为你忘记关闭的时间越长,它就越有可能是。这样try...finally保证配合起来万无一失(别嫌麻烦,java就是这么乖)。还有一种情况,如果我想在构造方法中打开一个文件或者创建一个JDBC连接,因为我们要在其他方法中使用这个资源,我们不能在构造方法中提前关闭这个资源。那么我们会迷路吗?答案是不。看看下面的例子:publicclassResourceInConstructor{BufferedReaderreader=null;publicResourceInConstructor(){try{reader=newBufferedReader(newInputStreamReader(newFileInputStream("")));}catch(FileNotFoundExceptione){e.printStackTrace();}}publicvoidreadFile(){try{while(reader.readLine()!=null){//dosomework}}catch(IOExceptione){e.printStackTrace();}}publicvoiddispose(){try{reader.close();}catch(IOExceptione){e.printStackTrace();}}}这部分有点多,但是异常确实是一些看似好用实则难用的东西。java中还有很多东西需要深挖。