【问题标题】:Setting object to null and program flow in Java在Java中将对象设置为null和程序流程
【发布时间】:2015-09-07 00:16:44
【问题描述】:

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

我有一个类,我在其中创建一个对象。这个对象是一个场景。这是一个 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

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

我真的很想了解 Java 中的工作原理 - 为什么它似乎在一分钟内(或从一个地方)为 null 而不是(从另一个地方)? p>

【问题讨论】:

    标签: java nullpointerexception null program-flow


    【解决方案1】:

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

    混淆似乎是关于“将对象设置为空”。那是不可能的。您只能将指向对象的变量设置为 null。在此之后,变量指向 null,但对象可能仍然存在(如您的情况)。

    具体来说,你在这里把它交给场景管理器:

    SceneManager.getInstance().setCurrentScene(myScene);
    

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

    【讨论】:

    • 谢谢@StefanHaustein,好的,这很有道理。我可能误解了一些事情。因此,因为“对象”仍在使用中(它的 render() 和 updateLogic() 方法仍在被调用),它不能被 GC 处理,因此在操作系统认为它是“免费的”之前一直保持活动状态' - 即使在实例化时分配给它的实际引用现在为空(并且不存在其他实际的“引用”,即,像 this.myScene = myScene 在某个其他类的某个地方)?
    • 我认为场景经理还在坚持,相应地澄清了我的答案。
    • 仍然有对对象的引用,因为调用堆栈必须从对象上的实例方法返回。另外,正如 stefan 所说,对象为空,渲染器中的引用是。
    【解决方案2】:

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

    除非您“安全地发布”值的更改,否则其他线程可以看到变量的陈旧值。 (您有一个 CPU 更改了其缓存行中的值,但另一个 CPU 仍然看到过时的缓存数据 - 您需要强制缓存写入主内存,但这很昂贵,因此 Java 在被告知之前不会这样做)。

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

    private volatile Scene myScene;
    

    如果你想正确理解 Java 中的线程安全,那么我强烈推荐“培训手册”:http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601

    【讨论】:

    • 我试着让它变得易变,但我得到了同样的结果——没有崩溃,它运行得很好。这真的是一个线程安全问题吗?我的意思是我在同一个线程(GL 渲染线程)上运行,然后调用 cleanUp();也来自同一个线程的方法....
    猜你喜欢
    • 2011-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-18
    • 2010-10-09
    • 1970-01-01
    • 2010-10-25
    相关资源
    最近更新 更多