五月天青色头像情侣网名,国产亚洲av片在线观看18女人,黑人巨茎大战俄罗斯美女,扒下她的小内裤打屁股

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

這篇文章匯聚33個(gè)BUG!來(lái)挑戰(zhàn)一下,看看你能找出來(lái)幾個(gè)?

2023-03-16 13:48 作者:董嘉dongjia  | 我要投稿

我寫的過(guò)程中我就感覺有些地方我可能答的不對(duì),沒(méi)回答到點(diǎn)子上,所以我特別希望你能帶著自己的思考去找問(wèn)題,如果我的答案和你的回答有出入,或者對(duì)于我的回答你有補(bǔ)充,也特別感謝你能在評(píng)論區(qū)分享你的回答,感謝。

我們碰撞一下,共同進(jìn)步。


另外,官方題解在這里:《看ChatGpt解去哪兒網(wǎng)第四屆CR大賽題目》

目前只解答了 7 道題目,后面的解答還在路上。但是官方的題解中整了個(gè)活兒,就是把整段代碼喂給 ChatGPT 讓它幫忙診斷有什么問(wèn)題,可以去看看。

我只能用一個(gè)字來(lái)形容它:


第 1 題


首先,拿到這題第一眼,我就看到一個(gè)非常別扭的地方,方法入?yún)ⅲ篶azeList?

不應(yīng)該是 caseList 嗎?

其實(shí)我理解 core review 不只是應(yīng)該關(guān)注業(yè)務(wù)邏輯,也應(yīng)該關(guān)注到這些命名規(guī)范、變量拼寫的“小地方”。

所以,秉著寧可錯(cuò)殺,不可放過(guò)的原則,這算是一個(gè)需要修改的點(diǎn)。

然后第二個(gè)點(diǎn):入?yún)⒉慌袛喾强盏膯幔?/p>

你看第 9 行,拿著 caseList 這個(gè)入?yún)⒕驮陂_始 get(0) 了。caseList 是傳入進(jìn)來(lái)的,萬(wàn)一傳了一個(gè) null 怎么辦?

不要給我說(shuō)外面可能判斷了,不會(huì)傳遞 null。不要基于假設(shè)去分析問(wèn)題,再次強(qiáng)調(diào):寧可錯(cuò)殺,不可放過(guò)。

在這里加上一個(gè)非空判斷,代碼的健壯性更強(qiáng)。

接著看代碼,在第 9 行做了一個(gè)批量插入的動(dòng)作。被 try-catch 代碼塊包裹了起來(lái)。

在 catch 代碼塊里面執(zhí)行了表不存在則創(chuàng)建,然后再插入的動(dòng)作。這個(gè)代碼能用,但是我個(gè)人覺得:不要在 catch 代碼塊里面寫過(guò)多的邏輯,也就是盡量不要基于異常去做程序邏輯控制。

在插入之前先執(zhí)行創(chuàng)建表的動(dòng)作,然后再執(zhí)行批量插入,這樣的正向邏輯它不香嗎?

總結(jié)一下第一題:

  1. 入?yún)⒚制磳戝e(cuò)誤,cazeList 應(yīng)該是 caseList。

  2. caseList 沒(méi)有做非空檢驗(yàn)。

  3. 應(yīng)該把 catch 代碼塊里創(chuàng)建表的動(dòng)作,放到 try 邏輯里面去。先嘗試創(chuàng)建表,再批量插入。

第 2 題


這題沒(méi)啥好說(shuō)的,直接秒殺。

五秒之內(nèi)沒(méi)有看出問(wèn)題,說(shuō)明“八股文”背的不牢。

我問(wèn)你:Spring 事務(wù)注解生效的前提是什么?

是不是事務(wù)方法的調(diào)用方得是經(jīng)過(guò) Spring 代理后的對(duì)象?

那么上面代碼中的事務(wù)會(huì)生效嗎?

肯定不會(huì)的。

經(jīng)典的 this 調(diào)用導(dǎo)致事務(wù)注解失效的場(chǎng)景,一眼秒殺之。


第 3 題


這題也應(yīng)該屬于可以“秒殺”的題目。

你看一下 getInstance 方法的實(shí)現(xiàn),這是什么老伙計(jì)?

這不就是我們可愛的基于雙重鎖檢查實(shí)現(xiàn)的單例模式嗎?

當(dāng)面試官問(wèn)你雙重鎖檢查的時(shí)候,考點(diǎn)是什么?

第一個(gè)看鎖對(duì)象,這里是 class 對(duì)象,全局一把鎖,沒(méi)毛病。

第二個(gè)問(wèn)你為什么要判斷兩次 instance == null。

因?yàn)橛锌赡?A,B 兩個(gè)線程都執(zhí)行到了 16 行去搶鎖。這個(gè)時(shí)候 A 先搶到了,B 就被阻塞。A 線程創(chuàng)建完一個(gè)實(shí)例之后釋放了鎖,這個(gè)時(shí)候 B 繼續(xù)執(zhí)行,如果不再次判斷 instance == null,那么就會(huì)再次執(zhí)行初始化的動(dòng)作。

第三個(gè)考察點(diǎn) instance 為什么要加 volatile?

因?yàn)橐粋€(gè)對(duì)象的創(chuàng)建是分為三步的:

  1. 分配對(duì)象的內(nèi)存空間

  2. 初始化對(duì)象

  3. 設(shè)置instance指向剛分配的內(nèi)存地址

