日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

八成Java开发者解答不了的问题

發(fā)布時(shí)間:2025/3/21 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 八成Java开发者解答不了的问题 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

統(tǒng)計(jì)數(shù)據(jù)來(lái)自Java“死亡”競(jìng)賽——一個(gè)針對(duì)開(kāi)發(fā)者的迷你測(cè)驗(yàn)

幾個(gè)月前,我們?cè)谝粋€(gè)小型網(wǎng)站上發(fā)布了一個(gè)稱為Java“死亡競(jìng)賽”的新項(xiàng)目。測(cè)驗(yàn)發(fā)布后,超過(guò)20000位開(kāi)發(fā)者參加了測(cè)驗(yàn)。網(wǎng)站以20道關(guān)于Java的多選題為主。我們得到了眾多開(kāi)發(fā)者的測(cè)驗(yàn)統(tǒng)計(jì)數(shù)據(jù),今天,我們非常樂(lè)意將其中的一些數(shù)據(jù)和答案與你們分享。

我們從20個(gè)題目中得到了61872個(gè)答案,大約每個(gè)題目有3094個(gè)答案。每個(gè)Java“死亡”測(cè)驗(yàn)都會(huì)隨機(jī)地從20個(gè)題目中抽取5個(gè)題目,然后每個(gè)題目90秒的時(shí)間作答。每個(gè)問(wèn)題有四個(gè)可能的選項(xiàng)。經(jīng)常有人向我們抱怨說(shuō)這些題目太難了。所以,我們的測(cè)驗(yàn)被稱為Java“死亡”競(jìng)賽并不是沒(méi)有理由的哦!從測(cè)驗(yàn)結(jié)果的統(tǒng)計(jì)數(shù)據(jù)中,我們能知道哪些問(wèn)題是最難的,哪些是最簡(jiǎn)單的。在這篇博客中,我想與你們分享5個(gè)從我們的測(cè)驗(yàn)中挑選出的最難的問(wèn)題,然后一起解決它們。

平均來(lái)看,開(kāi)發(fā)者給出的答案中大約41%是正確的,這個(gè)結(jié)果可一點(diǎn)不差。每個(gè)問(wèn)題的索引和它的作答統(tǒng)計(jì)結(jié)果可以從這里得到。這篇博客所用的統(tǒng)計(jì)數(shù)據(jù)是在7月26日得到的。從這里可以嘗試我們的Java“死亡”競(jìng)賽測(cè)驗(yàn)。

1、Java“死亡競(jìng)賽”中最難的問(wèn)題

讓我們從最難啃的骨頭開(kāi)始吧。這個(gè)問(wèn)題由來(lái)自羅馬尼亞首都布加勒斯特的?Alexandru-Constantin Bledea提供。這個(gè)問(wèn)題確實(shí)是一個(gè)腦筋急轉(zhuǎn)彎,只有約20%的參與者答對(duì)這道題,這意味著瞎選都能提高你回答正確的概率。這道題是關(guān)于Java泛型的。

題目大意:

這段代碼錯(cuò)在哪兒?

a.編譯錯(cuò)誤,因?yàn)闆](méi)有SQLException被拋出

b.拋出ClassCastException,因?yàn)镾QLException并不是RuntimeException的一個(gè)實(shí)例

c.沒(méi)有錯(cuò)誤,程序打印出拋出的SQLException堆棧跟蹤信息

d.編譯錯(cuò)誤,因?yàn)槲覀儾荒軐QLException類型轉(zhuǎn)換成RuntimeException

好,我們能從題目中得到什么信息?題目中的泛型涉及到了類型擦除,以及一些異常。這里需要回憶一些知識(shí):

RuntimeException和SQLException都繼承自Exception,但是在這個(gè)代碼中RuntimeException是未檢查的異常,而SQLException是受檢異常

2.Java的泛型并不是具體化的。這意味著在編譯時(shí),泛型的類型信息會(huì)“丟失”,并且泛型參數(shù)像是被它的限定類型替換了一樣,或者當(dāng)限定類型不存在時(shí),泛型參數(shù)被替換成了Object。這就是大家所說(shuō)的類型“擦除”。

我們天真地希望第七行能產(chǎn)生一個(gè)編譯錯(cuò)誤,因?yàn)槲覀儾荒軐QLException轉(zhuǎn)換成RuntimeException,但是這并不會(huì)發(fā)生。發(fā)生的是將T替換成了Exception,所以我們有:

1 throw (Exception) t; // t is also an Exception

pleaseThrow方法期望一個(gè)Exception,并且T被替換成了Exception,因此類型轉(zhuǎn)換被擦除了,就像沒(méi)寫(xiě)這個(gè)代碼一樣。這一點(diǎn)我們可從下面的字節(jié)碼中得到佐證:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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

我們?cè)倏匆幌?#xff0c;如果代碼中沒(méi)有涉及泛型,那么編譯產(chǎn)生的字節(jié)碼是什么樣的,我們看到,在ATHROW前會(huì)有如下的代碼:

