异常处理技巧

举报
资源描述
1 异常层次需要考察的异常情况有:用户输入错误、设备错误、物理限制、程序错误,传统的返回错误码的方法并不能处理所有的情况。1.1 异常分类在 Java 程序中,异常对象都派生于 Throwable。如果 Java 的内置异常不满足需求,则可以创建自定义异常。下图是基本的异常体系:Error 类描述了 Java 运行时系统的内部错误和资源耗尽错误,此类错误不应由应用程序抛出。而 Exception 分两类,一类是因程序逻辑问题,导致系统出问题,此时就是 Runtime 异常,如数组越界、类型转换错误、访问空指针。而如果程序可以正常,但在遇到不同 IO 情况时,出问题导致,则属于 IO 异常,如打开格式错误的 URL、在文件尾部读数据、加载不存在的类文件等。RuntimeException 一定是程序写的有问题,是可以避免出现的。Java 将 Error 和 RuntimeException 定义为未检查的异常。其他异常为已检查。编译器将检查代码是否为所有已检查异常提供异常处理器。1.2 异常声明方法应该在首部声明其所有可能抛出的异常,例如:public void lookup() throws XXException,YYException一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控,要么就应该避免发生。如果方法没有声明所有可能发生的已检查异常,编译器就会给出错误消息。当程序调用一个抛出已检查异常的方法、或程序自己会抛出异常时,有必要进行异常声明,对于 Error 和 Runtime 这些未检查异常,无人可以预料,因此不需要声明。1.3 自定义异常如果遇到标准异常不能说明的情况,则需要自定义异常。只需要继承自某个异常类,并定义两个构造函数即可。一个默认构造函数,一个带有描述信息的构造器。2 捕获异常异常的捕获需要周密的计划。如果异常没有被捕获,程序会终止,并会在控制台上输出异常信息。对 UI 程序,异常出现后会提示用户,用户可以继续返回到程序中。使用 try 块来捕获异常try{}catch(XXXException e){}catch(YYYException e){}如果 try 语句中的任何代码抛出一个在 catch 中指定的异常,则程序会跳过 try 语句中的其余代码转而执行 catch 子句中的代码。如果抛出的是不在 catch 中指定的异常,则程序立即返回到上层调用者。当捕获到异常后,一种方法是我们自己处理;另一种是不处理,交给调用者去处理,这样只需要声明异常即可。如果应该捕获那些知道如何处理的异常,而将不知道如何处理的异常传递给上层。将异常交给足以胜任的处理器,比压抑它好得多。2.1 捕获多个异常通过示例代码,可以捕获多个异常2.2 链式异常当捕获到异常后,我们可以改变其类型,并重新抛出。这样做可以隐藏底层异常的细节,保持异常解释的抽象性。为了获得真正的底层异常,可以用 setCause()方法将底层异常包装到高层异常中,并在需要的时候用 getCause()来获取。2.3 finally 子句不论异常是否发生,finally 中的语句都会被执行,这可以作为一种比较合适的清理资源的方式。当然如果在 finally 中抛出异常,则会导致 try 中异常类型丢失。因此建议异常的使用风格如下:try{try{}finally{ }}catch(){}内层的 try..finally 负责清理资源,而外层的 try 只负责报告错误。2.4 堆栈跟踪java.lang.ThrowableThrowable(String s)Throwable(Throwable cause) Throwable(String s,Throwable cause) 用给定的 cause 构造一个异常Throwable getCause()void setCause()String getMessage() 获得描述信息StackTraceElement[] getStackTrace() 获取构造这个对象时调用堆栈的跟踪java.lang.ExceptionException(Throwable cause) Exception(String s,Throwable cause) 用给定的 cause 构造一个异常java.lang.RuntimeExceptionRuntimeException(Throwable cause) RuntimeException(String s,Throwable cause) 用给定的 cause 构造一个异常java.lang.StackTraceElementString getFileName() 返回元素运行时对应的源文件名int getLineNumber() 返回元素运行时对应的源文件行数String getClassName() 返回元素运行时类命名String getMethodName() 返回方法名bool isnativeMethod()2.5 异常机制使用建议I 异常机制对于性能损耗较大,最好使用简单测试来避免异常发生,如在 stack 非空时才调用 pop,比使用 try 包围的 pop 性能高 100倍II 不要过分细分异常,将一段正常的流程放在一个 try 块中而不是分到不同的 try 块中,有得于代码的阅读。III 善于利用异常层次,不要只抛出 RuntimeException,而应该寻找和创建更适合的子类;也不要只捕获 Throwable,否则代码会较难阅读。IV 合理压制异常。如果认为异常不是问题则可以合理的压制,否则则应该声明或抛出。V 早抛出,晚捕获;在程序出错的地方立即抛出异常,而尽可能向高层传递非细节异常。3 记录日志3.1 logging 包的类结构3.2 全局日志日志模块,拥有一个全局变量 Logger.global,可以用它向控制台输出信息。Logger.global.info(String s) 打印日志到标准输出Logger.global.setLevel(Level level) 设置日志级别3.3 命名日志日志也具有层次结构,不同的包可以按结构名来获取各自的日志,把记录器命名与主程序包一样的名字是一个好的实践。日志有 7 个级别:SEVERE WARNING INFO CONFIG FINE FINER FINEST,默认为 INFO 级别。ALL 代表所有级别,OFF 表示关闭。java.util.logging.Logger 类static Logger getLogger(String path.name.log) static Logger getLogger(String name,String resName) 提供资源包的日志void setLevel(Level l) 设置日志记录器级别Level getLevel()void addHandler(Handler h) 添加处理器void removeHandler(handler h)void setUseParantHandlers(bool flag) 设置是否使用父处理器void log(Level level,String msg) 按指定级别记录日志void log(Level level,String format,Object[] objs) 按指定级别记录日志void logp(Level level,String classname,String methodName,String msg)void logp(Level level,String classname,String methodName,String format,Object[] objs)void logrb(Level level,String classname,String methodName,String resname,String format,Object[] objs)void throwing(String classname,String methodName,Thowable t) 记录异常的信息3.4 日志管理器配置日志系统的配置默认位于 jre/lib/logging.properties 中。如果要使用不同的配置,要在 JVM 启动时指定,java -Djava.util.logging.config.file = path MainClass在配置文件中可以指定日志处理器、对日志处理器进行相应的配置、日志输出级别、日志输出格式等3.5 日志的国际化在获取日志时,还可以为日志指定资源包,以支持本地化的日志显示。3.6 日志处理器默认情况下,日志将记录发送到 ConsoleHandler 中,并由它输出到 System.err 流中。日志处理器也有级别,其只处理日志条目级别高于处理器级别的条目。如果想绕过配置,则需要自己设置日志记录器的级别和处理器的级别,并将处理器安装到日志上。如果希望将日志发送到其他地方,需要添加其他的处理器。系统提供了两个有用的处理器,FileHandler 和 Sockethandler。用户可以自行继承自 Handler 或 StreamHandler 来实现自定义的日志输出方式。java.util.logging.handlerabstract void publish(LogRecord lr) 将日志写到特定流abstract void flush() 刷新已缓冲数据Formatter getFormatter()void setFormatter(Formatter f) 设置格式化器void setLevel(Level l) 设置处理级别java.util.logging.ConsoleHandlerjava.util.logging.FileHandlerFileHandler(String namePatter,bool append) 以日志命名模式和是否追加初始化日志FileHandler(String namePatter,int limit,int count,int append) limit 控制日志内最大条数,count 控制循环日志数3.7 格式化器系统提供的处理器可以生成平坦文本和 XML 格式的日志记录,我们也可以自定义格式,只需要扩展 Formatter 类并覆盖 format()方法即可。对于 XML 格式化,需要覆盖 getHead/Tail 来提供特定的头和尾内容。java.util.logging.Formatterabstract String format(LogRecord lr)String getHead/Tail()String formatMessage(LogRecord lr)4 使用断言assert cond 如果 cond 为假,则抛出异常assert cond:expr 如果 cond 为假,则使用 expr 作字符串构造异常在默认时,断言并不生效,通过在控制台增加 enable 进行启用:java -enableassertion myapp或 java -ea:class1 -ea:packet1启用和禁用断言并不需要重新编译程序,这是由类加载器来完成的。对于由虚拟机加载的类,可以使用 esa 来进行加载。断言是一种在开发和测试中使用的技术,不要将断言代替异常和日志。5 调试技术5.1 常用方法5.1.1 使用 print 或 log 方法输出程序信息到屏幕或文件中5.1.2 在每个类中放置 main 方法,进行单元测试5.1.3 使用 JUnit 进行单元测试5.1.4 使用 throwable 提供的 printStackTrace 或 thread.dumpStack 跟踪执行堆栈5.1.5 使用 -Xp
展开阅读全文
温馨提示:
金锄头文库所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。
相关搜索

当前位置:首页 > 行业资料 > 其它行业文档


电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号