一、异常
Java中所有异常对象都是由于Throwable类继承而来。但在Throwable类的下一层就立即分为Error和Exception两个类。
Error类层次结构描述了JAVA运行时系统的内部错误和资源耗尽错误。应用程序不该抛出这类型的错误对象。
Exception类层次结构又分为两支,一支派生于RuntimeException的异常,另一支是其它异常。
划分规则是由于程序错误导致的异常属于RuntimeException,曾经能够正常运行,现由于某些特定情况造成的不能运行不属于RuntimeException。
常见RuntimeException如数组越界,空指针以及错误的类型转换。常见非RuntimeException如IO操作异常。
JAVA中将所有派生于RuntimeException类或Error类的所有异常称为“未检查异常”,其它异常称为“已检查异常”。
一个方法不需要抛出从Error继承的异常,也不需要抛出从RuntimeException继承的异常未检查异常。一个方法可能抛出多个已检查异常,使用逗号分隔即可。
如果子类覆盖了超类的一个方法,那么子类抛出的已检查异常范围比超类的范围更小。
已存在异常抛出方法
FileNotFoundException e = new FileNotFoundException();
throw e;
定义自定义异常,自定义异常要么派生于Exception类,要么派生于Exception类的子类,一般提供两个构造方法。
class FileFormatException extends IOException
{
public FileFormatException(){};
public FileFormatException(String s)
{
super(s);
}
}
抛出自定义的异常与抛出已存在的异常方法一样。
异常捕获:
捕获异常需要使用try/catch语句,也可以捕获多个异常。
try
{
//.........
}
catch(ExceptionXXX e)
{
//.........
}
catch(ExceptionYYY e)
{
//.........
}
有些时候对于子系统类型,捕获到异常后需要抛出另外一个异常类型,
try
{
//........
}
catch(SQLException e)
{
throw new ServletException("database error: "+e.getMessage());
}
更好的处理办法是抛出子系统高级异常的同时保存原始异常。
try
{
//........
}
catch(SQLException e)
{
Throwable se = new ServletException("database error");
se.setCause(e);
throw se;
}
当捕获到异常的时候,可以使用Throwable e = se.getCause()方法获取原始异常。
finally子句:
对于有些方法中,在退出方法的时候需要释放资源,如果发生异常,则回收就会出现问题,因此提供了finally方法,
不论是否捕获异常,finally子句中的代码都会被执行。
使用finally的时候需要注意当finally子句中出现return语句的情况。
public static int f(int n)
{
try
{
int r=n*n;
return n;
}
finally
{
if(n==2) return 0;
}
}
当n为2时,执行到try中的return语句时,需要先执行finally中的语句,即return 0,最终返回结果为0。
堆栈跟踪元素分析:
堆栈跟踪(stack trace)是一个方法调用过程的列表,它包含了程序执行中方法调用的特定位置。
JDK1.4以前的版本中可以通过调用Throwable类的printStackTrace方法访问堆栈跟踪的文本描述。
现在可以使用getStackTrace方法获得一个StackTraceElement对象的数组,其中含有获取文件名、当前执行代码行号、类名以及方法名的方法。可以在程序中分析这个对象。
Throwable t = new Throwable();
StackTraceElement[] ele = t.getStackTrace();
for(StackTraceElement e:ele)
{
//........
System.out.println(e.toString());
}
JDK5.0中,增加了静态的Thread.getAllStackTrace()方法,可以产生所有线程的堆栈跟踪。
Map<Thread,StackTraceElement[]> map = Thread.getAllStackTrace();
for(Thread t:map.keySet())
{
StackTraceElement[] e = map.get(t);
//......................
}
打印递归阶乘堆栈跟踪情况
import java.util.*;
public class StackTraceTest01
{
public static int fact(int n)
{
System.out.println("fact("+n+");");
Throwable t = new Throwable();
/**/StackTraceElement[] e = t.getStackTrace();
for(StackTraceElement s:e)
{
System.out.println(s);
}
//t.printStackTrace();
int r;
if(n<1)
r=1;
else
{
System.out.println("此时n:"+n);
r=n*fact(n-1);
}
System.out.println("return:"+r);
return r;
}
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.println("Enter: n");
int n=in.nextInt();
fact(n);
}
}
二、日志
全局日志
Logger log = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
log.info("xxxxxxxx");
会输出Info日志,包含了时间调用类名和方法名。
不必将所有的日志都记录到全局日志记录器中,可以定义自己的日志记录器
Logger myLog = Logger.getLogger("xxxxxxx");
通常有以下7个日志记录级别SEVER,WARNING,INFO,CONFIG,FINE,FINER和FINEST。默认情况下只记录前三个级别。
可以使用setLevel方法设定日志级别,也可以使用log方法指定级别。
可以通过编辑配置文件来修改日志系统的各种属性,默认情况下,配置文件存在于jre/lib/logging.properties中。
修改默认日志记录级别,需修改logging.properties中.level选项即可
.level=INFO
也可以通过添加以下内容来指定自己的日志记录级别
日志记录名.level=FINE
日志记录器并不将消息记录器发送到控制台,这是处理器的任务,因此在jre/lib/logging.properties中还需要修改处理器级别
java.util.logging.ConsoleHandler.level=FINE
当然,如果要使用其它配置文件,可以使用下面命令启动应用程序
java -Djava.util.logging.config.file=xxxx.properties 程序名
本地化:
本地化的应用程序包含资源包中的本地说明信息。一个程序可以含有多个资源包。
每个资源包都有一个名字(例如“com.mycompany.logmessages”)。要将映射添加到资源包中,需要为每个地区添加一个文件。
例如英文消息映射位于com/mycompany/logmessage_en.properties文件中。这些文件为纯文本文件其中存储的是映射。类似
readingfile=Reading file
在请求日志记录器时,可以指定一个资源包
Logger logger = Logger.getLogger(logName,"com.mycompany.logmessages");
logger.info("readingfile");
有时需要在本地化的消息中添加一些参数,因此使用消息占位符{0},{1}等
readingfile=Reading file {0}
然后通过下面方法向占位符传递具体值
logger.log(Level.Info,"readingfile",filename);
处理器:
默认情况下,日志记录器将记录发送到ConsoleHandler中,然后在发送到System.err。
要将日志发送到其它地方,就需要添加其它的处理器,JDK中提供了FileHandler和SocketHandler。具体配置见JDK的API文档。
要想编写复杂的流处理器,应该扩展Handler类,并自定义publish、flush和close方法。
过滤器:
过滤器是对日志中内容的进一步细分,可以通过实现Filter接口并实现isLoggable方法来自定义过滤器
boolean isLoggable(LogRecord record)
格式化器:
对于处理器ConsoleHandler和FileHandler都可以通过配置项指定输出的日志格式,当然也可以通过扩展Formatter类实现format方法来自定义输出的日志格式
String format(LogRecord record)
在format中有可能调用formatMessage方法。
最后通过调用setFormatter(Formatter f)方法将格式化器安装到处理器即可。
断言:
在编写代码的过程中,可能会做出一些假设,而断言就是对这种假设的捕获。
例如在做sqrt运算的时候,断言被操作数大于0。
JDK1.4中引入了关键字assert,其有两种使用形式。
assert 条件;
和
assert 条件:表达式;
两个都会对条件进行判断,如果结果为false,则会抛出一个AssertionError异常。在第二个中表达式会被传入AssertionError的构造器。
断言机制允许在测试期间向代码中插入一些检查语句,当代码发布时,这些插入的检测语句将会被自动地移走。
使用JDK1.4版本时,需要告诉编译器使用了assert关键字,编译时使用-source 1.4选项,JDK5.0之后默认支持断言。
默认情况下断言是被禁用的,运行时可以通过-enableassertions或者-ea选项来启用它。
调试
JDK5.0中增加了对应用程序进行监控和管理的支持,允许使用虚拟机中的代理装置跟踪内存消耗、线程使用、类加载等情况。JDK加载了一个称为jconsole的图形工具,可以用于显示虚拟机性能的统计结果。
JDK6.0之后无需再使用Dcom.sun.management.jmxremote选项也可以。
java -Dcom.sun.management.jmxremote xxxxxx
jconsole PID