五分之四的 Java 开发人员未能解决此问题

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 / 赠书活动

目前, 星球 内第2个项目《仿小红书(微服务架构)》正在更新中。第1个项目:全栈前后端分离博客项目已经完结,演示地址:http://116.62.199.48/。采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 276 小节,累计 43w+ 字,讲解图:1917 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 1500+ 小伙伴加入,欢迎点击围观

几个月前,我们发布了一个新的附带项目,其中包含一个名为 java deathmatch(开发人员的益智迷你游戏)的迷你网站,从那时起,已有超过 20,000 名开发人员尝试了它。该网站有 20 个多项选择 java 问题,今天在我们收集了所有玩过的游戏的统计数据后,我们很高兴与您分享一些结果和解决方案。

总体而言,我们收集了 61,872 个答案,这为 20 个问题中的每个问题提供了大约 3,094 个答案。每个 Java 死亡竞赛会话随机选择 5 个问题,并给您 90 秒的时间来解决每个问题。每个问题都有 4 个可能的答案。我们一直被批评问题太难了,但是,好吧,这不能无缘无故地称为死亡竞赛!使用这些统计数据,我们能够确定哪些是最难的问题,哪些是最简单的。在这篇文章中,我们想分享这个实验中最棘手的 5 个问题,并一起解决它们。

新帖子:5 个中有 4 个 #java 开发人员未能解决此问题 http://t.co/b9m6b9nfhm pic.twitter.com/2qmnhcqlro

- takipi (@takipid) 2015 年 7 月 27 日

平均而言,41% 的尝试答案是正确的,这一点也不差。 此处提供按 索引列出的结果和问题的实时统计数据。这篇文章的统计数据是 7 月 26 日的快照。查看 java deathmatch 以获得完整的测验。

1. java死斗最难的问题

让我们从最难解决的问题开始,这是我们从布加勒斯特的 alexandru-constantin bledea 那里收到的一个问题。这是一个真正的脑筋急转弯。只有 20% 的参与者能够解决这个问题。这意味着如果您随机选择一个答案——您可能有更好的机会找到正确的答案。 Java 泛型具有这种特性。

好吧,那么我们这里有什么?我们有涉及类型擦除的泛型,还有一些例外。这里有几件事要记住:

1、 runtimeexception sqlexception 都是继承自exception,而 runtimeexception 是unchecked, sqlexception 是checked异常。
2. java 泛型没有具体化,这意味着在编译时,泛型类型信息“丢失”并且被视为代码被替换为类型的绑定或 对象 (如果不存在)。这就是您所说的类型擦除。

我们天真地期望第 7 行会导致编译错误,因为您不能将 sqlexception 转换为 runtimeexception,但事实并非如此。发生的事情是 t 被异常替换,所以我们有:


 throw (exception) t;  // t is also an exception

因为 pleasethrow 需要一个 exception ,并且 t 被替换为 exception ,所以演员表被删除,就好像它没有被写入一样。我们可以在字节码中看到:


 throw (exception) t;  // t is also an exception

只是为了好玩,我们试图看看在不涉及泛型的情况下字节码会是什么样子,并且强制转换出现在 throw 语句之前:


 throw (exception) t;  // t is also an exception

既然我们确信不涉及转换,我们就可以得出这两个答案:
“编译失败,因为我们无法将 sqlexception 转换为 runtimeexception”
“抛出 classcastexception 因为 sqlexception 不是 instanceof runtimeexception”

所以我们毕竟抛出一个 sqlexception,你会期望它被 catch 块捕获并获得它的堆栈跟踪。好吧,不是真的。这个游戏被操纵了。结果编译器和我们一样感到困惑,代码让它认为 catch 块是不可访问的。对于毫无戒心的旁观者, 没有 sqlexception 。正确的答案是编译失败,因为编译器不希望从 try 块中抛出 sqlexception——而实际上它确实被抛出!

再次感谢 alexandru 与我们分享这个问题!另一种很酷的方法可以准确地看到这里出了什么问题以及 sqlexception 实际是如何抛出的,这是替换 catch 块并让它期望运行时异常。这样你就会看到 sqlexception 的实际堆栈跟踪。