如果 2 和 3 步被重排序了,那么就會(huì)出現(xiàn)一個(gè)有內(nèi)存地址,但是還沒(méi)有初始化完成的對(duì)象。

如果這個(gè)對(duì)象被其他線程訪問(wèn)到了,那么就是有問(wèn)題的。所以我們加一個(gè) volatile 來(lái)就禁止指令重排序,來(lái)規(guī)避這個(gè)問(wèn)題。

這些都是老八股了,就不細(xì)說(shuō)了。

拿著剛剛說(shuō)的三個(gè)點(diǎn),去和題目里面的代碼對(duì)比,就會(huì)發(fā)現(xiàn)題目里面的代碼是沒(méi)有加 volatile,所以這就是要修復(fù)的地方。

另外,多說(shuō)一句。加 volatile 這個(gè)方案,得 JDK5 版本以上使用,因?yàn)樵?JDK5 之后,使用的新的 JSR-133 內(nèi)存模型規(guī)范,在這個(gè)規(guī)范里面增強(qiáng)了 volatile 的語(yǔ)義。

該死,這句話是怎么出現(xiàn)在我的腦子里面的?

它就像是突然蹦出的一樣,這就是傳說(shuō)中的刻在基因里面的“八股文”吧。

第 4 題


這題第一個(gè)非常明顯的優(yōu)化點(diǎn)在第 20 行。

要是看到這句話之后你還沒(méi)反應(yīng)過(guò)來(lái)是咋回事,那完?duì)僮恿?,多半是要回去等通知了?/p>

對(duì)于“流”的操作,關(guān)閉流的代碼必須放在 finally 代碼塊里面啊,或者用 try-with-resource 語(yǔ)法糖,這是早期學(xué) JavaSE 的時(shí)候就要重點(diǎn)掌握的東西了。

第二個(gè)要一眼秒殺的優(yōu)化點(diǎn)是 22 行。

誰(shuí)教你這樣打印日志的?這樣打印可以說(shuō)完全沒(méi)有任何卵用啊,因?yàn)閬G失了“故事現(xiàn)場(chǎng)”。

應(yīng)該這樣寫:

log.error("出現(xiàn)異常啦",e);

保留整個(gè)異常堆棧,方便后續(xù)定位問(wèn)題,

第三個(gè)問(wèn)題是 fileName,文件名稱用的是時(shí)間戳,這樣是有并發(fā)風(fēng)險(xiǎn)的。雖然看描述,這是一個(gè)給運(yùn)營(yíng)人員使用的功能,沒(méi)啥并發(fā)量。在這個(gè)場(chǎng)景下,屬于“錯(cuò)殺”。

但是,“并發(fā)”這根弦還是應(yīng)該繃緊的。萬(wàn)一是外部客戶導(dǎo)出功能呢?萬(wàn)一外部客戶很多呢?萬(wàn)一很多外部客戶就是在同一時(shí)間發(fā)起了統(tǒng)計(jì)呢?

第四個(gè)問(wèn)題是我沒(méi)搞懂第 11 行里面在干啥,因?yàn)轭}目中沒(méi)有給出這部分的代碼。

我理解應(yīng)該是一個(gè)從某個(gè)數(shù)據(jù)源根據(jù)指定參數(shù)查詢數(shù)據(jù)的功能。

為什么要把數(shù)據(jù)查出來(lái)之后再和 TOP_CAPCITY 比較并截取 list 呢?

如果運(yùn)營(yíng)商很多,比如有 10w 個(gè)運(yùn)營(yíng)商,但是運(yùn)營(yíng)只想看前 10 的運(yùn)營(yíng)商,也就是 TOP_CAPCITY=10 的情況。

這樣把 10w 個(gè)運(yùn)營(yíng)商查詢出來(lái)完全沒(méi)有意義,而且還容易產(chǎn)生大對(duì)象,對(duì) GC 也不友好啊。

所以,為什么不在查詢數(shù)據(jù)源的時(shí)候,把 TOP_CAPCITY 作為參數(shù)傳進(jìn)去呢?

我認(rèn)為這是第四個(gè)優(yōu)化點(diǎn)。

總結(jié)一下第四題:

  1. 輸出流關(guān)閉未放到 finally 代碼塊,或者用 try-with-resource。

  2. 日志打印不規(guī)范。

  3. 文件名稱用的是時(shí)間戳,有并發(fā)風(fēng)險(xiǎn)。

  4. 查詢數(shù)據(jù)的時(shí)候應(yīng)該把 TOP_CAPCITY 作為條件,而不是全部查詢出來(lái)再截取。

第 5 題


針對(duì) future 進(jìn)行異常捕獲的時(shí)候,只捕獲了 ExecutionException 異常。我覺得至少還得捕獲一個(gè) TimeoutException 吧。

因?yàn)檫@個(gè) future 是 dubbo 接口異步獲取的,所以 get 操作獲取超時(shí)的場(chǎng)景應(yīng)該還是比較常見吧。

另外一個(gè)就是 19 行和 29 行的日志打印,我不知道為什么沒(méi)有采用 {} 占位符的方式去做,而是采用了 + 拼接。這個(gè)點(diǎn)由于 TITLE_Arg1 參數(shù)到底長(zhǎng)啥樣的,代碼里面沒(méi)有體現(xiàn)出來(lái),所以只是提上一嘴。

