本章讨论的内容:异常、断言、日志。
1、错误分类:
(1)用户输入错误:输入url不合法
(2)设备错误:打印机被关掉了
(3)物理限制:如磁盘满了
(4)代码错误:下标越界等
2、有的时候出错用返回码表示,如false,java.io中常用的-1表示EOF等。但更多的时候,这些信息不足以表示错误类型。异常(Exception)更为常用。
3、Java中的所有可抛出错误和异常继承自Throwable,分为两类:
(1)Error:不可恢复的错误,如OutOfMemoryError,它们不应该由用户主动抛出。一般发生这些错误的时候,就应该结束程序了。
(2)Exception:可恢复或者处理的异常。
4、Exception又分为两类:
(1)RuntimeException:类型转换出错、URL格式错误、加载不存在的Class等。这些都应该是在程序设计中进行避免或主动判断的。
(2)IOException:I/O错误导致的。
5、如果类的某个方法内显示的throw出了某个Excpeton,则应该在方法定义的时候,声明”检查异常“,比如FileInputStream的构造函数:
public FileInputStream(String name) throws FileNotFoundException
它将构造一个FileInputStream对象,但有可能抛出一个FileNotFoundException异常。对于继承自RuntimeException的异常,不应该写在throws里,而是应该在代码中避免的。比如ArrayIndexOutOfBoundsException,也不应该写Error继承的类。
6、如果调用一个声明”检查异常“的方法,则要么在自己的方法上throws它,要么内部处理它。
7、如何在代码中抛出异常:
throws new IOException(); //有的还可以又更详细的构造函数: throws new EOFException("File end...");
8、有时候,Java内置的异常体系不能满足你的需求,可以实现自己的异常类:
class FileFormatException extends IOException { public FileFormatException() {} public FileFormatException(String msg) { super(msg); } }
上面的super(msg),会把msg存在Throwable中,通过getMessage()方法可以返回这个msg。
然后,就可以抛出自己的异常了:
throws new FileFormatExcption("Format illigel");
9、捕获异常:
try { //code may throw exception //code may throw exception } catch(Exception e) { //handler for exception }
如果try块内抛出了异常,将直接执行catch块中的内容。
如果try内没有抛出异常,将跳过catch块。
10、在定义基础逻辑时,最好不在内部处理异常,而直接定义throws,将异常交给调用者处理。
11、编译器将严格检查throws说明,如果调用了一个抛出异常的方法,就必须要么对它进行处理(try/catch),要么把它传递出去。
12、如果覆盖一个超类方法,那么子类的throws不许出现超过超类所列出的异常范围。也就是说,需要在子类内部处理所有超类没有列出throws的异常。
13、捕获多个异常:
try { //code may throw exception //code may throw exception } catch(Exception e1) { //handler for exception 1 } catch(Exception e2) { //handler for exception 2 }
14、获取异常信息:
e1.getMessag(); e1.getClass().getName();
15、有时候,在catch块内,可以还会抛出异常,即补上异常更详细的信息,方便调用这查看。有一种不太损失调用信息的好方法,如下:
try { access the database } catch(SQLException e) { Throwable se= new ServletException("database error"); se.setCause(e); throw se; }
恢复时候,用如下方法:
Throwable e=se.getCause();
16、finally放在catch后,不管是否进入了catch,都会执行finally。适用于资源的释放(文件句柄,数据库链接等)。有时候,.close()会抛出异常,要注意处理。例如InputStream。
17、堆栈跟踪是很有用的技术,可以分析程序执行的流程,调试时候非常有用:
Throwable t=new Throwable(); StackTraceElement[] farames=t.getStackTrace(); for(StackTracelElement frame:frames) { analyze frame }
下面是一个打印堆栈跟踪信息的例子:
public class TestException { public void gofunc(String msg) { String a = msg; a = ""; Throwable t = new Throwable(); StackTraceElement[] ss = t.getStackTrace(); for(StackTraceElement s: ss) { System.out.println(s); } } public static void main(String [] args) { TestException t = new TestException(); t.gofunc("aa"); } }
打印出来的结果:
TestException.gofunc(TestException.java:8) TestException.main(TestException.java:19)
18、JDK5之后,为线程Thread增加了静态的getAllStackTraces(),可以对所有线程进行跟踪。
19、对异常的建议:
(1)不要用异常代替简单判断,因为异常处理会消耗大量性能。
(2)不要过分细化异常处理,可以将多个异常放在一个try块内,附加多个catch即可。
(3)传递异常不可耻,在必要时候应当使用。
20、JDK1.4后开始支持断言,用于程序调试时的诊断,格式:
assert 条件;
当条件为false时,抛出AssertionError异常。
21、断言可以在java命令行被禁用:
java -enableassertions MyApp
22、断言用法:
(1)断言是致命、不可恢复的错误
(2)断言只用于开发和调试阶段
23、使用日志的优势:
(1)在发布阶段,可以很容易的取消调试日志(按照级别分类)
(2)可以记录到更多的文件格式,如XML、纯文本、数据库等。
24、默认的日志管理器,可以与System.out互换:
import java.util.logging.Logger; import java.util.logging.Level; public class TestLog { public static void main(String args []) { Logger def_log = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); def_log.setLevel(Level.INFO); def_log.info("First log"); } }
25、自创建日志管理器:
Logger my_log = Logger.getLogger("com.coder4.TestLog");
26、日志级别:
(1)SEVERE
(2)WARNING
(3)INFO
(4)CONFIG
(5)FINE
(6)FINER
(7)FINEST
Level.OFF可以关闭所有级别的日志。
默认日志管理器是INFO及以上
27、记录日志的方法:
logger.warning(msg); //或者 logger.log(LEVEL.WARNING, msg);
28、默认的日志管理只记录了类和方法名,没法具体到行,例如:
2011-12-29 22:50:05 TestLog main 信息: First log
29、Logger可以直接用于记录日志:
public void (Logger.)log(Level level, String msg, Throwable thrown)
30、可以通过修改java命令行或者JVM配置来更改默认的日志级别,以我的Ubuntu为例,在:
/usr/lib/jvm/java-6-sun/jre/lib/logging.properties里面,里面部分内容:
# Default global logging level. # This specifies which kinds of events are logged across # all loggers. For any given facility this global level # can be overriden by a facility specific level # Note that the ConsoleHandler also has a separate level # setting to limit messages printed to the console. .level= INFO
31、命令行方式改变默认日志级别:
#通过指定其他logging.properties文件的方式 java -Djava.util.logging.config.file=config_file MainClass #也可以通过System实现: System.setProperty("java.util.logging.config.file", "other_file")
32、我们在刚才的例子中已经看到,一部分日志已经是国际化的了(如显示”信息:“而不是INFO:)。
我们对于输出的msg也可以进行国际化:比如对于中文用户提示”用户名“,对英文提示"username"。
一个程序可以包含多个语种的资源包,用ResourceBundle类对它们自动定位,这一般按照类名完成。
绑定资源包要用两个String参数的工厂方法函数:
static Logger getLogger(String name, String resourceBundleName)
例如,新建如下文件com/TestLogger_en.properties,这是英文的国际化,内容如下:
hint=Please enter password for username {0}
对于中文资源文件com/TestLogger_zh_CN.properties,如下:
hint=请输入用户 {0} 的密码
记录日志时,可以用附加Object[]的方法将{0}这些占位符替换掉。
public void log(Level level, String msg, Object[] params)
例子,注意注释:
package com; import java.util.logging.Logger; import java.util.logging.Level; public class TestLog { public static void main(String args []) { //Get ResourceBundle would read com/TestLogger_zh_CN.properties Logger my_log = Logger.getLogger("mylog","com.TestLog"); my_log.setLevel(Level.INFO); //key hint is paramlized in TestLogger_zh_CN.properties my_log.log(Level.INFO,"hint","liheyuan"); } }
33、日志处理器(Log Handler):如果说日志级别决定了是否输出,那么日志处理器决定了输出到哪里。默认的ConsoleHandler是类似System.out,输出到屏幕。
其他常用的Handler还有:
java.util.logging.FileHandler // 输出到文件
FileHandler handler = new FileHandler("my.log"); logger.addHandler(handler);
更详细的配置一般是设置logging.properties,具体的可以参考网上的文章。
34、过滤器,也可以自己实现过滤器Filter接口,决定哪些日志可以被写入,哪些直接丢弃了。
public interface Filter boolean isLoggable(LogRecord record)
35、格式化器,类似于Log4j中的格式化器,但在默认的java.util.logging中要自己实现Formatter接口,并添加到Logger中。
public abstract class Formatter abstract String format(LogRecord record)
36、日志其他注意事项:
(1) 最好同一个应用共用一个日志,private static final Logger logger = Logger.getLogger(......);
(2) 默认的这个日志很难用,玩玩还凑合,正规系统建议还是log4j吧……
37、常用调试技术
(1)System.out / System.err 结合shell管道等打印到文件
(2)Logger
(3)IDE
(4)借助外界程序(一般是性能分析)
38、想观察执行Java过程时的类加载过程,则加入-verbose:
#加上-verbose,可显示详细的类加载信息 java -verbose TestException # liheyuan@liheyuan-desktop:tmp$ java -verbose TestException [Opened /usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/rt.jar] [Loaded java.lang.Object from /usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/rt.jar] [Loaded java.io.Serializable from /usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/rt.jar] [Loaded java.lang.Comparable from /usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/rt.jar] ......很长很长的......
39、JDK5后支持lint,即编译静态检查,在javac时候加上-Xlint:all即可:
javac -Xlint:all ./TestException.java ./TestException.java:19: 警告:[fallthrough] 可能无法实现 case case 2: ^ 1 警告
40、JDK5后可以用jconsole processID观察性能变化。当然现在一般借助更为专业的商业化第三方软件。
41、java.awt.Robot可以移动鼠标、模拟点击、模拟键盘事件,还可以截屏幕。书中介绍这个是说可以模拟用户进行测试,有点扯吧,截屏功能倒还是实用。
42、java也支持调试。。
#编译时候加上-g java -c TestException #然后就是调试模式了 java TestException
一般这个是IDE完成的,调试单步什么的还是Ecelipse等IDE吧。
本章完。
请问博主是怎么记下这些读书笔记的呢?是看电子版还是实体书?假如是后者,那么很佩服楼主啊~
看扫描的电子版,所以和纸质差不多,没法复制。都是手敲进去的,过过脑子才能记住啊,呵呵。