開發(fā)者應(yīng)該避免使用的6個Java功能
作者從多年的經(jīng)驗中吸取到,有六個Java SE功能/API是不提倡Java程序員使用的,它們分別是Reflection、Bytecode manipulation、ThreadLocals、Classloaders、Weak/Soft references、Sockets。
本文作者是一名擁有多年Java開發(fā)經(jīng)驗的程序員,他從經(jīng)驗中得出,并不是所有的Java SE功能/API都值得程序員去使用,比如本文列舉的這6個,大家在使用前得慎重對待。以下是對原文的摘譯。
多年的Java開發(fā)經(jīng)驗告訴我,從長遠角度來看,以下這些Java SE功能/API,開發(fā)者***停止使用。
Reflection
Bytecode
manipulation
ThreadLocals
Classloaders
Weak/Soft references
Sockets
1.Reflection
Reflection即反射,在許多流行的庫里面都有反射機制,比如Spring和Hibernate。通過對業(yè)務(wù)代碼進行反思,我建議大家避免使用反射。下面列出我反對使用的原因:
首先涉及到代碼可讀性/工具支持。打開IDE并且在Java代碼里找到相互依賴關(guān)系。使用relection替換方法調(diào)用,并且試著重復(fù)該步驟。事情變的愈發(fā)不可收拾,正常情況下都應(yīng)該封裝好了再修改狀態(tài)。下面來看看具體代碼示例:
- public class Secret {
- private String secrecy;
- public Secret(String secrecy) {
- this.secrecy = secrecy;
- }
- public String getSecrecy() {
- return null;
- }
- }
- public class TestSecrecy {
- public static void main(String[] args) throws Exception {
- Secret s = new Secret("TOP SECRET");
- Field f = Secret.class.getDeclaredField("secrecy");
- f.setAccessible(true);
- System.out.println(f.get(s));
- }
- }
通過查看以上代碼可以得知,方法getDeclaredField()參數(shù)只有在運行時才可以被發(fā)現(xiàn)。而你也清楚,運行時產(chǎn)生的bug總比不執(zhí)行腳本要更加棘手。
其次,反射調(diào)用優(yōu)化是由JIT執(zhí)行的,一些優(yōu)化可能需要花費很長時間才能得到應(yīng)用,而有些優(yōu)化甚至都得不到應(yīng)用,所以關(guān)于反射的性能優(yōu)化有時會被數(shù)量化。但在一個典型的業(yè)務(wù)應(yīng)用程序中——你可能不會真正意識到這些性能開銷。
總之,開發(fā)者應(yīng)該通過AOP合理地在業(yè)務(wù)層使用反射,除此以外,你***離它遠遠的。
2.Bytecode manipulation.
字節(jié)碼操作,如果我看到你在Java EE應(yīng)用程序里直接使用CGLIB或ASM,我可能會立即跑開。
最糟糕的事情莫過于在編譯期間沒有任何可執(zhí)行的代碼。實際上,當產(chǎn)品在運行時,你根本不知道哪塊代碼在運行。所以,當你遇到麻煩時,會自然地把錯誤拋給運行時故障排除和調(diào)試,不過這樣反而會更麻煩。
3.ThreadLocals
這里有兩個不相關(guān)的原因,當我在業(yè)務(wù)層代碼里看到ThreadLocals時會顫抖。首先,在ThreadLocals的幫助里,你可能會看到許多變量的使用都沒有通過方法調(diào)用鏈來明確地向下傳遞。這在某些場合下是有用的,但當你一旦粗心,你會在代碼里構(gòu)建許多意料不到的依賴關(guān)系。
第二個不相關(guān)的原因與我日常的工作相關(guān),在ThreadLocals里存儲數(shù)據(jù)會引發(fā)內(nèi)存泄露。最起碼我遇到的Permgen泄露有十分之一都是使用ThreadLocals造成的,在結(jié)合了類加載器和線程池后,“java.lang.OutOfMemoryError:Permgen space”異常可能就馬上出現(xiàn)了。
4.Classloaders
首先,類加載器是一個復(fù)雜的野獸。你必須先了解它的層次結(jié)構(gòu)、委托機制、類緩存等等。即使你認為自己已經(jīng)掌握了,它可能還是不能正常工作。最終將導(dǎo)致一個類加載器泄露問題。因此我只能建議你將這個任務(wù)留給應(yīng)用服務(wù)器處理
5.Weak/Soft references
現(xiàn)在,你應(yīng)該更好的理解Java的內(nèi)部方法。使用軟引用來重寫所有的緩存并不明智。我知道,當你手上拿著錘子的時候,就會到處尋找釘子??蓪τ阱N子來說,緩存并不是個好釘子。為什么?基于軟引用構(gòu)建緩存可能是如何委托一些復(fù)雜因素到GC而不是通過自身實現(xiàn)的一個好例子。
下面舉個緩存的例子,你使用軟引用來創(chuàng)建數(shù)據(jù),當內(nèi)存被耗盡時,GC進入并且進行清理。但是,緩存中被刪除的對象并未得到你的控制,而且很有可能在下一次的cache-miss中重新創(chuàng)建。如果內(nèi)存仍然不足,你可以觸發(fā)GC進行再次清理。你可能已經(jīng)看出了整個運行過程的惡性循環(huán),整個應(yīng)用程序就變成了CPU與GC不斷運行的狀態(tài)了
6.Sockets
普通老式的java.net.Socket實在是太復(fù)雜,以至于很難弄正確。我覺得阻塞性是其根本性的缺陷。當你編寫一個典型的帶有Web前端的Java EE應(yīng)用程序時,應(yīng)用程序需要高并發(fā)度來支持大量的用戶,而你現(xiàn)在最不想發(fā)生的是不具有可擴展的線程池坐等阻塞套接字。
目前有許多精彩可用的第三方庫,使用它們可以更好的完成任務(wù),比如Netty,開發(fā)者不妨嘗試下。