Java 为什么我得到的NPE只有在程序运行时才会偶尔出现?

Java 为什么我得到的NPE只有在程序运行时才会偶尔出现?,java,debugging,junit,bluej,Java,Debugging,Junit,Bluej,我正在使用BlueJ中的JUnit为我的GiftSelector类编写一个测试类。当我运行testGetCountForAllPresents()方法时,我在以下行中得到一个NullPointerException: assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 3); 这个NPE的奇怪之处在于,当我运行一次测试时,它很少出现,但在我运行第二次测试时经常出现。有时,直到我连续运行了7-8次测

我正在使用BlueJ中的JUnit为我的
GiftSelector
类编写一个测试类。当我运行
testGetCountForAllPresents()
方法时,我在以下行中得到一个
NullPointerException

assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 3);
这个NPE的奇怪之处在于,当我运行一次测试时,它很少出现,但在我运行第二次测试时经常出现。有时,直到我连续运行了7-8次测试,它才会出现

我收到的错误消息是: 没有异常消息

礼品选择测试中第215行的NPE。testGetCountForAllPresents

我的测试类的代码是:

import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * The test class GiftSelectorTest. The GiftSelector that you are 
 * testing must have testMode enabled for this class to function. 
 * This is done in the setUp() method.
 */
public class GiftSelectorTest
{
    private GiftList giftList1;
    private GiftList giftList2;
    private GiftList giftList3;
    private Child jack;
    private Child bob;
    private Child dave;
    private Child naughty1;
    private GiftSelector santasSelector;
    private Present banana1;
    private Present orange;
    private Present banana;
    private Present apple;
    private Present bike;
    private Present doll;
    private Present got;
    private Present pearlHarbour;
    private Present dog;
    private Present cat;
    private Present ball;
    private Present heineken;

    /**
     * Default constructor for test class GiftSelectorTest
     */
    public GiftSelectorTest()
    {
        //Nothing to do here...
    }

    /**
     * Sets up the test fixture.
     *
     * Called before every test case method.
     */
    @Before
    public void setUp()
    {
        santasSelector = new GiftSelector();
        santasSelector.setTestMode(true);
        jack = new Child("Jack", 20, "1 A Place", true, true, true, false);
        bob = new Child("Bob", 10, "2 A Place", true, true, true, true);
        dave = new Child("Dave", 10, "3 A Place", true, true, true, true);
        naughty1 = new Child("John", 5, "4 A Place", true, true, true, true);
        giftList1 = new GiftList(jack);
        giftList2 = new GiftList(bob);
        giftList3 = new GiftList(dave);
        banana = new Present("banana", "fruit", 10);
        orange = new Present("orange", "fruit", 10);
        banana1 = new Present("banana", "fruit", 10);
        apple = new Present("apple", "fruit", 10);
        bike = new Present("bike", "toy", 200);
        doll = new Present("doll", "toy", 40);
        got = new Present("game of thrones", "dvd", 50);
        pearlHarbour = new Present("pearl harbour", "dvd", 20);
        dog = new Present("dog", "animal", 100);
        cat = new Present("cat", "animal", 80);
        ball = new Present("ball", "toy", 5);
        heineken = new Present("heineken", "beer", 1.60);
    }

    /**
     * Tears down the test fixture.
     *
     * Called after every test case method.
     */
    @After
    public void tearDown()
    {
        //Nothing to do here...
    }


    @Test
    public void testGetCountForAllPresents()
    {
        System.out.println(santasSelector.getCountsForAllPresents());
        //Test on empty GiftSelector
        assertNull(santasSelector.getCountsForAllPresents());

        //Test on a GiftSelector with one giftlist containing one present
        giftList1.addPresent(banana);
        santasSelector.addGiftList(giftList1);
        System.out.println(santasSelector.getCountsForAllPresents());
        assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 1);

        //Test when GiftSelector contains 2 giftlists, each containing the same present object

