统计数据来自Java“死亡竞赛”-开发人员新项目的迷你测验。竞猜发布后,已有超过20,000名开发者参与了竞猜。该网站基于20个有关Java的多项选择题。我们从一群开发人员那里得到了测验统计数据,今天我们很乐意与您分享其中的一些统计数据和答案。我们从20个项目中得到了61872个答案,每个项目大约有3094个答案。每个Java“死亡”测验都会从20道题中随机抽取5道题,然后每道题有90秒的时间作答。每个问题都有四个可能的选项。人们经常向我们抱怨这些题目太难了。所以,我们的测验被称为Java的“死亡”竞赛并不是没有道理的!从测试结果的统计中,我们可以知道哪些题最难,哪些题最简单。在这篇博客中,我想与您分享从我们的测验中选出的5个最难的问题,并一起解决它们。平均而言,开发者给出的答案中大约有41%是正确的,这一点也不差。每个问题的索引和答案统计可以从这里获得。用于此博客的统计数据是在7月26日获得的。请在此处尝试我们的Java“死亡”竞赛测验。一、Java“死亡竞赛”中最难的问题先从最难的骨头说起。这个问题由来自罗马尼亚布加勒斯特的Alexandru-ConstantinBledea提出。这道题确实是一道脑筋急转弯,只有大约20%的参与者答对了,这意味着随机选择可以提高你答对的几率。这个问题是关于Java泛型的。主题:这段代码有什么问题?A。编译错误,因为没有抛出SQLExceptionb。ClassCastException被抛出,因为SQLException不是RuntimeExceptionc的一个实例。没有错误,程序打印了抛出的SQLExceptiond的堆栈跟踪。编译错误,因为我们无法将SQLException类型转换为RuntimeException,从题中我们能得到什么信息呢?题目中的泛型涉及到类型擦除,以及一些异常。这里需要回顾一下一些知识:RuntimeException和SQLException都是继承自Exception,但是这段代码中RuntimeException是uncheckedexception,而SQLException是checkedexception。2.Java的泛型是不具体的。这意味着在编译时,泛型的类型信息“丢失”了,泛型参数被替换为它的限定类型,或者当限定类型不存在时,泛型参数被Object替换。这就是人们所说的类型“擦除”。我们天真地希望第7行会产生编译错误,因为我们无法将SQLException转换为RuntimeException,但那并没有发生。发生的是T被Exception替换,所以我们有:throw(Exception)t;//tisalsoanExceptionpleaseThrow方法期望一个Exception,而T被Exception代替,所以cast被抹掉了,就像不写这段代码一样。我们可以从下面的字节码中得到证据:L0L10//签名LTemp;//声明:TempLOCALVARIABLEtLjava/lang/Exception;L0L11MAXSTACK=1MAXLOCALS=2我们再来看一下,如果代码不涉及泛型,编译生成的字节码是什么样子的,我们看到,ATHROW之前会有如下代码:CHECKCASTjava/lang/RuntimeException现在,我们可以确定代码中没有涉及类型转换,所以我们可以排除以下两种可能:“编译错误,因为我们不能将SQLException类型转换为RuntimeException”“抛出ClassCastException因为SQLException不是RuntimeException的实例"所以在我们抛出SQLException之后,您希望它被catch块捕获,然后打印它的堆栈跟踪。然而,事与愿违。这段代码具有欺骗性,它和我们一样让编译器感到困惑。此代码使编译器认为catch块无法访问。对于不知情的旁观者,代码中没有SQLException。所以,正确的答案是:编译失败是因为编译器认为SQLException不能从try块中抛出——但实际上它可以!再次感谢Alexandru与我们分享这个问题!我们可以使用另一种很酷的方式来查看代码中的错误以及SQLException实际上是如何抛出的,这种方法是:修改catch代码块,修改它以接收RuntimeException。这样就可以看到SQLException的堆栈信息了。(其实SQLException不是被catch代码段捕获,而是被虚拟机捕获并打印出异常堆栈信息。)2.问题的症结在于是否使用toString()这道题的正确率只有24%,难度在20道题中位居亚军。题目大意:这个程序的打印结果是什么?a.m1&新名称b.上面都是错误的c.m1&m1d.newname&newname这道题其实简单很多,我们只需要看第十二行,它直接打印了m1和m2,而不是m1.name和m2.name。这段代码的棘手之处在于,当我们想要打印一个对象时,Java使用了toString方法。“名称”属性是我们自己添加的。如果你忘了这个而其他地方都是正确的,你可能会误选答案m1&newname。这行代码将两个对象的名称属性分配给“m1”。m1.name=m2.name="m1";然后callMe方法将m2对象的名称属性设置为“新名称”,代码结束。然而,这个代码片段实际上会打印以下信息,包括类名和它们的哈希码:MyClass@3d0bc85&MyClass@7d08c1b7所以正确答案是“以上都不是”3.GoogleGuava类库Sets的含义话题:这个话题有什么问题?A。无法编译B.没问题C.可能导致内存溢出D.可能导致***循环这个问题其实对Guavasets类库的专业知识要求不是特别高,但是却让大部分开发者感到困惑。只有25%的参与者给出了正确答案,与盲选的比例相同。那么我们可以从这段代码中看出什么呢?我们有一个方法返回一个包含一个人的朋友圈的集合。该方法中有一个循环,用于检查person对象的bestfriend属性是否为null。如果不为空,则将bestfriend添加到结果集合中。如果一个person对象确实有bestfriend,那么对person的bestfriend重复上面的过程,所以我们可以不断的往bestfriend集合中添加person对象,直到有一个人没有bestfriend,或者他的bestfriend已经在我们的集合中在结果集合中。***这部分有点微妙,我们不能在这个Set集合中添加重复的元素,也就是person对象,所以这个方法不会造成***循环。真正的问题在于这段代码极有可能导致内存不足异常。这个循环实际上是无限的,所以我们可以不断地向集合中添加人物对象,直到内存耗尽。顺便说一下,如果你想了解更多关于GoogleGuava的知识,你可以看看我们写的这篇博客:关于它的鲜为人知但有用的特性4。使用两个花括号初始化主题:这段代码哪里出错了?A。没有错误B.可能得到空值c。代码不编译d。打印不正确的结果这个问题是编码最少的问题之一,但足以让大多数开发人员感到困惑。只有26%的受访者正确回答了这个问题。很少有开发人员知道这种用于初始化常量集合的便捷语法,尽管这种语法有一些副作用。但这种语法鲜为人知的事实并不是一件好事。感叹之后,你看,我们在列表中添加了一个元素,然后打印了列表。通常,您会希望看到[John]作为打印结果,但还有另一组初始化过程使用两个花括号进行初始化。这里,我们使用一个匿名类来初始化一个List。当要打印NAMES时,实际打印的是null。这是因为初始化程序还没有完成,此时列表是空的。关于使用两个花括号初始化容器,可以参考这个(就在这里)。5.关于runtimeMap容器??的怪异事件,这是另一个社区贡献的问题,贡献者是来自以色列的BarakYaish。只有27%的受访者可以回答这个问题。题目大意:这段代码的输出是什么?计算方法通过键在映射中查找值。如果值为空,则插入(键,值)并返回值。因为最初,列表是空的,值“foo”不存在,v是空的。然后,我们在map中插入一个“foo”,“foo”指向newArrayList