但是同樣是日志打印,34 行的日志打印和前面的一樣,丟失了“故事現(xiàn)場(chǎng)”,沒(méi)有把 e 打出來(lái)。

第 6 題


這一題,我首先想到的第一個(gè)是沒(méi)有對(duì) redPlanIdList 對(duì)象進(jìn)行非空判斷。

然后一看 Safes.of 這是什么寫法,沒(méi)見過(guò)啊。

于是用關(guān)鍵字搜索了一把:


相關(guān)的結(jié)果很少很少,只有看到這個(gè)靠譜一點(diǎn):


這是別人自己封裝的一個(gè)方法,并不是什么組件里面的。

好吧,就姑且當(dāng)這個(gè)方法是對(duì) null 對(duì)象進(jìn)行了處理吧,把 null 轉(zhuǎn)化為了空集合,避免了空指針異常。

然后我們接著往下看,在第 7 行使用了 parallelStream,并行流來(lái)對(duì) redPlanIdList 對(duì)象進(jìn)行操作。

但是你看一下第五行,我們知道 redPlanIdList 這個(gè)對(duì)象是一個(gè) List,但是它具體是什么 List 呢,是 ArrayList 還是 CopyOnWriteArrayList 呢?

不得而知。

那我們假設(shè)是非線程安全的 ArrayList 呢?

有的同學(xué)看到這里的時(shí)候,是不是要開始搶答了:這題我會(huì),在多線程里面使用了 ArrayList,有線程安全問(wèn)題...

不對(duì)啊,搶答的不對(duì)啊。

即使這個(gè)地方的 redPlanIdList 是非線程安全的 ArrayList 也沒(méi)有關(guān)系。因?yàn)樵诓⑿辛骼锩?,只是遍歷了它,并沒(méi)有對(duì)它進(jìn)行新增操作。

所以 redPlanIdList 并沒(méi)有線程安全問(wèn)題。

有問(wèn)題的是 15 行,result 是 ArrayList,調(diào)用了 addAll 方法,有線程安全問(wèn)題。需要使用線程安全的集合類。

或者,把 parallelStream 修改為單線程的 stream,對(duì)吧?

對(duì)嗎?

從程序邏輯上講是對(duì)的,使用單線程沒(méi)有任何毛病。

但是從需求背景上講,這個(gè)地方完完全全就可以、也應(yīng)該使用多線程的方式去提升響應(yīng)速度嘛。

所以,總結(jié)一下這題:在多線程里面調(diào)用了 arrayList 的 addAll 方法,有線程安全問(wèn)題。需要使用線程安全的集合類。

另外,追問(wèn)一個(gè)八股文:請(qǐng)問(wèn) ArrayList 的線程不安全具體體現(xiàn)在什么地方?

針對(duì)這個(gè)問(wèn)題,早年間,我還寫過(guò)相關(guān)的文章,如果你不清楚的話,可以去翻翻:《ArrayList 的線程不安全》

這里就直接說(shuō)答案了。

ArrayList 的線程不安全體現(xiàn)在多線程調(diào)用 add 方法的時(shí)候。具體有兩個(gè)表現(xiàn):

  1. 當(dāng)在需要進(jìn)行數(shù)組擴(kuò)容的臨界點(diǎn)時(shí),如果有兩個(gè)線程同時(shí)來(lái)進(jìn)行插入,可能會(huì)導(dǎo)致數(shù)組下標(biāo)越界異常。

  2. 由于往數(shù)組中添加元素不是原子操作,所以可能會(huì)導(dǎo)致元素覆蓋的情況發(fā)生。

第 7 題


這題,我靠,第一眼,不得秒了它?

遍歷 ArrayList 的同時(shí),還在調(diào)用它的 remove(int index) 方法。這寫法,江湖大忌啊。

在這個(gè)題目里面,由于調(diào)用了 remove(int index) 方法,所以導(dǎo)致隊(duì)列的長(zhǎng)度在變化,因此第 7 行會(huì)拋出數(shù)組下標(biāo)越界的異常:


此外,如果把題目中的 for 循環(huán)修改為這樣:

for (String s : testStrArray) { ? ?testStrArray.remove(s); }

也是要完?duì)僮拥膶懛?,?huì)拋出 ConcurrentModificationException,老八股了,不細(xì)說(shuō)了。

牢記一句話:針對(duì) ArrayList 的刪除,用迭代器來(lái)實(shí)現(xiàn),穩(wěn)得一筆。

除了這個(gè)顯而易見的問(wèn)題之外,在上面的題目中還有一處可以優(yōu)化的地方。

就是 emp、empany、q、t 這幾個(gè)變量名字取的真丑。你取名的時(shí)候,取做 isAllMatch、isAnyMatch、findFirstOfStr 它不香嗎?

第 8 題


問(wèn)題很好發(fā)現(xiàn),精度丟失。

改正嘛...

為什么不問(wèn)問(wèn)神奇的 ChatGPT 呢?


第 9 題


這個(gè) B...

我是說(shuō)這個(gè)類名稱,B,是不是有點(diǎn)太隨意了?

當(dāng)然我猜測(cè)這可能是主辦方為了脫敏,隨便取了一個(gè) B,不過(guò)這是不是有點(diǎn)太隨意了?

然后看看第 10 行,我理解不應(yīng)該把異常拋給前端吧,除非你項(xiàng)目里面有全局異常處理器,但是題目中沒(méi)體現(xiàn),所以也提一嘴,保平安。