1 CHECKCAST java/lang/RuntimeException

現(xiàn)在,我們可以確信,代碼中并沒(méi)有涉及到類型轉(zhuǎn)換,因此我們可以排除下面這兩個(gè)選項(xiàng):

“編譯錯(cuò)誤,因?yàn)槲覀儾荒軐QLException類型轉(zhuǎn)換為RuntimeException”

“拋出ClassCastException,因?yàn)镾QLException不是RuntimeException的一個(gè)實(shí)例”

因此畢竟我們拋出了SQLException,然后你希望它能被catch代碼塊捕獲,然后打印它的堆棧跟蹤信息。然而,事與愿違。

這個(gè)代碼具有欺騙性,它使得編譯器和我們一樣變得困惑。這段代碼讓編譯器認(rèn)為catch代碼塊是不能到達(dá)的。對(duì)于不知情的旁觀者來(lái)說(shuō),代碼中并沒(méi)有SQLException。所以,正確答案是:編譯失敗,因?yàn)榫幾g器認(rèn)為SQLException不會(huì)從try代碼塊中拋出-但是實(shí)際上它確實(shí)能拋出!

再次感謝Alexandru與我們分享這個(gè)問(wèn)題!我們可以用另一個(gè)很酷的方式來(lái)查看代碼中的錯(cuò)誤以及SQLException實(shí)際上是怎樣拋出的,這個(gè)方法是:修改catch代碼塊,把它修改為接收一個(gè)RuntimeException。這樣你就可以看到SQLException的堆棧信息了。實(shí)際上SQLException也并沒(méi)有被catch代碼段捕獲,而是被虛擬機(jī)捕獲并打印出異常棧的信息。

2、問(wèn)題的關(guān)鍵在于,是否使用了toString()

這道題只有24%的正確率,它的困難程度是這20道題中的亞軍。

題目大意:這個(gè)程序的打印結(jié)果是?

a.m1 & new name

b.以上都是錯(cuò)誤的

c.m1&m1

d.new name & new name

這道題實(shí)際上簡(jiǎn)單得多,我們只要看到第十二行,它直接打印了m1和m2,而不是m1.name和m2.name。這段代碼狡猾的地方在于,當(dāng)我們要打印一個(gè)對(duì)象時(shí),Java使用的是toString方法。“name”屬性是我們自己加入的,如果你忘記這點(diǎn),其他地方都判斷正確的話,你可能會(huì)錯(cuò)誤地選擇m1&new name這個(gè)答案。

這行代碼將兩個(gè)對(duì)象的name屬性都賦值為”m1”。

1 m1.name = m2.name = “m1";

然后callMe方法將m2對(duì)象的name屬性設(shè)置成”new name”,然后代碼就結(jié)束了。

但是,這個(gè)代碼片段實(shí)際上將會(huì)打印出如下信息,包括類名稱以及它們的哈希碼:

1 MyClass@3d0bc85 & MyClass@7d08c1b7

所以正確的答案是“None of the above”

3、Google Guava類庫(kù)中的Sets

題目大意:

這道題目不妥的地方在哪?

a.不能編譯

b.沒(méi)有問(wèn)題

c.可能造成內(nèi)存溢出

d.可能造成無(wú)限循環(huán)

這個(gè)問(wèn)題實(shí)際上并不特別需要關(guān)于Guava sets類庫(kù)的專業(yè)知識(shí),但卻使絕大多數(shù)的開(kāi)發(fā)者產(chǎn)生困惑。只有25%的參與者給出了正確的答案,和瞎選的正確率是一樣的。

那么我們能從這段代碼中看出什么呢?我們有一個(gè)方法,它返回一個(gè)集合,這個(gè)集合包含了某個(gè)人的好友圈。方法中有一個(gè)循環(huán),它檢查一個(gè)person對(duì)象的bestfriend屬性是否為null。如果不為null,則將bestfriend添加到results集合里。如果一個(gè)person對(duì)象確實(shí)有一個(gè)bestfriend,那么對(duì)這個(gè)person的bestfriend,重復(fù)執(zhí)行上述過(guò)程,所以我們就可以一直向bestfriend集合添加person對(duì)象,直到有一個(gè)person,它沒(méi)有bestfriend,或者它的bestfriend已經(jīng)在我們的result集合里了。最后這部分有一點(diǎn)微妙,我們不能向這個(gè)Set集合添加重復(fù)的元素,即person對(duì)象,所以這個(gè)方法并不會(huì)導(dǎo)致無(wú)限循環(huán)。

真正的問(wèn)題在于,這段代碼很有可能造成內(nèi)存用盡的異常(out of memory exception)。這個(gè)循環(huán)實(shí)際上是沒(méi)有邊界的,所以我們可以不停地往set中添加person對(duì)象,直到內(nèi)存用盡。