        giftList2.addPresent(banana);
        santasSelector.addGiftList(giftList2);
        System.out.println(santasSelector.getCountsForAllPresents());
        assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 2);

        //Test when GiftSelector contains 3 giftlists, 2 containing the same present object and another containing an identical present but with a different present instance
        giftList3.addPresent(banana1);
        santasSelector.addGiftList(giftList3);
        System.out.println(santasSelector.getCountsForAllPresents());
        assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 3); //This is the line I get the NPE

        //Test when GiftSelector contains 3 giftLists, the first with one with a banana, the second with a banana and apple, and the third with a banana1 and ball
        giftList2.addPresent(apple);
        giftList3.addPresent(ball);
        System.out.println(santasSelector.getCountsForAllPresents());
        assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 3);
        assertEquals(true, santasSelector.getCountsForAllPresents().get(apple) == 1);
        assertEquals(true, santasSelector.getCountsForAllPresents().get(ball) == 1);

    }


    @Test
    public void testGetMostPopularPresent()
    {
        //Test on empty GiftSelector
        assertNull(santasSelector.getMostPopularPresent());

        //Test on a GiftSelector with one giftList and one Present
        giftList1.addPresent(heineken);
        santasSelector.addGiftList(giftList1);
        assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(heineken));

        //Tset on a GiftSelector with 1 giftList and 2 presents, one more expensive than the other
        giftList1.addPresent(banana);
        assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(banana));

        //Test on a GiftSelector with 1 giftList and 3 presents. Banana and Apple are equal in price, and are both in the top3, 
        //therefore it should return the present closest to the start of the list
        giftList1.addPresent(apple);
        assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(banana) || santasSelector.getMostPopularPresent().comparePresent(apple));

        //Test on a GiftSelector with 2 giftLists, the second list containing banana1, an indentical present to banana
        giftList2.addPresent(banana1);
        santasSelector.addGiftList(giftList2);
        assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(banana));

        //Test on a GiftSelector with 2 giftLists, the first containing four presents and the second containing 2 presents.
        //This tests to see if top3 is working.
        giftList1.addPresent(bike);
        giftList2.addPresent(bike);
        assertEquals(true, santasSelector.getMostPopularPresent().comparePresent(bike));
    }
}
我只介绍了引用
getCountsForAllPresents()
方法的测试方法。您会注意到,我在每次调用包含
getCountForAllPresents()
方法的
assertEquals()
方法之前都添加了print语句。有趣的是,在我获取NPE的行之前,print语句为
getCountForAllPresents()
返回的
HashMap
打印出正确的值

我注意到的另一件奇怪的事情是,当我使用BlueJ的内置调试器执行
testGetCountForAllPresents()
方法时,我注意到
SantaAssetElector
中的
santaMap
HashMap中没有出现
giftList3
,但是print语句仍然打印正确的计数,这意味着它必须知道
giftList3

getCountForAllPresents()
的代码是:

/**
 * For each present, calculate the total number of children who have asked for that present.
 * 
 * @return - a Map where Present objects are the keys and Integers (number of children requesting
 * a particular present) are the values. Returns null if santaMap is empty.
 */