接著 15 行,又是流沒(méi)有關(guān)閉,前面已經(jīng)出現(xiàn)過(guò)一次了,不贅述。

再看看 16 行,HashSet 這玩意也不是線程安全的啊。所以在線程池里面,多線程調(diào)用其 add 方法,得改。

另外,你這個(gè) HastSet 對(duì)象的名稱叫做 visted。

我知道你是想表達(dá)這個(gè) Set 里面放的是被訪問(wèn)過(guò)的 url。

但是 visted,什么鬼?不應(yīng)該叫做 visited 嗎,老鐵。

既然都說(shuō)到拼寫錯(cuò)誤了,那么你在看看這個(gè)方法名:proceessLog。

處理日志,應(yīng)該是 processLog 吧。

多了一個(gè)字母,有一點(diǎn)代碼潔癖的人,看得難受。

最后,最嚴(yán)重的一個(gè)問(wèn)題,你看看第 10 行的這個(gè)線程池:newCachedThreadPool。

這玩意的調(diào)用的線程池的構(gòu)造方法是這樣的:


這是一個(gè)可以不斷開新線程的線程池啊,然后你再讀題:在文件中有大量的 URL。

“大量”,完?duì)僮樱€程過(guò)多,性能反而拉胯。

另外,線程池提交用的是 execute 方法,應(yīng)該用 try-catch 代碼塊把線程池里面的邏輯給包起來(lái),在 catch 里面記錄日志,免得后面出問(wèn)題了,日志都找不到。

第 10 題

官方給的題目中,第 9 題和第 10 題重復(fù)了。

我大抵是病了,橫豎都睡不著,坐起身來(lái)點(diǎn)起了一支煙,這悲傷沒(méi)有由來(lái),黯然看著面前的兩套考卷,一套是我的,另一套也是我的。

我翻開考卷一查,這題目沒(méi)有答案,歪歪斜斜的每頁(yè)上都寫著“找出BUG”四個(gè)字。我橫豎睡不著,仔細(xì)看了半夜,才從字縫里看出字來(lái),滿本都寫著兩個(gè)字是:卷起來(lái)。


第 11 題


這題,讀完題就得秒啊,得秒之。

都已經(jīng)說(shuō)的很明白了:

  1. is 開頭的 boolean 類型的屬性,用的是 FastJson 序列化。

  2. 反序列化的工具有可能是 FastJson,也有可能是 gson。

首先第一點(diǎn),即使你之前不知道 FastJson 對(duì)于 is 開頭的 boolean 類型屬性的處理方式,但是你從題目上也能讀出來(lái),題目上特意寫了這句話,說(shuō)明里面是有故事的。

第二個(gè),序列化工具是 FastJson,但是反序列化可能是 FastJson 也可能是 gson。

這不就離了大譜了嗎?


上面的代碼我也拿出來(lái)給你跑一下,就很清楚了:


FastJson 序列化的時(shí)候,把 isWmsSend 變成了 wmsSend。然后用 Gson 反序列化的時(shí)候,發(fā)現(xiàn)沒(méi)有 isWmsSend 這個(gè)屬性呀,再一看,是基本類型的 boolean,那就給默認(rèn)值,false 吧。

所以 isWmsSend 應(yīng)該用包裝類 Boolean,盡量不使用基本類型 boolean。因?yàn)榘b類的默認(rèn)值是 null,基本類型的默認(rèn)值是 false。

在你的業(yè)務(wù)代碼中,null 和 false 代表的可完全不是一個(gè)東西。

因?yàn)榉葱蛄谢膯?wèn)題導(dǎo)致把 true 變成了 false,這一聽就是一個(gè)大鍋啊,可不敢亂背。

第 12 題


看到這個(gè)題的第一眼,我的注意力是放在注釋上的。

你看第 13 行到第 15 行的注釋是放在代碼后面的。但是第 23 行到 28 行的注釋又是放在代碼上面的。

這是兩種不同的風(fēng)格,我個(gè)人喜歡的是第二種。但是不管你喜歡哪一種,你至少統(tǒng)一一下吧。

還有,這里又出現(xiàn)了 proccess 這個(gè)單詞。前面第 9 題的時(shí)候,寫的是 proceess。

我能說(shuō)什么呢?

我只能單打一個(gè):6。


我承認(rèn),這兩個(gè)點(diǎn)確實(shí)有點(diǎn)吹毛求疵了,但是作為有點(diǎn)代碼潔癖的我來(lái)說(shuō),必須要說(shuō)出來(lái)才爽。

然后,再說(shuō)說(shuō)這個(gè)代碼真正有問(wèn)題的地方。

第一個(gè)其實(shí)很明顯,一千萬(wàn)的數(shù)據(jù)量,一頁(yè)讀取一千條數(shù)據(jù),用傳統(tǒng)的分頁(yè),到后面的深度分頁(yè),你會(huì)發(fā)現(xiàn)越來(lái)越慢,因?yàn)檫@個(gè)是有性能問(wèn)題的。

應(yīng)該采取游標(biāo)的方式,比如返回本次查詢的 max(id),然后下一次分頁(yè)的時(shí)候帶著 id 去查詢。

這個(gè)也算是一個(gè)八股文吧,如果你不了解的話,去找資料背一背。

第二個(gè)問(wèn)題是沒(méi)有對(duì) objectList 進(jìn)行非空判斷,防止空指針。