2. tostring(),还是不tostring(),这是个问题

只有 24% 的正确答案,以下问题在艰难的规模上排名第二。

这一个实际上要简单得多,仅从第 12 行我们就可以看到这段代码打印出 m1 和 m2,而不是 m1.name 和 m2.name。这里棘手的部分是记住当打印出一个类时,java 使用它的 tostring 方法。 “名称”字段是人为添加的。如果您错过了这一点并正确地遵循了其余代码,您可能会被骗去选择 m1 和新名称。

此行将两个名称都设置为“m1”:


 throw (exception) t;  // t is also an exception

然后 callme 将 m2 的名称设置为新名称,我们就完成了。

但这段代码实际上会打印出类似这样的内容,包括类名和哈希码:


 throw (exception) t;  // t is also an exception

正确答案是“以上都不是”。

3.谷歌番石榴套装

这个问题并不真正需要番石榴集的具体知识,但让大多数受访者感到困惑。只有 25% 的人回答正确,与随机选择答案相同。

那么我们在这里看到了什么?我们有一个方法可以返回一个集合,其中包含一个人最好的朋友的“小圈子”。我们看到有一个循环检查一个人是否有最好的朋友,并将他们添加到结果集中。如果一个人确实有一个最好的朋友,它会为他们重复这个过程,所以我们最终会得到一组最好的朋友,直到我们找到一个没有最好的朋友或者它最好的朋友已经在这个集合中的人。最后一部分可能有点棘手——我们不能添加一个已经在集合中的人,所以没有无限循环的可能性。

这里的问题是我们冒着内存不足异常的风险。集合没有限制,所以我们可以不断添加和添加人员,直到我们用完内存。

顺便说一下,如果您喜欢 google guava,请查看我们写的这篇关于 它的一些鲜为人知但有用的功能的 帖子。

4. 双括号初始化,哈哈?!

这是最短的问题之一,但足以让大多数开发人员感到困惑。只有 26% 的人答对了。

没有多少开发人员知道这种在需要初始化常量集合时派上用场的语法, 尽管其中包含一些副作用 。实际上,这种不受欢迎可能是一件好事。所以什么时候扫管笏?!效果逐渐消失,你可以看到我们向列表中添加了一个元素,然后尝试将其打印出来。通常你会期望它打印出 [john] 但双括号初始化有其他计划。我们在这里看到的是一个用于初始化列表的匿名类。当它试图打印出名字时,它实际上是空的。因为初始值设定项尚未使用且列表为空。

您可以 在此处 阅读有关双括号初始化的更多信息。

5. 地图在运行时的奇怪情况

这是另一个社区贡献的问题,来自以色列的 barak yaish。只有 27% 的参与者能够解决这个问题。

好的,计算在地图中查找一个值。如果它为 null,则添加它并返回它的值。由于列表为空,“foo”不存在,v 为空,我们将“foo”映射到一个 新的 arraylist<object>() arraylist 是空的,所以它打印出 []

对于第二行,地图中确实存在“foo”,因此我们评估右侧的表达式。 arraylist 成功转换为列表,并向其添加“ber”。 add 返回 true,这就是它打印出来的内容。

正确答案是 [] true 。再次感谢巴拉克与我们分享这个问题!

奖励:最简单的问题是……

这次我们有来自 openhft 的 peter lawrey 的问题,他也在 vanilla java 上写博客。 peter 在 stackoverflow 的前 50 名名单上,这次他转到另一边,问了一个 76% 的人都答对了的问题。


答案c比a简单,b&d不编译。

结论

有时我们真的很喜欢玩这种谜题来提高我们的 Java 知识,但是如果您发现自己在自己的代码库中花太多时间在这些谜题上,那么它可能不太理想。特别是如果有人在半夜打电话来修复严重的生产错误。对于这种情况,我们 为 java 构建了 takipi 。 takipi 是一个 java 代理,它知道如何在生产服务器上跟踪未捕获的异常、捕获的异常和日志错误。它可以让您查看整个堆栈中导致错误的变量值,并将它们覆盖在您的代码中。