为什么我的 NPE 只在程序 运行 时偶尔出现?
Why Am I Getting An NPE That Only Appears Occasionally When The Program is Run?
我正在使用 BlueJ 中的 JUnit 为我的 GiftSelector
class 编写测试 class。当我 运行 testGetCountForAllPresents()
方法时,我得到一个 NullPointerException
在线:
assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 3);
关于这个 NPE 的 st运行ge 事情是,当我 运行 测试一次时它很少出现,但在我 运行 测试时经常出现。它有时直到我连续 运行 测试 7-8 次后才会出现。
我收到的错误消息是:
没有异常消息。
NPE at line 215 in GiftSelectortest.testGetCountForAllPresents
我的测试代码 class 是:
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()
方法之前添加了打印语句。有趣的是,在我得到 NPE 的那一行之前,print 语句打印出 getCountForAllPresents()
.
返回的 HashMap
的正确值
我注意到的唯一其他 st运行ge 事情是,当我使用 BlueJ 的内置调试器执行 testGetCountForAllPresents()
方法时,我注意到 giftList3
没有't appear in the santaMap
HashMap
in santasSelector
, 但是 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;
}
}
我应该解释一下 santaMap
是一个 HashMap
,其中 Child
object 作为键,GiftList
object 作为价值。它基本上将 child 映射到他们的圣诞愿望清单。一个 santaMap
只能包含同一个 child.
的一个心愿单
我不知道为什么我会得到 NPE,这与我编写 getCountForAllPresents()
方法的方式有关吗?我是如何实施测试的 method/class?
您的 Present
class 不会覆盖 hashCode()
和 equals()
。这意味着 banana1
和 banana
是任何 HashMap
中的两个不同的键,将使用它们作为键。
让我们看看这里发生了什么。您有 banana
和 banana1
个对象 - 第一个对象中的两个,第二个对象中的一个。
在 getCountsForAllPresents()
中,您有两个哈希映射。第一个通过对象的比较字符串,第二个 - 通过对象本身。
添加遇到的第一根香蕉。如果它是 banana
对象,你将得到类似这样的东西:
uniquePresents
banana-fruit-10 ➞ [banana instance]
presentFrequency
[banana instance] ➞ Integer(1)
你继续迭代。您遇到下一个 banana
对象。这是同一个对象。你会得到:
uniquePresents
banana-fruit-10 ➞ [banana instance]
presentFrequency
[banana instance] ➞ Integer(2)
现在您到达 banana1
对象。这是一个不同的对象,但它具有相同的比较字符串!会发生什么?
此条件为真:uniquePresents.containsKey(present.toComparisonString())
。这意味着它进入 if
的真实部分。
Integer tmp = presentFrequency.get(uniquePresents.get(present.toComparisonString()));
这意味着它将获取 banana-fruit-10
当前指向的对象,即 banana
对象 - 而不是 banana1
对象, 获取其关联的频率,并增加它。它也由同一个对象存储。你现在拥有的是:
uniquePresents
banana-fruit-10 ➞ [banana instance]
presentFrequency
[banana instance] ➞ Integer(3)
请注意 presentFrequency
根本没有 banana1
键。现在你 return 这个对象。
当您尝试通过 banana
检索时,它工作正常 - 断言工作。
但是请记住,santaMap
本身就是一个HashMap
。这意味着没有保证订单。迭代器可能会给你 giftList1
、giftList2
、giftList3
,但它也可能会给你 giftList3
、giftList1
、giftList2
- 或任何其他订单。
那么当它首先给你 giftList3
时会发生什么?你最终会得到:
uniquePresents
banana-fruit-10 ➞ [banana1 instance]
presentFrequency
[banana1 instance] ➞ Integer(3)
为什么?因为banana1
是第一个带钥匙banana-fruit-10
的礼物,以后也是用的
发生这种情况时,当您尝试从 returned 对象获取 banana
时,频率列表中不存在该键。它 returns null
- 还有你的 NullPointerException
.
我正在使用 BlueJ 中的 JUnit 为我的 GiftSelector
class 编写测试 class。当我 运行 testGetCountForAllPresents()
方法时,我得到一个 NullPointerException
在线:
assertEquals(true, santasSelector.getCountsForAllPresents().get(banana) == 3);
关于这个 NPE 的 st运行ge 事情是,当我 运行 测试一次时它很少出现,但在我 运行 测试时经常出现。它有时直到我连续 运行 测试 7-8 次后才会出现。
我收到的错误消息是: 没有异常消息。
NPE at line 215 in GiftSelectortest.testGetCountForAllPresents
我的测试代码 class 是:
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()
方法之前添加了打印语句。有趣的是,在我得到 NPE 的那一行之前,print 语句打印出 getCountForAllPresents()
.
HashMap
的正确值
我注意到的唯一其他 st运行ge 事情是,当我使用 BlueJ 的内置调试器执行 testGetCountForAllPresents()
方法时,我注意到 giftList3
没有't appear in the santaMap
HashMap
in santasSelector
, 但是 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;
}
}
我应该解释一下 santaMap
是一个 HashMap
,其中 Child
object 作为键,GiftList
object 作为价值。它基本上将 child 映射到他们的圣诞愿望清单。一个 santaMap
只能包含同一个 child.
我不知道为什么我会得到 NPE,这与我编写 getCountForAllPresents()
方法的方式有关吗?我是如何实施测试的 method/class?
您的 Present
class 不会覆盖 hashCode()
和 equals()
。这意味着 banana1
和 banana
是任何 HashMap
中的两个不同的键,将使用它们作为键。
让我们看看这里发生了什么。您有 banana
和 banana1
个对象 - 第一个对象中的两个,第二个对象中的一个。
在 getCountsForAllPresents()
中,您有两个哈希映射。第一个通过对象的比较字符串,第二个 - 通过对象本身。
添加遇到的第一根香蕉。如果它是 banana
对象,你将得到类似这样的东西:
uniquePresents banana-fruit-10 ➞ [banana instance] presentFrequency [banana instance] ➞ Integer(1)
你继续迭代。您遇到下一个 banana
对象。这是同一个对象。你会得到:
uniquePresents banana-fruit-10 ➞ [banana instance] presentFrequency [banana instance] ➞ Integer(2)
现在您到达 banana1
对象。这是一个不同的对象,但它具有相同的比较字符串!会发生什么?
此条件为真:uniquePresents.containsKey(present.toComparisonString())
。这意味着它进入 if
的真实部分。
Integer tmp = presentFrequency.get(uniquePresents.get(present.toComparisonString()));
这意味着它将获取 banana-fruit-10
当前指向的对象,即 banana
对象 - 而不是 banana1
对象, 获取其关联的频率,并增加它。它也由同一个对象存储。你现在拥有的是:
uniquePresents banana-fruit-10 ➞ [banana instance] presentFrequency [banana instance] ➞ Integer(3)
请注意 presentFrequency
根本没有 banana1
键。现在你 return 这个对象。
当您尝试通过 banana
检索时,它工作正常 - 断言工作。
但是请记住,santaMap
本身就是一个HashMap
。这意味着没有保证订单。迭代器可能会给你 giftList1
、giftList2
、giftList3
,但它也可能会给你 giftList3
、giftList1
、giftList2
- 或任何其他订单。
那么当它首先给你 giftList3
时会发生什么?你最终会得到:
uniquePresents banana-fruit-10 ➞ [banana1 instance] presentFrequency [banana1 instance] ➞ Integer(3)
为什么?因为banana1
是第一个带钥匙banana-fruit-10
的礼物,以后也是用的
发生这种情况时,当您尝试从 returned 对象获取 banana
时,频率列表中不存在该键。它 returns null
- 还有你的 NullPointerException
.