public HashMap<Present, Integer> getCountsForAllPresents()
{
    if(!santaMap.isEmpty()) {
        //This HashMap contains a mapping from each unique real world present, represented by it's toComparisonString(), to a Present object representing it
        HashMap<String, Present> uniquePresents = new HashMap<String, Present>();
        //This HashMap contains a mapping from each Present object in uniquePresents to the number of times it's toComparisonString() is equal to another in santaMap
        HashMap<Present, Integer> presentFrequency = new HashMap<Present, Integer>();

         for(GiftList wishlist: santaMap.values()) {
            for(Present present: wishlist.getAllPresents()) {
                //Have we already seen this present?
                if(uniquePresents.containsKey(present.toComparisonString())) {
                    //If so, update the count in presentFrequency
                    Integer tmp = presentFrequency.get(uniquePresents.get(present.toComparisonString()));
                    tmp++;
                    presentFrequency.put(uniquePresents.get(present.toComparisonString()), tmp);
                } else {
                    //If not, add it to the maps uniquePresents and presentFrequency (with a frequency of 1)
                    uniquePresents.put(present.toComparisonString(), present);
                    presentFrequency.put(present, 1);
                }
            }
        }
        //Return a map with unique presents as keys and their frequencies as values
        return presentFrequency;
    }
    else {
        //If there are no mappings in Santa's map, return null
        return null;
    }
}
/**
*对于每一份礼物,计算要求得到该礼物的儿童总数。
* 
*@return-当前对象为键和整数(子对象数)的映射
*特定的礼物)是价值观。如果santaMap为空,则返回null。
*/
公共HashMap getCountsForAllPresents()
{
如果(!santaMap.isEmpty()){
//此HashMap包含从每个唯一的真实世界present(由它的toComparisonString()表示)到表示它的present对象的映射
HashMap uniquePresents=新的HashMap();
//此HashMap包含从uniquePresents中的每个Present对象到它的toComparisonString()与santaMap中的另一个相等的次数的映射
HashMap presentFrequency=新HashMap();
for(礼物列表愿望列表:santaMap.values()){
for(当前:wishlist.getAllPresents()){
//我们已经看过这个礼物了吗?
if(uniquePresents.containsKey(present.toComparisonString()){
//如果是,请更新presentFrequency中的计数
整数tmp=presentFrequency.get(uniquePresents.get(present.toComparisonString());
tmp++;
presentFrequency.put(uniquePresents.get(present.toComparisonString()),tmp);
}否则{
//如果没有,请将其添加到地图uniquePresents和presentFrequency(频率为1)
uniquePresents.put(present.toComparisonString(),present);
presentfFrequency.put(present,1);
}
}
}
//返回一个地图,其唯一呈现为关键帧,频率为值
返回频率;
}
否则{
//如果圣诞老人的映射中没有映射,则返回null
返回null;
}
}
我应该解释一下
santaMap
是一个
HashMap
,其中
Child
对象作为键,而
GiftList
对象作为值。它基本上把孩子映射到他们的圣诞愿望列表上。
santaMap
只能包含同一子级的一个愿望列表


我不知道为什么我会得到NPE,这与我如何编写
getCountForAllPresents()
方法有关吗?我是如何实现测试方法/类的?

您的
当前的
类不会覆盖
hashCode()
equals()
。这意味着
banana1
banana
是将它们用作键的任何
HashMap
中的两个不同键

让我们看看这里发生了什么。您有
banana
banana1
对象-第一个对象中的两个,第二个对象中的一个

getCountsForAllPresents()
中,您有两个哈希映射。第一个是对象的比较字符串,第二个是对象本身

添加遇到的第一个香蕉。如果它是
banana
对象,则会有如下内容:

uniquePresents banana-fruit-10 ➞ [banana instance] presentFrequency [banana instance] ➞ Integer(1) 现在进入
banana1
对象。它是一个不同的对象,但它具有相同的比较字符串!会发生什么

此条件为真:
uniquePresents.containsKey(present.toComparisonString())
。这意味着它进入
if
的真实部分

Integer tmp = presentFrequency.get(uniquePresents.get(present.toComparisonString()));
这意味着它将获取当前由
banana-fruit-10
指向的对象,该对象是
banana1
对象,而不是
banana1
对象,获取其相关频率,并将其递增。它还通过同一对象进行存储。您现在拥有的是:

uniquePresents banana-fruit-10 ➞ [banana instance] presentFrequency [banana instance] ➞ Integer(3) 为什么??因为
banana1
是第一个带有钥匙
banana-fruit-10
的礼物,从现在起它将使用这个钥匙


发生这种情况时,当您尝试从返回的对象获取
banana
时,该键在频率列表中不存在。它返回
null
-这是你的
null点异常

你能为
当前的
添加代码吗?你得到null的第215行在哪里?我认为
GiftSelector
的完整代码可能是一个很好的补充,因为我删除了一些代码,行号不会对应。如果查看testGetCountForAllPresents()方法,请转到最后一段代码,您应该会看到行
assertEquals(true,san
uniquePresents
banana-fruit-10 ➞ [banana instance]

presentFrequency
[banana instance] ➞ Integer(3)
uniquePresents
banana-fruit-10 ➞ [banana1 instance]

presentFrequency
[banana1 instance] ➞ Integer(3)