将对象设置为 null 并在 Java 中执行程序流程

Setting object to null and program flow in Java

我需要一些帮助来理解如何在 java 中将对象设置为 null。我有这样一种情况,乍一看似乎设置为 null 的对象突然不为 null,但显然情况并非如此。

我有一个 class,我在其中创建了一个对象。这个对象是一个场景。这是一个 Open GL ES 2.0 项目,因此场景的 render() 和 updateLogic() 方法是从 onDrawFrame 调用的(这是通过场景管理器控制的,因此我们可以轻松切换场景)。

所以,我可能有这样的东西(为了问题的目的而删减代码):

public class MyGLRenderer implements GLSurfaceView.Renderer{

    MyScene myScene;
    SomeOtherScene someOtherScene;

    public void createScenes(){
        myScene = new MyScene(this);
        someOtherScene = new SomeOtherScene(this);
        SceneManager.getInstance().setCurrentScene(myScene);
    }

    public void cleanUp(){
        myScene = null;
        Log.v("tag","myScene (from MyGLRenderer) is: "+myScene);
        SceneManager.getInstance().setCurrentScene(someOtherScene);   //Scene changed but this won't take effect until the next 'tick'   
    } 

    @Override
    public void onDrawFrame(GL10 gl) {
        SceneManager.getInstance().getCurrentScene().updateLogic();
        SceneManager.getInstance().getCurrentScene().render();
    }

}

在上述情况下,处理被移交给 myScene,它看起来像这样:

public class MyScene implements Scene{

    MyGLRenderer renderer;

    public myScene(MyGLRenderer renderer){     
        this.renderer = renderer;
    }

    @Override
    public void render(){
        //Render something here
    }

    @Override
    public void updateLogic(){
        doSomething();           
        //The condition here could be anything - maybe the user taps the sceen and a flag is set in onTouchEvent for example
        if (someConditionIsMet){
            renderer.cleanup();
        }            
         Log.v("tag","myScene (from within myScene) is: "+this);
    }
}

所以,当我使用我的场景管理器设置场景时,处理被移交给该场景,它的 updateLogic 和渲染方法从 onDrawFrame 连续调用。

当我 运行 我的代码时,我很惊讶它没有因 NullpointerException 而崩溃。日志是这样的:

myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from within myScene) is: com.program.name.MyScene@26354632
myScene (from MyGLRenderer) is: null
myScene (from within myScene) is: com.program.name.MyScene@26354632

如您所见,在调用 cleanUp() 方法并将其设置为 null 之前,'myScene' 一直有效。但是代码然后 returns 到 myScene 完成,它仍然有效(不为空)。

我真的很想了解 Java 中的工作原理 - 为什么 似乎 一分钟(或从一个地方)为空,然后又没有(来自不同的地方)?

您只是在渲染器中清除了对 MyScene 的引用,但该对象仍然存在并且 SceneManager 可能仍然保留着它。

混淆似乎与 "setting an object to null" 有关。这真的不可能。您只能将指向对象的变量设置为空。此后,变量指向 null,但对象可能仍然存在(如您的情况)。

具体你这里交给现场管理员:

SceneManager.getInstance().setCurrentScene(myScene);

只有在没有任何对象持有对它的引用时,该对象才会被垃圾回收。在您的情况下,这可能是场景更改生效的时间。

看起来您 运行 遇到了线程安全错误。

其他线程可以看到变量的陈旧值,除非您 "safely publish" 更改值。 (你有一个 CPU 在它的缓存行中改变了一个值,但是另一个 CPU 仍然看到陈旧的缓存数据 - 你需要强制缓存写入主内存但是这是昂贵的所以 Java 在被告知之前不会这样做)。

有很多方法可以根据您的具体要求安全地发布值。查看您的代码,似乎您需要做的就是将 myScene 字段声明为易变的。它可能也应该是私人的。所以尝试:

private volatile Scene myScene;

如果你想正确理解Java中的线程安全,那么我强烈推荐"the Train Book":http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601