第三個(gè)問(wèn)題,應(yīng)該在 26 行的 for 循環(huán)里面搞個(gè)線程池,來(lái)提升數(shù)據(jù)處理速度。你這 1000w 的數(shù)據(jù)量了,單線程得搞到什么時(shí)候啊。

可能營(yíng)銷活動(dòng)都要結(jié)束了,用戶還沒(méi)收到推送。到時(shí)候活動(dòng)效果不好,又是程序員背鍋。

第四個(gè)問(wèn)題是 35 行這個(gè)方法。雖然我不懂 UserDao 這個(gè)寫法是什么意思,應(yīng)該是公司內(nèi)部封裝的持久化框架。

但是我看到這里是把 tableName 作為參數(shù)傳遞到了方法里面,這個(gè)時(shí)候就得提高警惕了:同志們,謹(jǐn)防 SQL 注入啊。安全的那幫哥們就喜歡掃這樣的代碼,拿出來(lái)公示。

第 13 題


這個(gè)題很有意思啊,因?yàn)樗目键c(diǎn)比較多,也算比較“偏一點(diǎn)”。

另外,終于把 process 寫對(duì)了,舒服了。


首先第一個(gè),第 7 行加鎖,第 8 行萬(wàn)一拋出異常了怎么辦?

所以這個(gè)鎖弄的有大問(wèn)題啊,按照規(guī)范來(lái)說(shuō),加鎖操作是要放在 try 代碼塊外的第一行。這之間不應(yīng)該有任何其他的代碼,以免加鎖成功了,但是未解鎖,出現(xiàn)死鎖的現(xiàn)象。

第二個(gè)問(wèn)題,很明顯,你看第 15 行,processWorkB 方法有兩個(gè)入?yún)?,但是?10 行調(diào)用的時(shí)候只給了一個(gè)?

這個(gè)應(yīng)該是題出的失誤了,不做追究。要是有人真寫出了這樣的代碼并沒(méi)有發(fā)現(xiàn)問(wèn)題,說(shuō)明他是個(gè)奇才。關(guān)鍵是這玩意,代碼都編譯不過(guò)啊。

第三個(gè)問(wèn)題,還是 processWorkB 方法,沒(méi)有對(duì) param 參數(shù)進(jìn)行非空判斷。

來(lái)我問(wèn)你一個(gè)問(wèn)題:如果 param 是 null 會(huì)出現(xiàn)什么情況?

肯定是空指針異常了啊。

但是,你有沒(méi)有想過(guò)一個(gè)問(wèn)題:switch/case 里面為什么不做成支持 null 的模式呢?

如果表達(dá)式為 null ,我們就拿著 null 去 case 里面匹配,這樣理論上做也是可以做的。

關(guān)于這個(gè)問(wèn)題,我再這篇文章里面做過(guò)探討,有興趣可以去看看:《被阿里一道基礎(chǔ)面試題給干懵了,一氣之下寫出萬(wàn)字長(zhǎng)文?!?/p>


第四個(gè)問(wèn)題:在 processWorkA 里面,隱藏的比較深,但是確實(shí)是一個(gè)我在實(shí)際編碼的過(guò)程中踩到過(guò)的一個(gè)坑。

簡(jiǎn)單來(lái)說(shuō)就是三目表達(dá)式里面的拆箱問(wèn)題。

巧了,這個(gè)問(wèn)題,我之前也提到過(guò)一次:《三目表達(dá)式的自動(dòng)拆箱問(wèn)題》

網(wǎng)上的相關(guān)解析也很多,如果不了解的話可以定向攻破一下。

具體到這個(gè)代碼中的問(wèn)題,就是中間的 params1*params2 之后的結(jié)果,類型是 int 型。由于三目表達(dá)式類型要對(duì)齊的特性,所以 params3 會(huì)被拆箱為 int 類型。

結(jié)果,params3 參數(shù)又是一個(gè) null。

哦豁,空指針就來(lái)了。


第 14 題


題目太長(zhǎng),我實(shí)在是看的費(fèi)勁兒。

就......不解析了吧。

你發(fā)現(xiàn)了問(wèn)題,可以在評(píng)論區(qū)教教我。

第 15 題


這一題,說(shuō)實(shí)話,我第一眼沒(méi)看出來(lái)問(wèn)題。

隔了一會(huì),第二眼看,也沒(méi)看出來(lái)啥問(wèn)題。

要不是這是一道題目,代表它確實(shí)有問(wèn)題,不然在實(shí)際 review 的時(shí)候,我應(yīng)該就放過(guò)這個(gè)部分了。

當(dāng)我?guī)е欢ㄊ怯袉?wèn)題的心態(tài)去看問(wèn)題的時(shí)候,終于還是發(fā)現(xiàn)了端倪。

第 6 行,多線程操作,一般出題人會(huì)在多線程里面埋坑,只要找出多線程里面線程不安全的操作即可。

而多線程里面的核心邏輯是給 Status 的 reason 字段賦值。再定眼一看:Status 是個(gè)枚舉啊。

枚舉是單例啊。

單例對(duì)象的一個(gè)字段,在多線程里面被瘋狂操作...

你明白我意思吧?

我給你搞段代碼驗(yàn)證一下:


首先,把 List 里面的對(duì)象搞得多多的。

然后,closeStatus 方法里面打印 log 的時(shí)候,入?yún)⒌?id 肯定要和 Status 枚舉中的 reason 這個(gè)字符串里面拼接的 id 一樣,對(duì)吧?

