Java Deathmatch的结果–开发人员的益智迷你游戏
几个月前,我们发布了一个名为Java Deathmatch的微型站点 ,作为我们的新辅助项目,自那时以来,已有20,000多名开发人员进行了尝试。 该站点包含20个选择题Java的问题,今天我们收集了所有已玩游戏的统计信息后,很高兴与您分享一些结果和解决方案。
总体而言,我们收集了61,872个答案,对于20个问题中的每个问题,我们总共提供了3,094个答案。 每个Java Deathmatch会话随机选择5个问题,并给您90秒的时间来解决每个问题。 每个问题都有4个可能的答案。 我们一直被批评说,问题太难了,但是,没有理由将其称为死亡竞赛! 使用这些统计信息,我们能够确定哪些是最难的问题,哪些是最简单的问题。 在这篇文章中,我们想分享这个实验中的5个最棘手的问题,并将它们一起解决。
1. Java死亡竞赛的最棘手问题
让我们从最难破解的螺母开始,这是我们从布加勒斯特收到的Alexandru-Constantin Bledea提出的问题。 这是一个真正的脑筋急转弯。 只有20%的参与者能够解决此问题。 这意味着,如果您会随机选择一个答案,则可能有更好的机会找到正确的答案。 Java泛型对此具有这种品质。
好吧,那我们这里有什么? 我们有涉及类型擦除的泛型,还有一些例外。 这里要记住几件事:
- RuntimeException和SQLException都从Exception继承,而RuntimeException是未检查的,而SQLException是已检查的异常。
- Java泛型未进行泛化,这意味着在编译时,泛型类型信息会“丢失”,并视为将代码替换为类型的绑定,如果不存在则替换为Object 。 这就是您所说的类型擦除。
我们天真地希望第7行会导致编译错误,因为您无法将SQLException强制转换为RuntimeException,但事实并非如此。 发生的是用Exception替换了T,所以我们有:
throw (Exception) t; // t is also an Exception
由于pleaseThrow期望一个Exception ,并且T被Exception替换,因此强制类型被消除,就好像它没有被编写一样。 我们可以在字节码中看到:
private pleaseThrow(Ljava/lang/Exception;)V throws java/lang/Exception
L0
LINENUMBER 8 L0
ALOAD 1
ATHROW
L1
LOCALVARIABLE this LTemp; L0 L1 0
// signature LTemp<TT;>;
// declaration: Temp<T>
LOCALVARIABLE t Ljava/lang/Exception; L0 L1 1
MAXSTACK = 1
MAXLOCALS = 2
只是为了好玩,我们试图查看不涉及泛型的字节码的样子,并且强制转换出现在ATHROW语句之前:
CHECKCAST java/lang/RuntimeException
现在我们已经确定不涉及任何强制转换,我们可以从以下两个答案中解脱出来:
- “编译失败,因为我们无法将SQLException强制转换为RuntimeException”
- “由于SQLException不是RuntimeException的实例而抛出ClassCastException”
因此,我们毕竟抛出了SQLException,并且您希望它被catch块捕获并获得其堆栈跟踪。 好吧,不是真的。 该游戏被操纵。 事实证明,编译器和我们一样感到困惑,代码使它认为catch块不可访问。 对于毫无戒心的旁观者, 没有SQLException 。 正确的答案是编译失败,因为编译器不希望从try块引发SQLException-实际上,确实会引发该异常!
再次感谢Alexandru与我们分享这个问题!
2. toString()或不是toString(),这就是问题
仅获得正确答案的24%,接下来的问题是艰难的挑战。
这实际上要简单得多,仅通过查看第12行,我们可以看到此代码打印出m1和m2,而不是m1.name和m2.name。 这里最棘手的部分是要记住,当打印出一个类时,Java使用其toString方法。 人为地添加了“名称”字段。 如果您错过了它并正确地遵循了其余代码,则可能会被诱骗选择m1和新名称。
此行将两个名称都设置为“ m1”:
m1.name = m2.name = "m1";
然后callMe将m2的名称设置为新名称,我们完成了。
但是,此代码段实际上将打印出如下内容,包括类名和哈希码:
MyClass@3d0bc85 & MyClass@7d08c1b7
正确的答案将是“以上皆非”。
3. Google Guava Sets
这个问题实际上并不需要特定的番石榴集知识,但大多数受访者感到困惑。 只有25%的人正确回答了该问题,这与随机选择答案相同。
那么我们在这里看到什么? 我们有一种方法可以返回一个包含一个人的最好朋友的“陈词滥调”的集合。 我们看到有一个循环,用于检查一个人是否有最好的朋友,并将其添加到结果集中。 如果一个人确实有一个最好的朋友,它将为他们重复该过程,因此我们最终会拥有一组最好的朋友,直到我们找到一个没有最好朋友的人,或者它的最好朋友已经存在。 最后一部分可能有些棘手-我们无法添加已经在集合中的人,因此没有无限循环的可能。
这里的问题是我们冒着内存不足异常的风险。 集合上没有限制,因此我们可以不断添加和添加人员,直到内存用完为止。
顺便说一句,如果您喜欢Google Guava,请查看我们写的这篇文章,其中介绍了一些鲜为人知但有用的功能 。
4.双括号初始化,大声笑吗?
这是最短的问题之一,但足以使大多数开发人员感到困惑。 只有26%的人做对了。
尽管需要一些常量, 但没有很多开发人员知道需要初始化常量集合时使用的这种语法。 实际上,缺乏人气可能是一件好事。 那么当WAT? 效果消失了,您可以看到我们在列表中添加了一个元素,然后尝试将其打印出来。 通常,您希望它能打印出[John],但双括号初始化还有其他计划。 我们在这里看到的是一个用于初始化List的匿名类。 当它尝试打印出NAMES时,实际上显示为null。 由于尚未使用初始化程序,因此列表为空。
您可以在此处阅读有关双括号初始化的更多信息。
5.运行时地图的奇怪情况
这是来自以色列的Barak Yaish提出的另一个由社区提出的问题。 只有27%的参与者能够解决此问题。
好了,compute在地图中查找一个值。 如果为null,则将其添加并返回其值。 由于列表为空,因此不存在“ foo”,v为空,我们将“ foo”映射到新的ArrayList <Object>() 。 ArrayList为空,因此将输出[] 。
对于第二行,“ foo”确实存在于地图中,因此我们评估右侧的表达式。 ArrayList已成功转换为List,并向其中添加了“ ber”。 add返回true,这就是它输出的内容。
正确答案是[] true 。 再次感谢Barak与我们分享这个问题!
奖金:最简单的问题是……
这次,我们有一个来自OpenHFT的Peter Lawrey的问题,他也发表了Vanilla Java博客。 彼得在StackOverflow的前50名名单中,这次他移到另一边,问一个问题:你们中有76%的人正确。
答案C比A简单,B&D不编译。
结论
有时我们真的很喜欢玩这种谜题以增强我们的Java知识,但是如果您发现自己在自己的代码库中花了太多时间在这些谜题上,那可能就不那么理想了。 特别是如果有人在半夜打电话来解决严重的生产错误。 对于这种情况,我们为Java构建了Takipi 。 Takipi是一个Java代理,它知道如何在生产中的服务器上跟踪未捕获的异常,捕获的异常以及记录错误。 它使您可以查看导致错误的变量值(遍及整个堆栈),并将其覆盖在代码中。
翻译自: https://www.javacodegeeks.com/2015/08/4-out-of-5-java-developers-failed-to-solve-this-question.html