无需使用外部调试器即可跟踪 java 程序中变量更改的简单方法



所以我创建了一个类,我可以把它包含在我的程序类中,并从Thread开始,只要某些变量发生变化,它基本上会提醒我。我想知道是否有人有更好的想法来快速有效地获取此类信息,如果没有,他们是否对此类有任何建议。 仅供参考,alert()方法基本上是一个简单的System.out.println((。

public class OutputState extends Thread{
  String   className = this.getClass().toString().replace("class ","").replace("$OutputState",""); 
  String[] desc = {"canvasWidth","canvasHeight","relativeCenterX","relativeCenterY","zoom","hAdjust","vAdjust"};
  int[]    delay = {0,0,99,99,0,0,0,};
  Object[] var;
  int maxDescLength = 0;
  public void init(){
     Object[] v = {canvasWidth,canvasHeight,relativeCenterX,relativeCenterY,zoom,hAdjust,vAdjust};
     var = v;
  }
  public void run(){
     init();
     while(true){
        boolean newLine = false;
        Object[] v = {canvasWidth,canvasHeight,relativeCenterX,relativeCenterY,zoom,hAdjust,vAdjust};
        for(int i = 0; i < var.length; i++){
           if(maxDescLength < desc[i].length()){
              maxDescLength = desc[i].length();
           }
           if(!var[i].equals(v[i])){
              var[i]=v[i];
              String spaces = " ";
              int count = desc[i].length()+1;
              while(count <= maxDescLength){
                 spaces += " ";
                 count++;
              }
              alert(className + "." + desc[i] + spaces + "= " + var[i]);
              newLine = true;
              if(delay[i] > 0){try{Thread.sleep(delay[i]);}catch(InterruptedException e){}}
           }
        }
        if(newLine){
           alert("------------------------------------");
        }
        try{Thread.sleep(1);}catch(InterruptedException e){}
     }   
  }
} 

基本概念是v设置为您要跟踪的变量,并将继续使用程序中不断变化的变量进行自我更新,var将包含OutputState上次知道的变量。每当v中的任何内容发生变化时,var都会捕获它并输出自定义消息。我知道很多人会发现这样的东西很有用,这让我相信可能有更好的方法,但我还没有找到。我将非常感谢任何意见!

没有一个简单的方法来完成你想要做的事情。即使你循环(就像你目前所做的那样(,你不仅会减慢其余的计算速度(由于总线流量(,而且还可能会错过一些更新(如果监视器线程的调度频率较低,或者在比工作线程更繁忙的处理器上(。

就像评论中指出的那样,您最好使用一些面向方面的编程语言,或Java代理,或更简单的委托模式。无论哪种方式,都可能需要修改现有代码。

interface IWorkState {
    //Declares the method of WorkState
}
class WorkState implements IWorkState {
    //Whatever java bean
}
class WorkTask implements Runnable {
    private final IWorkState state;
    public void addStateObserver(IWorkState initialState) {
         state = initialState
    }
    @Override
    public void run() {
        //Task instructions here
        //instructions should operate on the state variable
    }
}
class VarMonitor {
    public void update(Object changed) {
        //subject has changed, react to it
        eventQueue.submit(/* a Runnable that handles the state change,
                           with embedded information about the change */);
    }
}
class MainClass {
    public static void main(String[] args) {
        final VarMonitor mon = new VarMonitor();
        WorkTask t = new WorkTask(Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {IWorkState.class}, new InvocationHandler() {
            private final WorkState realWorkState = new WorkState();
            Object invoke(Object proxy, Method method, Object[] args) {
                Object res = method.invoke(realWorkState, args);
                //An annotation would work better
                if (method.getName().substring(0,3).equals("set")) {
                    mon.update(this);
                }
            }
        });
    }
}

第三种选择是使用 java.util.Observable 的子类,当然还有观察者模式。像这样:

class WorkState extends java.util.Observable {
    //A thread-safe observable java bean
}
class WorkTask implements Runnable {
    private final WorkState state = new WorkState();
    public void addStateObserver(Observer ob) {
         state.addObserver(ob);
    }
    @Override
    public void run() {
        //Task instructions here
        //instructions should operate on the state variable
    }
}
class VarMonitor implements Observer {
    private final ExecutorService eventQueue
            = Executors.newSingleThreadExecutor();
    @Override
    public void update(Observable subject, Object info) {
        //subject has changed, react to it
        eventQueue.submit(/* a Runnable that handles the state change,
                           with embedded information about the change */);
    }
}

如果将此模式用于不可变对象,则可以轻松实现 ObservableReference 类,该类适用于所有对象。

PS:我之前没有提到过,但是对共享变量的多线程访问必须同步,或者变量必须是线程安全的或易失的。您不能盲目轮询共享变量,否则可能会得到一些错误的结果(例如查看部分更新的对象(。

对于每个包含要跟踪的属性的 bean,您可以添加 PropertyChangeSupport 和向其添加 PropertyChangeListener 的方法。 然后在 setter 中,您将 firePropertyChange 称为 PropertyChangeBack。 使用此配置,您甚至可以使用配置来决定要侦听的对象,并根据需要动态添加和删除侦听器。 你不需要一个单独的线程,你也不需要循环和休眠。 下面是一个小示例来演示。

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class ChangeTest {
  private String val1;
  private String val2;
  private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
  public String getVal1() {
    return val1;
  }
  public void setVal1(String val1) {
    pcs.firePropertyChange("val1", this.val1, val1);
    this.val1 = val1;
  }
  public String getVal2() {
    return val2;
  }
  public void setVal2(String val2) {
    pcs.firePropertyChange("val2", this.val2, val2);
    this.val2 = val2;
  }
  public void addPropertyChangeListener(PropertyChangeListener listener ) {
    pcs.addPropertyChangeListener(listener);
  }
  public static void main( String[] args ) {
    ChangeTest test = new ChangeTest();
    test.addPropertyChangeListener(new Listener());
    for ( int i = 0; i < 5; i++ ) {
      test.setVal1("value " + i );
      test.setVal2(test.getVal1() + test.getVal1());
    }
  }
  public static class Listener implements PropertyChangeListener {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
  System.out.println( evt.getPropertyName() + " changed from '" + evt.getOldValue() + "' to '" + evt.getNewValue() + "'" );
    }
  }
}

该程序的输出是:

val1 changed from 'null' to 'value 0'
val2 changed from 'null' to 'value 0value 0'
val1 changed from 'value 0' to 'value 1'
val2 changed from 'value 0value 0' to 'value 1value 1'
val1 changed from 'value 1' to 'value 2'
val2 changed from 'value 1value 1' to 'value 2value 2'
val1 changed from 'value 2' to 'value 3'
val2 changed from 'value 2value 2' to 'value 3value 3'
val1 changed from 'value 3' to 'value 4'
val2 changed from 'value 3value 3' to 'value 4value 4'

可靠地判断变量何时更改的唯一方法是将其声明为 private 并且仅通过 setter 方法进行更改。 然后编写setter,以便它测试以查看每个调用是否会更改变量的状态。

例如:

private int summit;
public void setSummit(int value) {
    if (value != summit) {
        System.err.println("Ey up: summit's changed!");
    }
}

如果变量的类型是数组,则无法可靠地判断数组元素何时更改...除非您隐藏阵列以防止直接访问。


如果你愿意使用AOP或其他形式的代码注入,你可以在不修改源代码的情况下做这种事情。 但在运行时,您将有效地执行上述操作。

最新更新