所以,我在 log 里面打印日志的時(shí)候,判斷 reason 不包含當(dāng)前的 id。

如果有輸出,則說(shuō)明有問(wèn)題,能反應(yīng)過(guò)來(lái)吧?

程序跑起來(lái),確實(shí)有輸出:那就說(shuō)明確實(shí)有問(wèn)題:

id:4548 當(dāng)前的狀態(tài)是:關(guān)閉 變更原因?yàn)?關(guān)閉當(dāng)前狀態(tài)3370id:796 當(dāng)前的狀態(tài)是:關(guān)閉 變更原因?yàn)?關(guān)閉當(dāng)前狀態(tài)3300id:8621 當(dāng)前的狀態(tài)是:關(guān)閉 變更原因?yàn)?關(guān)閉當(dāng)前狀態(tài)8000id:9791 當(dāng)前的狀態(tài)是:關(guān)閉 變更原因?yàn)?關(guān)閉當(dāng)前狀態(tài)7528id:8283 當(dāng)前的狀態(tài)是:關(guān)閉 變更原因?yàn)?關(guān)閉當(dāng)前狀態(tài)7842

不要在多線程里面對(duì)單例對(duì)象進(jìn)行修改操作,你把握不住。

第 16 題


這題,拿到手第一眼,非常扎眼的一個(gè)問(wèn)題就是 SearchCategory 對(duì)象里面的屬性沒(méi)有被 private 修飾,不滿足 Java 對(duì)于對(duì)象進(jìn)行封裝的思想。

第二個(gè)考察點(diǎn)其實(shí)也不難,第 22 行,兩邊都是 Integer 對(duì)象,針對(duì)封裝對(duì)象的對(duì)比,應(yīng)該使用 !equals 而不是 !=。

別問(wèn)為什么,問(wèn)就是 Integer 緩存了 -128~127 之間的數(shù)字,在這個(gè)范圍內(nèi)用 == 和這個(gè)范圍外用 ==,運(yùn)行結(jié)果不一樣。

老八股了,不細(xì)說(shuō)了。

第三個(gè)考察點(diǎn),稍微稍微隱蔽一點(diǎn):SearchCategory 沒(méi)有重寫 hashcode 和 equals 方法。

categorySet 是一個(gè) Set 集合對(duì)象。第 25 行里面,在把 SearchCategory 往 categorySet 里面放。

自定義對(duì)象往 Set 集合里面放,如果不重寫 hashcode 和 equals 方法,那么會(huì)有重復(fù)元素滴。

如果這個(gè)你一時(shí)間沒(méi)反應(yīng)過(guò)來(lái)的話,那我問(wèn)你一個(gè)老八股:HashMap 的 key 是自定義對(duì)象的時(shí)候,應(yīng)該注意什么?

這樣一聽,是不是就熟悉多了。

你在結(jié)合我之前寫過(guò)的這篇文章:《悄悄給你說(shuō)幾個(gè)HashCode的破事。》

輕松拿下。

第 17 題


這題我沒(méi)搞懂第 6 行和第 9 行,調(diào)用了 queryUserStock 方法,但是這個(gè)方法又在同一個(gè)類里面。

為什么要通過(guò)自己注入自己的方式去調(diào)用呢?

又不涉及到比如事務(wù)、緩存這些切面相關(guān)的東西。所以我覺得這兩行代碼沒(méi)用。

然后是第 4 行里面的入?yún)?userId 是基本類型 long,而到了 14 行里面就變成了包裝類型 Long。

所以,從這個(gè)代碼片段來(lái)說(shuō),15 行的判斷永遠(yuǎn)為 true,因?yàn)閭鬟f進(jìn)來(lái)的 userId 是基本類型,默認(rèn)值為 0,不可能為 null。

應(yīng)該保持類型一致,都用包裝類型。

還有一個(gè)點(diǎn)是 11 行,對(duì)于返回的集合沒(méi)有做非空判斷,萬(wàn)一空指針了呢,對(duì)吧?


對(duì)個(gè)毛線,我在這里虛晃一槍,看看你有沒(méi)有帶著自己的思考看文章。

14 行的方法是不會(huì)返回空指針的,如果沒(méi)查到數(shù)據(jù),MyBatis 會(huì)自動(dòng)幫我們封裝為空集合,而不是 null。

所以外面拿到空集合也沒(méi)有任何毛病。

另外,在這里我再次重申一下:官方也沒(méi)有給出全部的題目解析,所以我給的答案不一定是正確的,你要帶著批判的眼光來(lái)看。

我等著你發(fā)現(xiàn)錯(cuò)誤,指正我呢。

第 18 題


題目做到這里后,一看到多線程,就開始條件反射式的想到了線程安全問(wèn)題。

定眼一看,33 行,CopyOnWriteArrayList 是線程安全的,看來(lái)這次的考察點(diǎn)不一樣了。

從上往下看,首先是自定義線程池,我一眼就看出了問(wèn)題:

(r, executor) -> log.error("...")

自定義拒絕策略中,僅僅打印了一行日志,沒(méi)有做其他任何操作,有坑,容易喜提生產(chǎn)事件:


詳情見連接:《雖然是我遇到的一個(gè)棘手的生產(chǎn)問(wèn)題,但是我寫出來(lái)之后,就是你的了?!?/p>

所以這個(gè)拒絕策略需要重新寫一個(gè)。

