你應該遠離的6個Java特性

InfoQ中國發表於2015-03-09
坦白地說,從長遠來看,大多數團隊都應該遠離如下的Java特性。不過凡事總有例外的情況。如果你有一個強大的團隊,總是能夠清楚地意識到自己在做什麼,那就按照你的想法去做就行。但對於大多數情況來說,如果你在專案的開發中使用了下面這幾個Java特性,那麼從長遠來看你是會後悔的。

這些應該遠離的Java特性有:
  • 反射
  • 位元組碼操縱
  • ThreadLocal
  • 類載入器
  • 弱引用與軟引用
  • Sockets
下面對這些特性進行逐個分析,看看為什麼普通的Java開發者應該遠離他們:

反射
在流行的庫如Spring和Hibernate中,反射自然有其用武之地。不過內省業務程式碼在很多時候都不是一件好事,原因有很多,一般情況下我總是建議大家不要使用反射。
首先是程式碼可讀性與工具支援。開啟熟悉的IDE,尋找你的Java程式碼的內部依賴,很容易吧。現在,使用反射來替換掉你的程式碼然後再試一下,結果如何呢?如果通過反射來修改已經封裝好的物件狀態,那麼結果將會變得更加不可控。請看看如下示例程式碼:


如果這樣做就無法得到編譯期的安全保證。就像上面這個示例一樣,你會發現如果getDeclaredField()方法呼叫的引數輸錯了,那麼只有在執行期才能發現。要知道的是,尋找執行期Bug的難度要遠遠超過編譯期的Bug。

最後還要談談代價問題。JIT對反射的優化程度是不同的,有些優化時間會更長一些,而有些甚至是無法應用優化。因此,有時反射的效能損失可以達到幾個數量級的差別。不過在典型的業務應用中,你可能不會注意到這個代價。

總結一下,我覺得在業務程式碼中唯一合理(直接)使用反射的場景是通過AOP。除此之外,你最好遠離反射這一特性。

位元組碼操縱
如果在Java EE應用程式碼中直接使用了CGLIB或是ASM庫,那麼我建議你好好審視一下。就像方才我提到的反射帶來的消極影響,使用位元組碼操縱所帶來的痛苦可能是反射的好幾倍之多。
更糟糕的是在編譯期你根本就看不到可執行的程式碼。從本質上來說,你不知道產品中實際執行的是什麼程式碼。因此在面對執行期的問題以及除錯時,你要花費更多的時間。

ThreadLocal
看到業務程式碼中如果出現ThreadLocal會讓我感到顫抖,原因有二。首先,藉助於ThreadLocal,你可以不必顯式通過方法呼叫就可以傳遞變數,而且會對這種做法上癮。在某些情況下這麼做可能是合理的,不過如果不小心,那麼我可以保證最後程式碼中會出現大量意想不到的依賴。
第二個原因與我每天的工作有關。將資料儲存在ThreadLocal中很容易造成記憶體洩漏,至少我所看到的十個永久代洩漏中就有一個是由過量使用ThreadLocal導致的。連同類載入器及執行緒池的使用,“java.lang.OutOfMemoryError:Permgen space”就在不遠處等著你呢。

類載入器
首先,類載入器是個很複雜的東西。你必須首先理解他們,包括層次關係、委託機制以及類快取等等。即便你覺得自己已經精通了類載入器,一開始使用時還是會出現各種各樣的問題,很可能會導致類載入器洩漏問題。因此,我建議大家還是將類載入器留給應用伺服器使用吧。

弱引用與軟引用
關於弱引用與軟引用,你是不是隻知道他們是什麼以及簡單的使用方式而已?現在的你對Java核心有了更好的理解,那會不會使用軟引用重寫所有的快取呢?這麼做可不太好,可不能手裡有錘子就到處找鼓敲吧。
你可能很想知道我為什麼說快取不太適用使用軟引用吧。畢竟,使用軟引用來構建快取可以很好地說明將某些複雜性委託給GC來完成而不是自己去實現這一準則。
下面來舉個例子吧。你使用軟引用構建了一個快取,這樣當記憶體行將耗盡時,GC會介入並開始清理。但現在你根本就無法控制哪些物件會從快取中刪除,很有可能在下一次快取中不再有這個物件時重新建立一次。如果記憶體還是很緊張,又觸發GC執行了一次清理,那麼很有可能會出現一個死迴圈,應用會佔用大量CPU時間,Full GC也會不斷執行。

Sockets
java.net.Socket簡直太難使用了。我認為它的缺陷歸根結底源自其阻塞的本質。在編寫具有Web前端的典型的Java EE應用時,你需要高度的併發性來支援大量的使用者訪問。這時你最不想發生的事情就是讓可伸縮性不那麼好的執行緒池呆在那兒,等待著阻塞的Sockets。
現在已經出現了非常棒的第三方庫來解決這些問題,別自己寫了,嘗試一下Netty吧。
Java出現至今經歷了多次版本更迭,每次也都會有諸多新特性的加入。在日常的Java開發中,你認為存在哪些Java特性是很容易導致問題的呢?作者提到不建議在普通的應用開發中使用反射,不過對於一些框架或庫的開發,離開反射實際上是無法實現的,例如Spring、Struts2等框架,那麼在一般的Java專案開發中,你覺得哪些地方有使用反射的必要呢?換句話說,如果不使用反射就實現不了功能或是需求。文中作者也不建議使用位元組碼操縱,實際上一些框架在實現某些功能時是必須要使用的,比如說Spring在實現AOP時就使用了Java的動態代理與CGLib庫兩種方式來達成的。那麼對於一般的Java專案來說,哪些地方需要用到位元組碼操縱呢?歡迎各位讀者暢所欲言,一起討論這些有趣的話題。
來自:碼農網
評論(1)

相關文章