调试器是 Eclipse 、 NetBeans 这类专业集成开发环境的一部分 。 在启动调试器之前, 本节先给出一些有价值的建议 。
1 ) 可以用下面的方法打印或记录任意变量的值 :
System.out.println("x=" + x);
// 或者
Logger.getGlobal().info("x=" + x)
2 ) 一个不太为人所知但却非常有效的技巧是在每一个类中放置一个单独的 main 方法 。这样就可以对每一个类进行单元测试 。
public class MyClass
{
methods and fields
...
public static void main ( String[] args)
{
test code
}
}
3) 使用单元测试框架例如 JUnit
4) 日志代理(logging proxy)是一个子类对象,他可以截获方法按调用,并进行日志记录,然后调用超类中的方法。例如,如果在调用 Random 类的 nextDouble 方法时出现了问题,就可以按照下面的方式,以匿名子类实例的形式创建一个代理对象:
public static void main(String[] args){
Random generator = new Random(){
@Override
public double nextDouble() {
double result = super.nextDouble();
Logger.getGlobal().info("nextDouble: " + result);
return result;
}
};
System.out.println(generator.nextDouble());
}
当调用 nextDouble 方法时 , 就会产生一个日志消息 。 要想知道谁调用了这个方法, 就要生成一个堆栈轨迹 。
运行结果:
5. 利用 Throwable 类提供的 pringStackTrace 方法,可以从任何一个异常对象中获得堆栈情况。下面的代码将捕获任何异常,打印异常对象和堆栈轨迹,然后,重新抛出异常,以便能够找到相应的处理器。
try{
...
}catch(Throwable t){
t.printStackTrace();
throw t;
}
不一定要通过捕获异常来生成堆栈轨迹。只要在代码的任何位置插入下面这条语句就可以获得堆栈轨迹:
Thread.dumpStack();
6) 一般来说,堆栈轨迹显示在 System.err 上。也可以利用printStackTrace(PrintWriter s)方法将它发送到一个文件中。另外,如果想记录或者显示堆栈轨迹,就可以采用下面的方式,将它捕获到一个字符串中:
StringWriter out = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(out));
String description = out.toString;
7) 通常,将一个程序的错误信息保存在一个文件中是非常有用的。然而,错误信息被送到 System.err 中,而不是 System.out 中。因此,不能够通过运行下面的语句获取它们:
java MyProgram > errors.txt // 这里只是将标注输出【即 System.out】内容重定向到 errors.txt 中
// 正确的做法是
java MyProgram 2> errors.txt // 这里就是将标准出错【即 System.out】 内容重定向到 errors.txt
// 要想在同一个文件中同时捕获 System.out 和 System.err,需要这样做
java MyProgram 1> errors.txt 2>&1 // 1> errors.txt 表示将 System.out 重定向到 errors.txt;2>&1 表示将 System.err 重定向到 System.out 中
// 上述命令将在bash 和 windows shell 中运行
// PS: 操作系统一般都有这三个三个I/O,分别是标准输入、标准输出、标准出错。分别对应Java中的 System.in System.out System.err
// “>" 重定向符号 ">>" 可以追加的重定向
8) 让非捕获异常[即运行时异常 RuntimeException,非检查异常]的栈轨迹出现在 System.err 中并不是一个很理想的方法。如果在客户端偶然看到这些消息,则会感到迷惑,而且在需要的时候也无法实现诊断目的。比较好的方式是将这些内容记录到一个文件中。keyi调用静态的 Thread.setDefaultUncaughtExceptionHandler 方法改变非捕获异常的处理器。
new Thread.UncaughtExceptionHandler(){
@Override
public void uncaughtException(Thread t, Throwable e) {
// save information in log file
Logger.getGlobal().throwing("com.mycompany.myapp", "main", e);
}
}
9) 要想观察类的加载过程,可以用 -verbose 标志启动 Java 虚拟机。这样就可以看到如下所出书结果:
有时候 , 这种方法有助于诊断由于类路径引发的问题 。
10 ) -Xlint 选项告诉编译器对一些普遍容易出现的代码问题进行检査 。 例如 , 如果使用下面这条命令编译 :
javac -Xlint : fallthrough
当 switch 语句中缺少 break 语句时 , 编译器就会给出报告(术语 “ lint ” 最初用来描述一种定位 C 程序中潜在问题的工具 , 现在通常用于描述查找可疑但不违背语法规则的代码问题的工具 。 )
下面列出了可以使用的选项 :
11) java 虚拟机增加了对 Java 应用程序进行监控 (monitoring ) 和管理 ( management ) 的支持 。 它允许利用虚拟机中的代理装置跟踪内存消耗 、 线程使用 、 类加载等情况 。 这个功能对于像应用程序服务器这样大型的 、 长时间运行的 Java 程序来说特别重要 。 下面是一个能够展示这种功能的例子 : JDK 加载了一个称为 jconsole 的图形工具 , 可以用于显示虚拟机性能的统计结果, 如图 7 -3 所示 。 找出运行虚拟机的操作系统进程的 ID 。 在 UNIX / Linux 环境下 ,运行 ps 实用工具, 在 Windows 环境下, 使用任务管理器 。 然后运行 jconsole 程序 :
jconsole processID【即进程ID】
控制台给出了有关运行程序的大量信息 。有关更加详细的信息参见 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 。 输出结果中还会显示哪些方法是由即时编译器编译的 。