使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。
day052 调试技巧
假设编写了一个程序,并对所有的异常进行了捕获和恰当的处理,然后,运行这个程序,但还是出现问题,现在该怎么办呢?
当然,如果有一个方便且功能强大的调试器就太好了。调试器是Eclipse、NetBeans这类专业集成开发环境的一部分。在启动调试器之前,先给出一些有价值的建议。
1)可以用下面的方法打印或记录任意变量的值:
System.out.println("x="+x);
或
Logger.getClobal().info("x="+x);
如果X是一个数值,则会被转换成等价的字符串。如果x是一个对象,那么Java就会调用这个对象的toString方法。要想获得隐式参数对象的状态,就可以打印this对象的状态。
Logger.getClobal().info("this="+this);
Java类库中的绝大多数类都覆盖了toString方法,以便能够提供有用的类信息。这样会使调试更加便捷。在自定义的类中,也应该这样做。
2)一个不太为人所知但却非常有效的技巧是在每一个类中放置一个单独的main方法。这样就可以对每一个类进行单元测试。
public class MyClass
{
methods and fields
...
public static void main(String[]args)
{
test code
}
}
利用这种技巧,只需要创建少量的对象,调用所有的方法,并检测每个方法是否能够正确地运行就可以了。另外,可以为每个类保留一个main方法,然后分别为每个文件调用Java虚拟机进行运行测试。在运行applet应用程序的时候,这些main方法不会被调用,而在运行应用程序的时候,Java虚拟机只调用启动类的main方法。
3)如果喜欢使用前面所讲述的技巧,就应该到http://junit.org网站上査看一下JUnit。
JUnit是一个非常常见的单元测试框架,利用它可以很容易地组织测试用例套件。只要修改类,就需要运行测试。在发现bug时,还要补充一些其他的测试用例。
4)日志代理(loggingproxy)是一个子类的对象,它可以截获方法调用,并进行日志记录,然后调用超类中的方法。
例如,如果在调用Random类的nextDouble方法时出现了问题,就可以按照下面的方式,以匿名子类实例的形式创建一个代理对象:
Random generator = new
Random()
{
public double nextDouble0
{
double result =super.nextDoubleO();
Logger.getClobal().infoC'nextDouble: "+result);
return result;
}
};
当调用nextDouble方法时,就会产生一个日志消息。要想知道谁调用了这个方法,就要生成一个堆栈轨迹。
5)利用Throwable类提供的printStackTmce方法,可以从任何一个异常对象中获得堆栈情况。
下面的代码将捕获任何异常,打印异常对象和堆栈轨迹,然后,重新拋出异常,以便能够找到相应的处理器。
try
{
...
}
catch(Throwable t)
{
t.printStackTrace();
throw t;
}
不一定要通过捕获异常来生成堆栈轨迹。只要在代码的任何位置插入下面这条语句就可以获得堆栈轨迹:
Thread.duapStack();
6)—般来说,堆栈轨迹显示在System.err上。也可以利用printStackTrace(PrintWriter s)方法将它发送到一个文件中。
另外,如果想记录或显示堆栈轨迹,就可以采用下面的方式,将它捕获到一个字符串中:
StringWriter out = new StringWriter();
newThrowable().printStackTrace(new PrintWriter(out));
String description = out.toString();
7)通常,将一个程序中的错误信息保存在一个文件中是非常有用的。然而,错误信息被发送到System.err中,而不是System.out中。因此,不能够通过运行下面的语句获取它们:
java MyProgram > errors.txt
而是采用下面的方式捕获错误流:
java MyProgram2 > errors.txt
要想在同一个文件中同时捕获System.err和System.out,需要使用下面这条命令
java MyProgram 1> errors.txt 2>&1
这条命令将工作在bash和Windows shell中。
8)让非捕获异常的堆栈轨迹出现在System.err中并不是一个很理想的方法。
如果在客户端偶然看到这些消息,则会感到迷惑,并且在需要的时候也无法实现诊断目的。比较好的方式是将这些内容记录到一个文件中。可以调用静态的Thread.setDefaultUncaughtExceptionHandler方法改变非捕获异常的处理器:
Thread.setDefaultUncaughtExceptionHandlerf
new Thread.UncaughtExceptionHandler()
{
public void uncaughtException(Thread t, Throwable e)
{
save information in log file
};
});
9)要想观察类的加载过程,可以用-verbose标志启动Java虚拟机。
这样就可以看到如下所示的输出结果:
[Opened /usr/local/jdk5•0/jre/1i b/rt.ar]
[Opened /usr/local/jdk5.0/jre/lib/jsse.jar]
[Opened /usr/local/jdk5.0/jre/lib/jce.jar]
[Opened /usr/local/jdk5.0/j re/1i b/charsets.jar]
[Loaded java.lang.Object from shared objects file]
[Loaded java.io.Serializable from shared objects file]
[Loaded java.lang.Comparable from shared objects file]
[Loaded java.lang.Cha「Sequence from shared objects file]
[Loaded java.lang.String from shared objects file]
[Loaded java.1ang.reflect.GenericDeclaration from shared objects file]
[Loaded java.lang.reflect.Type from shared objects file]
[Loaded java.lang.reflect.AnnotatedElement from shared objects file]
[Loaded java.lang.Class from shared objects file]
[Loaded java.lang.Cloneable from shared objects file]
有时候,这种方法有助于诊断由于类路径引发的问题。
10)-Xlint选项告诉编译器对一些普遍容易出现的代码问题进行检査。
例如,如果使用下面这条命令编译:
javac-Xlint:fallthrough
当switch语句中缺少break语句时,编译器就会给出报告(术语“lint”最初用来描述一种定位C程序中潜在问题的工具,现在通常用于描述查找可疑但不违背语法规则的代码问题的工具。)
下面列出了可以使用的选项:
-Xlint 或 -Xlint:all 执行所有的检查
-Xlint:deprecation 与 -deprecation—样,检查废弃的方法
-Xlint:fall through 检查 switch 语句中是否缺少 break 语句
-Xlint:finally 警告 finally 子句不能正常地执行
-Xlint:none 不执行任何检查
-Xlint:path 检查类路径和源代码路径上的所有目录是否存在
-Xlint:serial 警告没有 serialVersionUID 的串行化类
-Xlint:unchecked 对通用类型与原始类型之间的危险转换给予警告
11)java虚拟机增加了对Java应用程序进行监控(monitoring)和管理(management)的支持。
它允许利用虚拟机中的代理装置跟踪内存消耗、线程使用、类加载等情况。这个功能对于像应用程序服务器这样大型的、长时间运行的Java程序来说特别重要。下面是一个能够展示这种功能的例子:JDK加载了一个称为jconsole的图形工具,可以用于显示虚拟机性能的统计结果,如图所示。找出运行虚拟机的操作系统进程的ID。在UNIX/Linux环境下,运行ps实用工具,在Windows环境下,使用任务管理器。然后运行jconsole程序:
jconsole processID
控制台给出了有关运行程序的大量信息。有关更加详细的信息参见www.orade.com/technetwork/articles/java/jconsole-1564139.html。
12)可以使用jmap实用工具获得一个堆的转储,其中显示了堆中的每个对象。
使用命令如下:
jmap -dump:format=b,file=dumpFileName processID
jhat dumpFileName
然后,通过浏览器进人loCalhost:7000,将会运行一个网络应用程序,借此探查转储对象时堆的内容。
13)如果使用-Xprof标志运行Java虚拟机,就会运行一个基本的剖析器来跟踪那些代码中经常被调用的方法。
剖析信息将发送给System.out。输出结果中还会显示哪些方法是由即时编译器编译的。
后面学习泛型程序设计和它最重要的应用:Java集合框架。