NPE,同时访问实例化和构造函数配置的不可变对象的原始类型(双精度)。 (不涉及自动装箱或反射)

Posted

技术标签:

【中文标题】NPE,同时访问实例化和构造函数配置的不可变对象的原始类型(双精度)。 (不涉及自动装箱或反射)【英文标题】:NPE while accessing a primitive type (double) of instantiated and constructor-configured immutable object. (no autoboxing or reflection involved) 【发布时间】:2012-04-12 14:14:02 【问题描述】:

这是我在下面引用的相关代码中遇到的奇怪异常:

23:51:39 在 java.lang.Thread.run(未知来源) 23:51:39 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(未知来源) 23:51:39 在 java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source) 23:51:39 在 com.dk.actions.c.run(未知来源) 23:51:39 在 com.dk.actions.TesterAction.yw(未知来源) 23:51:39 在 com.dk.actions.TesterAction.yX(未知来源) 23:51:39 在 com.dk.agent.tester.b.Bc(未知来源) 23:51:39 在 com.dk.agent.tester.r.run(未知来源) 23:51:39 在 com.dk.agent.tester.b.a(未知来源) 23:51:39 在 scal.Scal.onBar(Scal.java:241) 23:51:39 在 scal.Supres.evalSupres(Scal.java:2678) 23:51:39 在 scal.SR.supp(Scal.java:2187) 23:51:39 在 scal.SR.evaluateSRfor(Scal.java:2361) 23:51:39 在 scal.SR.isAtSR(Scal.java:2385) 23:51:39 在 scal.SR$Con.access$5(Scal.java:1741) 23:51:39 java.lang.NullPointerException

哦,在你问之前,是的,这个应用程序的所有类都在一个文件中。不要问为什么。就是这样。以下是对上述堆栈跟踪的代码引用:

ref. scal.Scal.onBar(Scal.java:241):
        try
        for(Ins instr : supresSourceMap.keySet())
            for(Per p : supresSourceMap.get(instr).keySet())
    241:        supresSourceMap.get(instr).get(p).evalSupres(currTime);
             catch (Exception e)
                e.printStackTrace(console.getErr());
            
ref. scal.Supres.evalSupres(Scal.java:2678):
    public void evalSupres(long time) throws Exception
        ...
    2678:   sup.supp(Con.of(getIns(), getPer(), center, time, conRange, true), null);
        ...
    
ref. scal.SR.supp(Scal.java:2187):
    void supp(Con nHt, Con remove)
        ...
    2187    evaluateSRfor(nHt);
        ...
    
ref. scal.SR.evaluateSRfor(Scal.java:2361):
    private void evaluateSRfor(Con nHt) 
        if(!hits.get(nHt.per).isEmpty())
            Con lastHt = getLastHt(nHt.per);
            if(lastHt != null)
                if(lastHt.srSource == null)
                    if(isNewSR(nHt))
                        addNewSR(nHt);
                    
                else
    2361:           if(isAtSR(nHt))
                        addConToLastSR(nHt);
                    

                

            
        
    
ref. scal.SR.isAtSR(Scal.java:2385):
            private boolean isAtSR(Con nHt) 
                ...
    2385:       double high = nHt.getHighestCon().upperConBound;
                ...
            
        ref. nHt.getHighestCon() :
            Con getHighestCon()
                Con highCon = null;
                boolean contains = false;
                if(this.srSource != null)
                    highCon = srSource.getFirst();
                    for(Con con : srSource)
                        if(!contains)
                            contains = this.equals(con);
                        if (con.compareTo(highCon) > 0) 
                            highCon = con;
                        
                    
                if(!contains) throw new IllegalStateException("getHighestCon(): " + this.toString() + " does not belong to its srSource list: " + srSource.toString());
                
                return highCon;
            
ref. scal.SR$Con.access$5(Scal.java:1741):
    1741:   private final double upperConBound;

重要提示:

srSource 是 LinkedList 类型的 Con 类中的一个字段。 方法 getHighestCon() 在 Con 类型中定义。 Con 类型是类型 SR 中的静态内部类。 Con 类型是不可变的,尽管 srSource 列表不是最终的,并且稍后会通过 setter 方法进行实例化和填充。 每个 Con 实例都在 srSource 列表中保存对自身的引用。 我使用 hashCode()、equals()、toString()、Comparable (compareTo(Conf c)) 实现了 Con 类型。他们都没有在计算中使用 srSource 字段。 字段“private final double upperConBound”通过静态方法从 Con 的构造函数初始化: this.upperConBound = value + Utils.pValue(ins, conRange); 如果我通过方法而不是直接访问该字段,问题不会消失。 但是,当我像这样从 isAtSR() 访问 upperConBound 字段时,问题就消失了: 双高 = getHighestCon(srSource).upperConfBound; 在哪里: srSource SR 实例的字段和 方法 getHighestCon(LinkedList srSource) 在 SR 类型中的实现方式与在 Con 类型中的实现方式相同,但访问的是参数而不是字段,并且不会引发异常。 请记住,上面的解决方案不是我想要的解决方案。我需要在 Con 类型中实现和工作 getHighestCon() 方法。 如果您有任何问题或需要更多代码示例,请告诉我。感谢您花时间为我解决这个问题。

【问题讨论】:

啊啊,代码太多了。请将此归结为minimal test-case。 你能用更小的代码重现异常吗?你能把它归结为 10-15 行来证明这个问题吗? 什么是JVM?是 Sun 还是像 JRocket 这样的定制? java -version 给出以下内容: java 版本 "1.6.0_21" Java(TM) SE Runtime Environment (build 1.6.0_21-b07) Java HotSpot(TM) 64-Bit Server VM (build 17.0) -b17,混合模式) 抱歉,这段代码会引起呕吐反射。 【参考方案1】:

字段“private final double upperConBound”由Con的构造函数通过静态方法初始化

类源代码内的Java 初始化程序顺序非常重要。 尝试将 upperConBound 字段声明和静态初始化程序放在 Con 类源代码中的任何其他内容之前。

【讨论】:

以上是关于NPE,同时访问实例化和构造函数配置的不可变对象的原始类型(双精度)。 (不涉及自动装箱或反射)的主要内容,如果未能解决你的问题,请参考以下文章

来自 C++ 的 QQuickItem 实例化和设置难题(不允许将任何内容传递给构造函数)

不可变类/对象、私有构造函数、工厂方法

谈谈JS构造函数

C++类和对象

C++初阶---类和对象

JS的构造函数