无法解释的字段值变化

发布于 2024-10-06 19:29:28 字数 2361 浏览 0 评论 0原文

看看这个奇怪的问题:

调试步骤

  1. 我对字段 res1
  2. Res1 被赋值为 f 的值,即“bar”
  3. Res1 现在是“bar”
  4. 在下一次中断时,res1 突然变为 null

为什么这是不可能的?

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

可能会发生什么?我假设这不是 JVM 中的错误,但谁知道呢。


正如乔恩·斯基特所说,问题在于实例不同。这是由这个奇怪的反射问题引起的:

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>

Take a look at this strange issue:

Debugging steps

  1. I have a breakpoint on all access to the field res1
  2. Res1 is assigned the value of f, which is "bar"
  3. Res1 now is "bar"
  4. At the next break, res1 is suddenly null

Why is this impossible?

  • Because of the breakpoint on res1, I can see that it shouldn't have changed at all
  • And it couldn't, because I don't explicitly change it between the assignment and the assertEquals and this code is running in a single JUnit thread.
  • There are no other fields or variables named res1.

What could be up? I'm assuming it's not a bug in the JVM, but who knows.


As Jon Skeet said, the problem is that the instances are different. This is caused by this strange reflection issue:

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
    }

}

What happens when the tests are run:

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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情泪▽动烟 2024-10-13 19:29:28

猜测:您在断言阶段看到的类实例与设置为“bar”时的实例不同。

不过,如果不看到其余代码,就很难判断。

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

At a guess: you're looking at a different instance of the class during the assertion phase than you were when it was set to "bar".

It's hard to tell without seeing the rest of the code though.

EDIT: The problem is that your opsByClass cache is going to include an Operation, which implicitly has an associated ServiceObject. So when you create the first ServiceObject with a Foo, it will cache Operation instances associated with that first instance of ServiceObject. When you call svc.execute on the second instance of ServiceObject, it will look up the operation in the map, and find the Operation associated with the first instance... which probably isn't what you intended.

负佳期 2024-10-13 19:29:28

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

I'm guessing you've got your code spanning different tests.
Suggest you do the setup in the onSetUp() method

滴情不沾 2024-10-13 19:29:28

我找到了。问题是,因为 Operation 不是 static,它引用的是最初创建的 ServiceObject,而不是第二次调用的那个。执行于。

I found it. The problem is that because Operation is not static, it refers to the ServiceObject it was initially created by, not the one which the second call is executed upon.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文