第二個(gè)問(wèn)題:21 行調(diào)用了 filter 方法,在這個(gè)方法里面用到了線程池,但是你仔細(xì)分析一下 34 行的這個(gè) for 循環(huán),要循環(huán)多少次?

是不是要循環(huán)“一頁(yè)的大小”,也就是第 19 行的 500 次。但是這個(gè)線程池的容量才多少?

這個(gè)線程池同時(shí)間做多也只能容納 120 個(gè)任務(wù),所以這個(gè)地方參數(shù)不合理,需要調(diào)整線程池參數(shù)。

另外,34 行這個(gè)循環(huán)對(duì)象也沒(méi)有判空啊。這是一個(gè) JSONObject 對(duì)象,一不留神,取出一個(gè) null 怎么辦?

第三個(gè)問(wèn)題,35 行,用了 submit 提交任務(wù),這個(gè)方法是有返回值的,但是代碼里面又沒(méi)有使用這個(gè)返回值,那么為什么不用 execute 方法呢?

第四個(gè)問(wèn)題,47 行,await 方法沒(méi)有指定等待時(shí)間,容易死等。

比如就這個(gè)代碼,線程池滿了之后,并不會(huì)執(zhí)行 countDown 方法,喜提一個(gè)永遠(yuǎn)在阻塞的線程。

這題考多線程,這我熟啊,秒了,再見。

第 19 題


這題沒(méi)啥特別好說(shuō)的,第 18 行 repaceFirst 的第一個(gè)入?yún)⑹钦齽t表達(dá)式。


所以,如果供應(yīng)商的名字里面有一些什么 *?.| 這些玩意,就會(huì)出現(xiàn)問(wèn)題。

其實(shí) Sting 的一些關(guān)于內(nèi)容替換的方法里面好多都是支持正則的,所以如果你不清楚這個(gè)“坑”的話,那后面需要注意一下了。

這個(gè)題就不多說(shuō)了,寫個(gè)案例,一目了然:


別問(wèn)我為什么一眼就看到了 repaceFirst,因?yàn)槲以谒砩铣赃^(guò)虧。還有 split,這個(gè)“坑爹”的方法,也是一樣,有正則坑。

第 20 題


這題...

恕我學(xué)藝不精,確實(shí)沒(méi)發(fā)現(xiàn)有什么問(wèn)題???

難道是 13 行這個(gè) false?它代表用 JDK 的動(dòng)態(tài)代理,要實(shí)現(xiàn)接口,但是這個(gè)代碼也確實(shí)實(shí)現(xiàn)了接口呀?

肉眼沒(méi)看出來(lái),我把代碼放到自己的 idea 里面去跑也是能正常跑的:


如果這題在 AOP 上有考點(diǎn)的話,而且我覺得肯定是有考點(diǎn)的,我確實(shí)拿不到這個(gè)分。

如果考的是不要把 http 接口寫在項(xiàng)目啟動(dòng)類上的這種編碼結(jié)構(gòu)上的規(guī)范的話。好吧,只能說(shuō)考查角度刁鉆。

不知道你有沒(méi)有看出啥問(wèn)題,可以在評(píng)論區(qū)教教我。

第 21 題


只有幾行代碼,秒之。

第 7、8 行,用 redis 加鎖,set 方法和給值設(shè)置過(guò)期時(shí)間的方法非原子性,如果 set 完成之后,expire 無(wú)法執(zhí)行, 會(huì)出現(xiàn)死鎖。

第 11 行,捕獲異常后沒(méi)有通過(guò)日志打印異常具體的信息,不方便追溯問(wèn)題。


第 22 題


其實(shí)題目做到這里的時(shí)候,都已經(jīng)有點(diǎn)掉入思維陷阱里面了。

啥意思呢。

比如這個(gè)題,我只是瞟了一眼,我就知道 34 行的這個(gè)方法里面肯定是有問(wèn)題的。

因?yàn)槿绻麤](méi)有問(wèn)題,按照出題人的習(xí)慣,這個(gè)方法的實(shí)現(xiàn)會(huì)略去。

所以,當(dāng)我?guī)е@個(gè)想法去看這部分代碼的時(shí)候,getCerts 之后直接調(diào)用了 get(0),不用判斷集合對(duì)象是否為空嗎?

但是,你注意啊,其實(shí)在 23 行的時(shí)候判斷了。

所以這個(gè)地方其實(shí)不是空指針的問(wèn)題。

我覺得應(yīng)該是需要結(jié)合到業(yè)務(wù)場(chǎng)景來(lái)看的,certs 是一個(gè)集合,里面放的是乘機(jī)人的證件,一個(gè)人可能有多個(gè)證件,比如身份證、護(hù)照、工牌、打工人認(rèn)證、一疊好人卡等等這些玩意,所以用集合來(lái)裝,和合理。

但是,get(0) 你怎么給我保證獲取出來(lái)的不是好人卡,而是身份證呢?

代碼中都沒(méi)有體現(xiàn)這個(gè)邏輯,所以我懷疑出題人在這里是埋坑了。

第二個(gè)問(wèn)題就很明顯了,你仔細(xì)看看題目中的三次 stream 操作。前兩次操作的都是同一個(gè)集合,然后用結(jié)果去覆蓋了 passengers 參數(shù)。

看代碼,是會(huì)一點(diǎn)點(diǎn)流式操作的,但是不多,不然也不會(huì)分開寫了,三次操作完全可以合并為一個(gè)。


第 23 題


這題一看...

代碼太多,看得我眼睛充血,腦殼疼。

