- 用下面的方法打印或记录任意变量的值
System.out.println("x="+x);
或Logger.getGlobal().info("x="+x);
如果x是一个数值,则会被转换成等价的字符串。如果x是一个对象,那么Java会调用这个对象的toString方法。要想获得隐式参数对象的状态,可以打印this对象的状态
Logger.getGlobal().info("this=+this);
Java类库中的绝大多数类都覆盖toString方法,以便能提供有用的类信息。这样会使调试更加便捷。在你自定义的类中也应该这么做。 - 可以在每一个类中放一个单独的main方法。这样就能提供一个单元测试桩,能独立地测试类。
上述方法代码说明
package com.zhd.DebugTest;
import java.util.logging.Logger;
public class DebugTest {
private String name="baby";
public static void main(String[] args){
int age=22;
var player=new DebugTest();
System.out.println("age="+age);//输出值
System.out.println("this"+player);//输出对象状态
Logger.getGlobal().info("age="+age);//日志记录值
player.setName("33");
}
public void setName(String name) {
this.name = name;
Logger.getGlobal().info("this:"+this);//获得隐式参数对象的状态
}
@Override
public String toString() {
return "DebugTest{" +
"name='" + name + '\'' +
'}';
}
}
- JUnit是个非常流行的单元测试框架,利用他们可以很容易组织测试用例套件,https://junit.org/junit5/docs/current/user-guide/
- 日志代理(logging
proxy)是一个子类的对象,它可以截获方法调用,记录日志,然后调用超类中的方法。例如,如果在调用Random类的nextDouble方法时出现了问题,就可以如下创建一个代理对象,这是一个匿名子类的实例:
var generator=new Random(){
public double nextDouble(){
double result=super.nextDouble();
Logger.getGlobal().info("nextDouble:"+result);
return result;
}
}
只要调用nextDouble 方法,就会生成一个日志消息。要想知道谁调用这个方法,可以生成一个堆栈轨迹
代码说明
package com.zhd.DebugTest;
import java.util.Random;
import java.util.logging.Logger;
public class DebugTest {
private String name="baby";
private double getNumber;
public static void main(String[] args){
int age=22;
var player=new DebugTest();
System.out.println("age="+age);//输出值
System.out.println("this"+player);//输出对象状态
Logger.getGlobal().info("age="+age);//日志记录值
player.setName("33");
var generator=new Random(){//创建匿名子类
public double nextDouble(){
double result=super.nextDouble();
Logger.getGlobal().info("nextDouble:"+result);//截获方法调用 记录日志
Thread.dumpStack();//获得堆栈轨迹
return result;
}
};
player.setGetNumber(generator.nextDouble());
}
public void setGetNumber(double getNumber) {
this.getNumber = getNumber;
}
public void setName(String name) {
this.name = name;
Logger.getGlobal().info("this:"+this);//获得隐式参数对象的状态
}
@Override
public String toString() {
return "DebugTest{" +
"name='" + name + '\'' +
'}';
}
}
Thread.dumpStack();//获得堆栈轨迹
运行截图
- 利用Throwable类的printStackTrace方法可以从任意的异常对象获得堆栈轨迹。下面的代码将捕获任意异常,打印这个异常对象和堆栈轨迹,然后重新抛出异常,以便找到相应的处理器。
- 代码说明
//方法一
try{
//给出一个有隐患的例子 除数为零
Scanner input = new Scanner(System.in);
System.out.println("Enter two integers: ");
int num1 = input.nextInt();
int num2 = input.nextInt();
System.out.println(num1 / num2);
}catch(Throwable t){
t.printStackTrace();
throw t;
}
//方法二
// Scanner input = new Scanner(System.in);
// System.out.println("Enter two integers: ");
// int num1 = input.nextInt();
// int num2 = input.nextInt();
// System.out.println(num1 / num2);
方法二 不用printStackTrace
方法一
可以看到确实将捕获任意异常,打印这个异常对象和堆栈轨迹,然后重新抛出异常
当然也不一定非要通过捕获异常来生成堆栈轨迹,在代码的某个位置插入这个Thread.dumpStack();
也可以获得堆栈轨迹
- 一般来说,堆栈轨迹显示在System.err上。如果想要记录或显示堆栈轨迹,可以如下将它捕获到一个字符串中:
var out=new StringWriter();
new Throwable().printStackTrace(new PrintWriter(out));
String description=out.toString();
System.out.println(description);
输出如下
- . 通常将程序错误记录到一个文件中会很有用。不过错误会发送到System.err,而不是System.out
所以不能通过运行java Myprogram>errors.txt
而应当用java MyProgram 2>error.txt
是会在你的包内生成一个errors.txt文件的
要想在同一个文件中同时捕获System.err 和System.out,需要使用以下命令java DebugTest 1> errors.txt 2>&1
- 在System.err中显示未捕获的异常的堆栈轨迹不是理想方法,更好的是把这些方法记录到一个文件中。可以用静态方法Thread.setDefaultUncaughtExceptionHandler改变未捕获异常的处理器
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){
public void uncaughtException(Thread t,Throwable e){
//save information in log file
// Logger.getGlobal().info(t + " throws exception: " + e);
}
});
说是save information in log file ,我就直接把他放全局日志管理器了,不知道具体怎么操作,里面的信息t + " throws exception: " + e
应该就是这个,把这个放到具体文件应该就好
- 要想观察类的加载过程,启用java虚拟机是可以使用
-verbose
标志。这样就可以看到
老大一堆这玩意儿 这对玩意儿对
诊断类路径问题会很有帮助
划重点
- -Xlint选项告诉编译器找出常见的代码问题。例如,如果使用下面这条命令编译
javac -Xlint sourceFiles
当switch语句中缺少break语句时,编译器就会报告这个问题
下面简单的加了点代码
switch (age){
case 22://这里没加break;
System.out.println("I am " + age);
}