順便提一下,如果你想詳細(xì)了解Google Guava,可以看看我們寫(xiě)的這篇博客:?the lesser known yet useful features about it

4、利用兩個(gè)花括號(hào)進(jìn)行初始化

題目大意:這段代碼錯(cuò)誤的地方在哪?

a.沒(méi)有錯(cuò)誤

b.可能獲得null值

c.代碼不能編譯

d.打印出不正確的結(jié)果

這個(gè)問(wèn)題是代碼最少的問(wèn)題之一,但是足以迷惑絕大部分的開(kāi)發(fā)者。這道題只有26%的答題者回答正確。

很少有開(kāi)發(fā)者知道這個(gè)初始化常量集合的簡(jiǎn)便語(yǔ)法,雖然這個(gè)語(yǔ)法會(huì)帶來(lái)一些副作用。但事實(shí)上,這個(gè)語(yǔ)法鮮為人知未免不是一件好事。在感嘆之后,你看到,我們往list里添加了一個(gè)元素,然后打印這個(gè)list。正常情況下,你期望看到打印的結(jié)果是[John],但是利用兩個(gè)花括號(hào)進(jìn)行初始化是有另一套初始化過(guò)程的。這里,我們用了一個(gè)匿名類來(lái)初始化一個(gè)List,當(dāng)要打印NAMES時(shí),實(shí)際上打印出來(lái)的是null,這是因?yàn)槌跏蓟绦蛏形赐瓿?#xff0c;此時(shí)的list是空的。

關(guān)于使用兩個(gè)花括號(hào)進(jìn)行容器的初始化,可參考這里(right here)。

5、對(duì)于運(yùn)行時(shí)Map容器的離奇事件

這是另一個(gè)社區(qū)貢獻(xiàn)的問(wèn)題,貢獻(xiàn)者是來(lái)自以色列的Barak Yaish。只有27%的答題者能解答這個(gè)問(wèn)題。

題目大意:這段代碼的輸出是什么

a.不能編譯

b.類型轉(zhuǎn)換異常

c.[] true

d.[“bar”, “ber”]

好吧,來(lái)看看代碼。compute方法通過(guò)key在map中查找一個(gè)value。如果這個(gè)value是null,則插入(key, value),并返回value。因?yàn)殚_(kāi)始時(shí),這個(gè)list是空的,“foo”值并不存在,v是null。然后,我們向map中插入一個(gè)“foo”并且“foo”指向new ArrayList<Object>(),此時(shí)的ArrayList對(duì)象是空的,所以它打印出[]。

下一行,“foo”鍵值存在于map容器中,所以我們計(jì)算右邊的表達(dá)式。ArrayList對(duì)象成功轉(zhuǎn)換為L(zhǎng)ist類型,然后“ber”字符串被插入到List中。add方法返回true,因此true就是第二行打印的內(nèi)容。

所以正確的答案是”[]true”。再次感謝Barak于我們分享這道題。

鼓勵(lì)一下:來(lái)看看最簡(jiǎn)單的題吧

題目大意:哪一種方法是初始化Java字符串最簡(jiǎn)單的方式

a.A

b.沒(méi)有一個(gè)

c.C

d.B和C不能編譯

現(xiàn)在,我們來(lái)看一下Peter Lawrey提供的問(wèn)題。他工作于OpenHFT開(kāi)源項(xiàng)目,同時(shí)也在Vanilla Java上撰寫(xiě)博客。Peter在StackOverflow上排名top 50,這一次他反過(guò)來(lái)向大家提問(wèn),76%的開(kāi)發(fā)者能回答出這個(gè)問(wèn)題。

C答案比A簡(jiǎn)單,B和D是不能編譯的。

結(jié)論

我們有時(shí)喜歡做這樣的小測(cè)驗(yàn)來(lái)加深我們對(duì)Java知識(shí)的理解但是,你是否發(fā)現(xiàn)自己的代碼庫(kù)中也有這樣或那樣類似小測(cè)驗(yàn)的問(wèn)題使自己困惑,常常需要花許多時(shí)間來(lái)維護(hù),這樣的話可能并不好。特別是在半夜時(shí),你接到一個(gè)電話,讓你去解決一個(gè)嚴(yán)重的產(chǎn)品錯(cuò)誤。對(duì)于這種情況,我們開(kāi)發(fā)了Takipi這個(gè)Java工具。Takipi是一個(gè)Java代理,它能在生產(chǎn)環(huán)境下追蹤未捕獲的異常、捕獲異常以及記錄服務(wù)器上的錯(cuò)誤日志。使用這個(gè)工具,你可以在堆棧中看到引發(fā)異常的變量值,然后在你的代碼中修改它們。

原文鏈接:? takipi ?翻譯:? ImportNew.com? -? justyoung

譯文鏈接:?http://www.importnew.com/16566.html


from:?http://www.importnew.com/16566.html

總結(jié)

以上是生活随笔為你收集整理的八成Java开发者解答不了的问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。