再一看題,按照“充血模式”開發(fā),補(bǔ)全一些代碼。

這玩意感覺得結(jié)合著業(yè)務(wù)來(lái)才行啊,算了,我放棄,好吧。

這題我不做了。

你來(lái)。

第 24 題


感覺問(wèn)題在 21 行的這個(gè) Lists.transform 方法上,但是我沒(méi)有用過(guò)這個(gè)方法,不清楚是干啥的。

于是在網(wǎng)上查了一圈:


簡(jiǎn)單來(lái)說(shuō)就是第 24 行這個(gè) for 循環(huán)是沒(méi)有用的。你可以理解未返回的這個(gè) addresses 僅僅是一個(gè)視圖,對(duì)視圖進(jìn)行修改,沒(méi)有任何卵用。

然后是同樣沒(méi)有看到對(duì)于異常的處理機(jī)制,如果沒(méi)有全局異常處理機(jī)制的話,有可能會(huì)直接拋給用戶不友好的錯(cuò)誤提醒。

第 25 題


第 29 行,又看到事務(wù)了,又是事務(wù)不生效,就不多說(shuō)了。這個(gè)事務(wù)不生效這一點(diǎn)確實(shí)是一大考點(diǎn),一定要把這個(gè)玩意搞的滾瓜爛熟才行。

然后是 41 行到44 行,這是個(gè)什么神奇的寫法,鏈?zhǔn)?set?沒(méi)見過(guò)。

所以我感覺這個(gè)地方也是有問(wèn)題的,應(yīng)該是想用 Builder 模式來(lái)構(gòu)建對(duì)象吧。

另外,saveData 方法里面有一些數(shù)據(jù)庫(kù)操作,難免會(huì)出現(xiàn)一點(diǎn)異常情況,在這個(gè)方法里面我沒(méi)有看到對(duì)于異常的相關(guān)捕獲和處理。

最后一個(gè)點(diǎn),38 行,除非你用了 MDC 鏈路追蹤,不然這行日志沒(méi)有任何意義啊,至少再加上一個(gè)訂單號(hào)吧。才能一眼看出來(lái)是哪個(gè)訂單號(hào)被成功更新了。

附加題

官方的題目只公開了 25 題,但是歪歪歪師傅也想給你出幾道附加題,讓你來(lái)體驗(yàn)一下。

我丑話說(shuō)在前頭:做不出來(lái)的,都是假粉絲。


再多說(shuō)一句,我都說(shuō)了是附加題了,這些題肯定就很偏了,看著圖個(gè)樂(lè)就行,真正的代碼 review 注意點(diǎn),還是得看前面的題目。

附加題一

請(qǐng)找出下面代碼的問(wèn)題:


《附加題一的答案》

附加題二

請(qǐng)找出下面代碼的問(wèn)題:


《附加題二的答案》

附加題三

請(qǐng)解釋下面代碼不能正常結(jié)束的原因:


《附加題三的答案》

附加題四

asyncResult 是一個(gè) Future 對(duì)象,下面框起來(lái)的代碼可以認(rèn)為是無(wú)限期等待,請(qǐng)指出其和 future.get() 的區(qū)別:


《附加題四的答案》

附加題五

請(qǐng)找出下面代碼的問(wèn)題:


下面這個(gè)環(huán)節(jié)叫做[荒腔走板],技術(shù)文章后面我偶爾會(huì)記錄、分享點(diǎn)生活相關(guān)的事情,和技術(shù)毫無(wú)關(guān)系。我知道看起來(lái)很突兀,但是我喜歡,因?yàn)檫@是一個(gè)普通博主的生活氣息。

荒腔走板


讀高中的時(shí)候,老師要求我們自愿購(gòu)買教輔資料。后來(lái)有幾個(gè)同學(xué)由于生活困難,考慮之后決定不買。老師就嚴(yán)厲得批評(píng)了他們,大家都買了,你們?yōu)槭裁床毁I呢?后來(lái)他們還是從生活費(fèi)中,摳出了錢,去買了。老師很開心。

后來(lái)想著以后有幸當(dāng)老師的話,一定不會(huì)這樣去做。

工作的時(shí)候,老板要求我們自愿周六加班,說(shuō)這是奮斗者的工作日。后來(lái)有幾個(gè)同事由于留給自己的休息時(shí)間確實(shí)不夠,考慮就不去“奮斗”了。老板就找他們談話,你們?yōu)槭裁床粖^斗呢?后來(lái)他們還是從少得可憐的休息時(shí)間中,擠出了時(shí)間,去奮斗。老板很開心。

現(xiàn)在想著,資本家真的很可惡,一個(gè)主要的原因是:

我不是資本家。


·············· END ··············


這篇文章匯聚33個(gè)BUG!來(lái)挑戰(zhàn)一下,看看你能找出來(lái)幾個(gè)?的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
金沙县| 武川县| 甘德县| 额敏县| 海原县| 太原市| 嘉义市| 康马县| 东平县| 济南市| 衡东县| 资兴市| 石首市| 尉氏县| 弋阳县| 虎林市| 桂平市| 宜都市| 龙岩市| 连州市| 中牟县| 松阳县| 招远市| 汕尾市| 永嘉县| 抚州市| 高唐县| 翁牛特旗| 红桥区| 贵定县| 襄汾县| 安塞县| 阳新县| 买车| 广安市| 吉林市| 开阳县| 铁力市| 丰台区| 社旗县| 库车县|