原因不明的字段值发生变化

时间:2010-12-09 13:02:35

标签: java debugging reflection variable-assignment

看看这个奇怪的问题:

Debugging steps

  1. 我对所有访问字段res1
  2. 都有断点
  3. 为Res1分配值f,即“bar”
  4. Res1现在是“bar”
  5. 在下一次休息时,res1突然null
  6. 为什么这不可能?

    • 由于res1上的断点,我可以看到它根本不应该改变
    • 并且它不能,因为我没有在赋值和assertEquals之间明确地更改它,并且此代码在单个JUnit线程中运行。
    • 没有名为res1的其他字段或变量。

    什么可能?我假设它不是JVM中的错误,但谁知道。


    正如Jon Skeet所说,问题在于实例不同。这是由这个奇怪的反思问题造成的:

    public class ServiceObject {
    
        private static final Map<Class<?>, Map<String, Operation>> opsByClass =
            new ConcurrentHashMap<Class<?>, Map<String,Operation>>();
    
        private final Object target;
        private final Map<String, Operation> myClassOps;
    
        private class Operation {
            private final Method method;
            public Operation(Method met) {
                this.method = met;
                method.setAccessible(true);
            }
            public void execute(Map<String,?> args, Responder responder, Object context, boolean coerce) {
                ...
                method.invoke(target, mArgs);
            }
        }
    
        public ServiceObject(Object target) {
            Class<?> myClass = target.getClass();
            Map<String, Operation> op = opsByClass.get(myClass);
            if (op == null) {
                op = new HashMap<String, Operation>();
                for (Method meth : myClass.getMethods()) {
                    ...
                    op.put(opName, new Operation(meth));
                }
                opsByClass.put(myClass, op);
            }
            this.target = target;
            this.myClassOps = op;
        }
    
        public void execute(String opName) {
            Operation op = myClassOps.get(opName);
            ...
            op.execute(args, responder, context, coerce);
        }
    }
    
    // Foo is equivalent to the class that contains <res1> above
    class Foo {
    
        @ServiceOperation
        public void bar() {
            // breakpoint here
        }
    
    }
    

    运行测试时会发生什么:

    a = new Foo();
    b = new Foo();
    svc = new ServiceObject(a);
    svc.execute("bar", ...); // inside Foo.bar() <this> will be <a>
    svc = new ServiceObject(b);
    svc.execute("bar", ...); // inside Foo.bar() <this> will still be <a>, but should be <b>
    

3 个答案:

答案 0 :(得分:4)

猜测:在断言阶段,你正在查看类的不同实例,而不是设置为“bar”时的类。

虽然没有看到其余的代码,但很难说。

编辑:问题是您的opsByClass缓存将包含一个操作,该操作隐式具有关联的ServiceObject。因此,当您使用ServiceObject创建第一个Foo时,它将缓存与第一个ServiceObject实例关联的操作实例。当您在svc.execute第二个实例上调用ServiceObject时,它将在地图中查找操作,并找到与第一个实例关联的Operation。 ..这可能不是你想要的。

答案 1 :(得分:0)

我猜你的代码跨越了不同的测试。
建议你在onSetUp()方法中进行设置

答案 2 :(得分:0)

我找到了。问题在于,因为Operation不是static,所以它指的是最初创建的ServiceObject,而不是执行第二次调用的{{1}}。