常见异常处理

来源:互联网 发布:莫知我哀 影九 小说 编辑:程序博客网 时间:2024/05/20 17:27
package pack.java.demo;   import java.util.HashMap;   java 中 关于java.lang.ArrayStoreException: java.lang.Integer异常,是什么原因?import java.util.Map;     public class Test {       /**       * @param args       */      public static void main(String[] args) {           // TODO Auto-generated method stub           Map<String, Object> map = new HashMap<String, Object>();           map.put("A", 12);           map.put("B", "SAP");           map.put("C", 'ÖÐ');                      String[] keyArr = map.keySet().toArray(new String[map.size()]);           for(int i=0;i<keyArr.length;i++){               System.out.println(keyArr[i]);           }                      System.out.println("");                      Object[] valueArr = map.values().toArray(new String[map.size()]);           for(int i = 0;i<valueArr.length;i++){               System.out.println(valueArr[i]);           }       }   }  --------------------------------------------------------------------------------------------------   Value的值是Object型,要装到Object[]数组中,而不是String[]。 往数组里装不匹配的类型,就抛这个异常。   package pack.java.demo;     import java.util.HashMap;     import java.util.Map;          public class Test {         /**        * @param args        */         public static void main(String[] args) {             // TODO Auto-generated method stub             Map<String, Object> map = new HashMap<String,  Object>();             map.put("A", 12);             map.put("B", "SAP");             map.put("C", 'ÖÐ');                          String[] keyArr = map.keySet().toArray(new String[map.size()]);             for(int i=0;i<keyArr.length;i++){                 System.out.println(keyArr[i]);             }                          System.out.println("");                  -            Object[] valueArr = map.values().toArray(new String[map.size()]);     +            Object[] valueArr = map.values().toArray(new Object[map.size()]);             for(int i = 0;i<valueArr.length;i++){                 System.out.println(valueArr[i]);             }         }     } -----------------------------------------------------------------------------------------------------------------------BufferOverflowException异常本文来自-编程入门网:http://www.bianceng.cn/Programming/Java/201101/22985.htmByteBuffer byteBuffer = ByteBuffer.allocate(6); 对于byteBuffer来说,只能访问属于这个缓冲区的六个字节的数据,如果超过了这个范围,将抛出一个BufferOverflowException异常,这是一个运行时错误,因为这个错误只能在程序运行时被发现。既然缓冲区和数组类似,那么缓冲区也应该象数组一样可以标识当前的位置。缓冲区的position方法为我们提供了这个功能。position方法有两种重载形式,它们的定义如下: ==================================================================================mina read方法出现BufferUnderflowException异常的解决办法经过对mina的分析,这是由对包长度不对做成的(即,我们发的包长是大于64byte的,但他的byteBuffer大小只有64byte,当我们尝试读取第65个byte就会出现这个错误)mina怎会出现这种错误的呢??是不是有什么配置可以调整找到mina读取byte的方法AbstractPollingIoProcessor类的read(T session)此方法源代码如下-----------------------------------------------------------------------------------------------总结了一下JAVA中常见的几种RuntimeException,大约有如下几种:NullPointerException - 空指针引用异常ClassCastException - 类型强制转换异常。IllegalArgumentException - 传递非法参数异常。ArithmeticException - 算术运算异常ArrayStoreException - 向数组中存放与声明类型不兼容对象异常IndexOutOfBoundsException - 下标越界异常NegativeArraySizeException - 创建一个大小为负数的数组错误异常NumberFormatException - 数字格式异常SecurityException - 安全异常UnsupportedOperationException - 不支持的操作异常--------------------------------------------------------------------------------Java中的异常、断言、日志解析(下)作者:佚名出处:IT专家网2009-09-18 13:00本章主要讲解Java里面比较核心的一块内容--异常处理,Java异常处理机制,一致都是比较复杂的一块,而很多时候我们如果写程序的时候能够适当地注意对应的一些异常处理情况,那么我们就会在开发过程节省一大部分时间,最常见的情况就是辅助我们进行调试以及维护工作以及提高系统的容错性和稳定性。  5)java.util包:  RuntimeException(1.0):  |-ConcurrentModificationException(1.2):[->RuntimeException]  |:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。需要注意的是:  [1]此异常不会始终指出对象已经由不同线程并发修改。如果单线程发出违反对象协定的方法调用序列,则该对象可能抛出此异常。  [2]迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败操作会尽最大努力抛出ConcurrentModificationException。因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法。  [3]某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该Collection。通常在这些情况下,迭代的结果是不确定的。如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常。执行该操作的迭代器称为快速失败迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险。  |-EmptyStackException(1.0):[->RuntimeException]  |:该异常由Stack类中的方法抛出,以表明堆栈为空  |-java.lang.IllegalArgumentException(1.5):[->RuntimeException]【前边已经讲过该类了】  |-IllegalFormatException(1.5):[->IllegalArgumentException->RuntimeException]  |:当格式字符串包含非法语法,或者包含与给定参数不兼容的格式说明符时,将抛出未经检查的异常。只应当实例化此异常对应于具体错误的显式子类型。  |-DuplicateFormatFlagsException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:格式说明符中提供重复标志时抛出的未经检查的异常。  |-FormatFlagsConversionMismatchException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:转换与标志不兼容时抛出未经检查的异常。  |-IllegalFormatCodePointException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:将具有Character.isValidCodePoint(int)所定义的无效Unicode代码点的字符传递给Formatter时,抛出未经检查的异常。  |-IllegalFormatConversionException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:当对应于格式说明符的参数为不兼容的类型时,抛出未经检查的异常。  |-IllegalFormatFlagsException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:当给出非法组合标志时,抛出未经检查的异常。  |-IllegalFormatPrecisionException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:当精度为除-1以外的负值、转换类型不支持某个精度或者值在其他方面不受支持时,将抛出未经检查的异常。  |-IllegalFormatWidthException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:当格式宽度为除-1以外的负值或其他不受支持的值时,将抛出未经检查的异常。  |-MissingFormatArgumentException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:如果格式说明符没有相应的参数,或者参数索引引用了不存在的参数时,则抛出未经检查的异常。  |-MissingFormatWidthException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:请求格式宽度时抛出未经检查的异常。  |-UnknownFormatConversionException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:给定未知的转换时所抛出的未经检查的异常。  |-UnknownFormatFlagsException(1.5):[->IllegalFormatException->IllegalArgumentException->RuntimeException]  |:给定未知标志时所抛出的未经检查的异常。  |-MissingResourceException(1.1):[->RuntimeException]  |:缺少资源时抛出此异常。  |-java.lang.IllegalStateException(1.5):[->RuntimeException]【前边已经讲过该类了】  |-FormatterClosedException(1.5):[->java.lang.IllegalStateException->RuntimeException]  |:格式器已关闭时抛出的未经检查的异常。  |-NoSuchElementException(1.0):[->RuntimeException]  |:由Enumeration的nextElement方法抛出,表明枚举中没有更多的元素。  |-InputMismatchException(1.5):[->NoSuchElementException->RuntimeException]  |:由Scanner抛出,表明获取的标记与期望类型的模式不匹配,或者该标记超出期望类型的范围。  Exception(1.0)  |-TooManyListenersException(1.1):[->Exception]  |:TooManyListenersException异常用作Java Event模型的一部分来注释和实现多播Event Source的单播特例。  |-java.io.IOException(1.0):[->Exception]【前边已经讲过】  |-InvalidPropertiesFormatException(1.5):[->IOException->Exception]  |:当按照Properties规范,输入内容不符合属性集的正确XML文档类型,从而无法完成操作时,抛出此异常。  6)整理下来的异常树快照为:【异常列表:java.io、java.net、java.lang、java.text、java.util。】  java.lang.Exception(1.0):  |-java.lang.RuntimeException(1.0)  |-java.lang.ArithmeticException(1.0)  |-java.lang.ArrayStoreException(1.0)  |-java.lang.ClassCastException(1.0)  |-java.lang.EnumConstantNotPresentException(1.5)  |-java.lang.IllegalArgumentException(1.0)  |-java.lang.IllegalThreadStateException(1.0)  |-java.lang.NumberFormatException(1.0)  |-java.util.FormatterClosedException(1.5)  |-java.util.IllegalFormatException(1.5)  |-java.util.DuplicateFormatFlagsException(1.5)  |-java.util.FormatFlagsConversionMismatchException(1.5)  |-java.util.IllegalFormatCodePointException(1.5)  |-java.util.IllegalFormatConversionException(1.5)  |-java.util.IllegalFormatFlagsException(1.5)  |-java.util.IllegalFormatPrecisionException(1.5)  |-java.util.IllegalFormatWidthException(1.5)  |-java.util.MissingFormatArgumentException(1.5)  |-java.util.MissingFormatWidthException(1.5)  |-java.util.UnknownFormatConversionException(1.5)  |-java.util.UnknownFormatFlagsException(1.5)  |-java.lang.NegativeArraySizeException(1.0)  |-java.lang.IndexOutOfBoundsException(1.0)  |-java.lang.ArrayIndexOutOfBoundsException(1.0)  |-java.lang.StringIndexOutOfBoundsException(1.0)  |-java.lang.NullPointerException(1.0)  |-java.lang.SecurityException(1.0)  |-java.lang.TypeNotPresentException(1.5)  |-java.lang.UnsupportedOperationException(1.2)  |-java.util.ConcurrentModificationException(1.2)  |-java.util.EmptyStackException(1.0)  |-java.util.MissingResourceException(1.1)  |-java.util.NoSuchElementException(1.0)  |-java.util.InputMismatchException(1.5)  |-java.lang.InstantiationException(1.0)  |-java.lang.NoSuchFieldException(1.1)  |-java.lang.NoSuchMethodException(1.0)  |-java.lang.InterruptedException(1.0)  |-java.lang.ClassNotFoundException(1.0)  |-java.lang.CloneNotSupportedException(1.0)  |-java.lang.IllegalAccessException(1.0)  |-java.lang.URISyntaxException(1.4)  |-java.io.IOException(1.0)  |-java.util.InvalidPropertiesFormatException(1.5)  |-java.io.CharConversionException(1.1)  |-java.io.EOFException(1.0)  |-java.io.FileNotFoundException(1.0)  |-java.io.InterruptedIOException(1.0)  |-java.io.ObjectStreamException(1.1)  |-java.io.InvalidClassException(1.1)  |-java.io.InvalidObjectException(1.1)  |-java.io.NotActiveException(1.1)  |-java.io.NotSerializableException(1.1)  |-java.io.OptionalDataException(1.1)  |-java.io.StreamCorruptedException(1.1)  |-java.io.WriteAbortedException(1.1)  |-java.io.SyncFailedException(1.1)  |-java.io.UnsupportedEncodingException(1.1)  |-java.io.UTFDataFormatException(1.0)  |-java.net.UnknownServiceException(1.0)  |-java.net.UnknownHostException(1.0)  |-java.net.ProtocolException(1.0)  |-java.net.HttpRetryException(1.5)  |-java.net.MalformedURLException(1.0)  |-java.net.SocketException(1.0)  |-java.net.NoRouteToHostException(1.1)  |-java.net.PortUnreachableException(1.4)  |-java.net.BindException(1.1)  |-java.net.ConnectException(1.1)  |-java.text.ParseException(1.0)  |-java.util.TooManyListenersException(1.1)  这里对Checked Exception做个简单补充,这种“可检查异常”本身具有这样一些特性:  [1]该异常发生过后,是可以恢复的,比如当使用Swing界面去读取一个文件的时候,路径里面的文件不存在换一个路径继续读取  [2]的程序运行的时候有时候会对它运行的环境有依赖性,这种依赖是程序本身不可估计的,这里针对程序环境的依赖条件是允许出错的,比如最常见的IO问题和网络问题  [3]该异常并不会导致程序错误不能运行,进行一些适当的内部处理过后,该异常可以继续进行后续的操作  【*:也就是说,在使用Java异常机制的时候,往往考虑得最多的就是针对CheckedException的设计,因为这种异常往往不是由于程序本身造成的,而是由于程序所运行的环境造成的,可以这样理解,CheckedException的产生很多时候都是由于客观条件产生的,对程序本身而言,只是针对例外的客观环境进行了一种处理方式的编写,并不是程序本身存在什么问题,所以这种异常在设计的时候是需要考虑的。例如前边说的:当的程序运行的时候从网上下东西,突然网线被拔了,那么该程序不会终止,因为有个异常抛出,折中异常可以通知程序这会儿网络不通,那么程序针对这种情况就会采取一定的处理措施。】  iv.自定义异常  当然也可以在项目开发过程设计一个属于自己的异常类,其定义方式用以下一段代码来说明: class DreadfulProblemException extends ArithmeticException {   public DreadfulProblemException() {   }   public DreadfulProblemException(String s) {   super(s);   }   }   public class MainClass   {   public static void main(String args[])   {   int[] array = new int[]{1,0,2};   int index = 0;   try   {   System.out.println("First try block in divide() entered");   array[index + 2] = array[index]/array[index + 1];   System.out.println("Code at end of first try block in divide()");   }   catch(ArithmeticException e)   {   System.out.println("Arithmetic exception caught in divide()");   throw new DreadfulProblemException("index + 1"); // Throw new exception   }   catch(ArrayIndexOutOfBoundsException e)   {   System.out.println("Index-out-of-bounds index exception caught in divide()");   }   System.out.println("Executing code after try block in divide()");   }   }   以上的代码就定义了一个属于程序自身的异常类,这种用法在开源框架的开发和设计中比较普遍,就像在使用Hibernate和Spring的时候经常会遇到类似HibernateException以及其他相关异常,这种异常都是其框架在设计和开发过程中的自定义异常。  【*:一般情况在开发过程中最好使用自定义的异常结构,异常本身的命名行为使用与业务有关的异常行为,这种设计方式比较规范,而且便于自己进行管理,在提供了规范文档的前提下,使用自定义异常是开发产品必不可少的步骤,而项目开发,根据客观情况而定,关于异常的使用心得和开发我在下边一个章节进行说明。】  2.异常处理心得  i.关于异常的一些开发心得总结:  --◆编程心得[1]:针对不正常条件使用异常机制--  针对这点先看一段简单的代码:  /**   *滥用异常的概念说明代码   **/   public class ExceptionUse   {   public static void main(String args[]) throws Exception   {   List testList = new ArrayList();   List resultList = new ArrayList();   try   {   int index = 0;   while(true){   String tempString = new String(testList[index++].getBytes("UTF-8"),"GB2312");   System.out.println(tempString);   resultList.add(tempString);   }   // ...针对resultList的一些相关操作   }   catch(ArrayIndexOutOfBoundException ex)   {   ex.printStackTrace();   }   }   }   上边这段代码是为了针对某个字符串数组里面的每一个字符串进行转码的操作,将一个UTF-8编码的字符串列表转化成为GB2312的字符串列表,这样直接看起来这段代码是没有问题的,但是这段代码却遇到了一个很致命的概念问题,怎么讲呢?仔细注意上边这段代码,注意语句while(true)这句话,这是一个检测数组越界的条件,这种检测方式在整整运行的时候是不会有问题的,因为一旦数组越界就会通过异常的方式检测出来,但是这是一个很不良好的做法。很多时候依赖JVM的异常来辅助进行业务的数据规范,实际上这样的做法是很不合理的,但是为什么有时候会优先使用基于异常的方式,而不使用比较可靠和可行的编程模式?这样做其实是在编程过程养成了一个依赖心理,JVM确实提供了很多异常机制可以直接检测的一些数据是否合法,但是这种做法应该是不被提倡的。主要原因在于:  [1]异常机制的设置应该是JVM编程里面的最后一道防线,最初目的就是为了用于不正常的情况,所以很多时候JVM实现是不会对这样的方式进行优化的,这样的方式来检测业务数据的开销是很昂贵的;  [2]把代码块放在try-catch里面对于JVM本身运行而言就已经阻止了某些优化操作了,异常的目的是防止出错,而这种做法是故意去依赖这种错误;  实际上,去分析JVM内部原理就可以发现,基于异常的运行模式本身比标准模式要慢很多。这段代码仅仅说明了一个问题:异常只能应用于不正常条件,在正常模式里面不应该使用异常来参与业务流程的运行,即在正常控制流里面不可以使用异常来做辅助操作。从整个系统的设计上讲,良好的API设计不应该强迫它的运行时为了正常的控制流程而使用异常。  以下这种情况也是不提倡的: public class ExceptionUseTwo   {   // ……定义一些类里面的方法   public static void main(String args[])   {   String temp = getInputString();//从某个定义的方法里面去读取输入   try   {   Integer tempNumber = Integer.parseInteger(temp);   // 继续往下走   }   catch(NumberFormatException ex)   {   System.out.println("Sorry,the value of input is not a number!");   }   }   }   上边使用了NumberFormatException的异常检测来检测输入是否是个数字,这种方式其实最好的方式是使用正则表达式来检测,而不是通过这样的方式来检测输入格式是否正常。  --◆编程心得[2]:合理使用CheckedException和RuntimeException--  从上边的讲述可以知道,JVM里面的异常机制本身分为三种:Error、CheckedException和RuntimeException,对于Error不用去考虑使用问题,因为这种情况往往是不可恢复的情况,可以不作理睬。在考虑使用一个CheckedException或者是使用一个RuntimeException的时候,主要原则在于:如果期望调用者能够恢复,对于这种异常考虑使用CheckedException;这种情况下,可以直接抛出一个异常用catch来处理或者直接使用throw语句将这个异常往外层抛出。对于这样的异常,一旦捕捉到了过后需要对其进行一定的处理操作,但是这种异常在设计的时候最好不要忽略掉。在编程过程最容易忽略的异常就是NullPointerException,做过开发的人都能够感受,经常调试的工作遇到的就是NullPointerException这个异常。  那么如何合理使用RuntimeException呢?一种典型的做法就是:用RuntimeException来指明程序错误!大多数运行时异常都是一种前提违例【Precondition Violation】,这种方式指代的是在编写程序库以及一些API的时候没有去遵循某种约定,比如:NumberFormatException一旦出现了表明的是程序在运行的时候违背了这样一种约定。虽然很多时候,正规编程没有强制性要求,但是按照惯例,这种错误往往会被JVM保留下来,比如资源不足或者出现了约束越界等各种问题。所以按照的通常对于JVM异常机制的运用,有一种常见的约定:所实现的只要是UnCheckedException的结构都应该是JVM里面定义的RuntimeException的子类。  【*:可以仔细地思考一下,JVM本身在设计异常体系结构的时候是经过了严格思考的,可以这样讲,CheckedException的出现的本身目的是为了检测内部的一些问题,这种问题是通过编译器检测出来的。而RuntimeException却是定义了程序使用的一种域,这种域的实则是告诉在编程过程尽量使得自己不要去触犯这种域定义的规则。这些异常的定义是在于防止触犯这种规则,在运行时的过程里面一旦的程序触犯了这种规则,就会被JVM进行RuntimeException的指示。而Error本身是系统出现不可恢复错误,所以这种情况是不能够被处理的,这种情况只能对程序说抱歉,进行重新设计。合理使用JVM里面提供的两种错误是程序设计很好的方法。】  从概念上讲CheckedException往往指代的是可以恢复的错误,所以在处理这种异常的时候往往提供相关的辅助函数来操作,通过这些操作,可以针对不同的CheckedException来进行相关处理操作。对于一个软件系统本身而言,可以恢复的异常是值得深思的,从现实生活的角度上讲,如果你去寻找一个人,打了电话,而对方没有接通,事情很着急,最好的办法是找个时间重播一次,这种做法跟网络连接异常SocketException的做法有点类似,而且这种做法应该是软件本身应该被允许的。  --◆编程心得[3]:尽量使用标准异常--  软件工程有一个核心的概念就是“软件复用”,在设计系统的时候往往会从细处考虑到代码重用问题。代码复用本身应该是值得提倡的,否则就不会进行模块化设计和接口设计了,而在处理一些系统异常本身的时候,为了保证一定的通用性个规范性,尽量采取官方提供的标准异常。Java的异常定义里面,有很多基本的CheckedException,它已经从概念上覆盖了很多需要的异常类型,这种情况下尽量采取“复用技术”。  这里提供在使用标准库里面比较常用的异常快照,虽然不完整:  异常 使用场合  IllegalArgumentException 传入的参数的值不合适  IllegalStateException 对于这个方法的调用,对象状态有问题  NullPointerException 在null被禁止的情况下参数值为null  IndexOutOfBoundException 越界  ConcurrentModificationException 禁止并发修改,被对象检测到的  UnsupportedOperationException 对象不支持某种操作  这里有一个系统设计上的问题,在开发过程往往会选择一定的异常类型,至于真正要选择什么样的异常类型跟本身的系统设计有关,也是在程序设计里面需要考虑的问题。一般情况下,如果没有特殊的业务规则需要特定的自定义异常,直接使用标准的异常类型是一个很不错的方式,除非有标准异常库未曾定义的异常,或者说标准库的异常不能满足的业务需求,否则不要轻易定义一些和标准库相冲突的自定义异常。  所以,在程序开发过程中,尽量使用标准异常!  --◆编程心得[4]:在构造函数中抛出异常--  根据对构造子的理解,是可以知道构造函数本身属于一个特殊的数据结构,是不存在返回值的,那么如果在构造函数里面来获得异常信息就成为了很困难的事情。面对这样的情况如何进行相关处理呢?  【*:两年前我参与开发一个基于J2EE的CRM客户端的时候就遇到了这样一个问题:因为系统里面具有一个界面管理器,而在管理器最初需要针对初始化的内容进行监控,而且该管理器必须将这个过程的一些错误记录下来,而最开始使用普通的办法都没有做到,而且使用日志也不管用,主要原因是因为日志是正常运行的。所以针对这一点深有感触。】  有些技术可以实现所需要的这种情况的需求:  双阶段构造(two-stage construction):  将可能产生错误的代码移除到构造函数里面,这些函数能够返回一定的错误代码或者错误相关信息。而这样做就使得的调用顺序有了典型的不同:用户必须先调用构造函数、然后调用那些可能产生错误信息的函数、最后再检查返回代码以及捕捉相关异常。先提供一段代码,然后再进行详细的分析:  import java.io.FileReader;   import java.io.BufferedReader;   import java.io.IOException;   import java.io.FileNotFoundException;   /**   *构造函数双阶段构造的概念说明   **/   public class FileScanner   {   public FileScanner(String filename) throws FileNotFoundException,IOException   {   FileReader reader = new FileReader(filename);   BufferedReader br = new BufferedReader(reader);   String str = br.readLine();   //……   }   public void scanFile(){}   public static void main(String args[])   {   FileScanner scanner = null;   try   {   scanner = new FileScanner("C:/text.txt");   }   catch(FileNotFoundException ex)   {   ex.printStackTrace();   }   catch(IOException ex)   {   ex.printStackTrace();   }   scanner.scanFile();   }   }   【*:仔细思考上边这段代码:FileScanner对象接受到了一个字符串参数,这个参数代表了一个文件路径,然后尝试着打开这个文件。若该文件不存在,构造函数就会引发一个FileNotFoundException异常,如果读取过程再出现问题,就会直接抛IOException。仔细考虑这段调用代码,如果构造失败,就会使得引用scanner为null,这种情况下,表示既然没有被构建,如果再访问该对象就会直接抛出NullPointerException。】  从这点上讲,即使构造函数不是一般函数,仍然可以使用异常处理或者throws语句,这样的方式来处理构造失败的情况是比较高效合理的一种方式。如果结合使用JVM里面深入引用,那么不仅仅可以在一个对象创建过程监控该对象,而且可以在对象毁灭一直到垃圾回收的过程里面监控整个对象的所有状态,在开发一些严格的程序的时候就会遇到这样各种各样的情况。  --◆编程心得[5]:深入理解throws--  throws是一种语言特性,用来列出[可以从某个函数传到外界]的所有可能的异常。JVM编译器会强迫必须在函数中捕捉这些列出的异常,否则就在该函数的throw子句中声明。如果某个项目已经完成了,但是有时候需要在里面为某些不良的设计添加一些新的可抛出的异常,这种情况如何来完成呢。当然和前边讲的异常处理一样的:  [1]使用try-catch将这个异常捕捉  [2]使用throws或者throw将这个异常抛出  【*:注意这里说的是在一个已经完善的系统里面,所以根据参考可以知道,这种worker method类型的低级系统或者不良设计里面,如果要使用try-catch的方式来处理,可能行不通,因为它没有办法独立处理这个异常,唯一的办法就是使用第二种方式,那么就需要理解throws的一些特性以及相关缺陷。要知道在一个已经完善的系统里面添加throw或者throws是一个大工作量,如果系统已经完成了过后才来考虑异常抛出,是一个很严重的问题。而且在添加过程不小心会影响系统的整体开销或者资源调配,假设在设计之初忽略了一个来自数据层的异常,那么添加和修改的时候不是进行的处理而是throws,注意这里说的是使用try和catch不能处理的时候,那么不仅仅会在业务逻辑层遇到该处理的问题,还会在其他层次遇到这种一直往外抛出的问题,那么在系统里面加入该异常的时候,难免会很有影响。】  其实最好的方式应该是在设计之初将这些可能的关键性因素考虑到,这样就使得throws的使用更加合理,记住一点:  在一个系统开发设计之初最好能够设计一个良好的异常体系处理结构,而这部分设计可能往往会成为最容易忽略的问题,因为最初没有办法考虑到一些系统本身的异常问题。设计异常本身对系统而言就是一个挑战,这种方式和TDD的设计方式是一个出发点,因为要在设计之初考虑到系统可能会出现和遇到的问题,所以这样的设计经常是出于积累和思考。  先考虑这样一段代码:class ExceptionOne extends Exception{}   class ExceptionTwo extends ExceptionOne{}   class ExceptionThree extends ExceptionTwo{}   public class ExceptionThrowTester   {   public void testException(int i) throws ExceptionOne   {   if( i == 1 )   throw new ExceptionOne();   if( i == 2 )   throw new ExceptionTwo();   if( i == 3 )   throw new ExceptionThree();   }   }   注意上边这段代码,编译是合理的,但是这样的方法有一定的弱点。实际上这个地方抛出的所有异常都是属于ExceptionOne类型的,这里能够通过编译是因为符合throws语句的语法要求,这段代码可以这样理解:函数可能产生ExceptionOne的异常。可以稍稍修改一下testException函数的定义:   public void testException(int i) throws ExceptionOne,ExceptionTwo,ExceptionThree   但是这样改写过后,抛出的异常还是会这样理解:函数还是可能会产生一个隶属于ExceptionOne的异常,因为这里这三个自定义异常是存在继承关系的。如果想要知道ExceptionOne的派生异常类,就得查看源代码才能够办到,假设这个代码设计存在于已经编译好的jar库里面,其实这本身而言是不切实际的。这种做法使得真正在调用过程里面会出现下边的这种写法,这种写法在编程的时候很常见: public class ExpThrowTesterTwo   {   public static void main(String args[])   {   try   {   //正常的逻辑代码   }   catch(ExceptionOne ex)   {   //异常的处理代码   }   }   }   上边这种写法是不会出现任何编译错误的,但是考虑一点,会使得养成了一个不良的习惯:正确地说,应该使得抛出的异常精确到某种类型,而不应该是它的父类型,这样真正在开发过程也方便进行调试工作。也就是说可以避免[试图精确推断这个函数可能产生哪种异常]的困境。再看一段代码:import java.io.File;   import java.io.FileReader;   import java.io.FileNotFoundException;   public class ExpThrowTesterThree   {   public static void main(String args[])   {   try   {   File file = new File("C:/readme.txt");   FileReader reader = new FileReader(file);   }   catch(Exception ex)   {   //对应的异常处理方式   }   }   }   按照理论来讲这段代码没有任何问题,但是会有一点,比如try块里面出现了其他异常,那么只能从一个异常的堆栈信息来确定程序里面到底遇到了什么问题,其实最好的方法就是把其中的异常处理分开操作。比如会出现FileNotFoundException的地方使用对应的异常,而会出现NumberFormatException的地方写另外的catch语句来进行分类匹配,这样程序里面有什么异常就一目了然了。  总之:使用throws的时候,最好能够明确抛出某种派生类型的异常,而且使用catch捕捉的时候也尽量使对应的子类异常。  --◆编程心得[6]:避免资源泄漏,finally用法--  在JVM异常处理机制里面,比较出色的一点就是finally方法,finally方法里面的代码块总是会得以执行的,而且不论是否有异常,都能够执行,这在维护对象内部状态和资源清理上是很实用的:  对比两段代码:import java.net.*;   import java.io.*;   /**   *不使用finally写法【*:概念说明代码里面可能存在很多不规范的内容】   **/   public class FinallyTester{   public static void main(String args[])   {   ServerSocket ss = new ServerSocket(0);   try{   Socket socket = ss.accept();   // 其他操作   }   catch(Exception ex)   {   ss.close();   throw ex;   }   ss.close();   }   }   以下是使用finally的代码:   import java.net.*;   import java.io.*;   /**   *使用finally写法   **/   public class FinallyTester{   public static void main(String args[])   {   ServerSocket ss = new ServerSocket(0);   try{   Socket socket = ss.accept();   // 其他操作   }   finally   {   ss.close();   }   }   }   【*:仔细思考一下上边两段代码,当然第二段更加规范一点。因为有可能程序员会忘记了close()的操作,这样就有可能造成忘记关闭而出现资源泄漏的情况,所以确保资源能够正常回收,最好的办法就是使用finally块防止资源泄漏,和try/catch块匹配使用,finally代码块里面的内容存在于异常处理机制里面作为统一的出口,就是任何情况都会执行的,而且会在异常处理机制里面确保退出之前执行。】

原创粉丝点击