JavaSE_8系列部落格——專家之路(一)---Java反射的總結

獨孤文彬發表於2017-10-22

先來四個基本問題:

一、什麼是反射(what)?

生活中的反射

百科定義:反射(外文名reflection),指的是聲波、光波或其他電磁波遇到別的媒質分介面而部分仍在原物質中傳播的現象。如光的反射、波的反射。

光在兩種物質分介面上改變傳播方向又返回原來物質中的現象,叫做光的反射

程式設計語言中的反射

百科定義:反射是一種計算機處理方式。有程式可以訪問、檢測和修改它本身狀態或行為的這種能力。能提供封裝程式集、型別的物件。(程式集包含模組,而模組包含型別,型別又包含成員。)

Java中的反射

簡而言之,通過反射,我們可以在執行時獲得程式或程式集中每一個型別的成員和成員的資訊。

程式中一般的物件的型別都是在編譯期就確定下來的,而Java反射機制可以動態地建立物件並呼叫其屬性,這樣的物件的型別在編譯期是未知的。所以我們可以通過反射機制直接建立物件,即使這個物件的型別在編譯期是未知的。

 反射的核心是JVM在執行時才動態載入類或呼叫方法/訪問屬性,它不需要事先(寫程式碼的時候或編譯期)知道執行物件是誰。

其他語言中的反射

.NET平臺中的反射

https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/reflection

二、為什麼需要反射(why)?

歷史原因:

http://blog.csdn.net/coder_xia/article/details/14526055
http://blog.csdn.net/sole_ghost/article/details/1561646

反射的好處

執行期型別的判斷,動態類載入,動態代理使用反射。

  1. 帶來更好的擴充套件性,增加程式的靈活性。避免將程式寫死到程式碼裡

  2. 提供類檢視器的功能,在IDE中幫助人們更好更快更簡單的進行程式設計。

    當我們在使用IDE(如Eclipse,IDEA)時,當我們輸入一個物件或類並想呼叫它的屬性或方法時,一按點號,編譯器就會自動列出它的屬性或方法,這裡就會用到反射

  3. 除錯和測試工具中,我們將廣泛應用反射的特性,去檢查所有類中的成員、物件中的各種成員

反射的應用場景(where)?

1)Java的反射機制在做基礎框架的時候非常有用,有一句話這麼說來著:反射機制是很多Java框架的基石。而一般應用層面很少用,不過這種東西,現在很多開源框架基本都已經給你封裝好了,自己基本用不著寫。典型的除了Hibernate之外,還有Spring也用到很多反射機制。經典的就是在xml檔案或者properties裡面寫好了配置,然後在Java類裡面解析xml或properties裡面的內容,得到一個字串,然後用反射機制,根據這個字串獲得某個類的Class例項,這樣就可以動態配置一些東西,不用每一次都要在程式碼裡面去new或者做其他的事情,以後要改的話直接改配置檔案,程式碼維護起來就很方便了,同時有時候要適應某些需求,Java類裡面不一定能直接呼叫另外的方法,這時候也可以通過反射機制來實現。
總的來說,自己寫的很少,具體什麼時候要用那要看需求,反射機制無非就是根據一個String來得到你要的實體物件,然後呼叫它原來的東西。但是如果是要自己寫框架的話,那就會用得比較多了。

2)當你做一個軟體可以安裝外掛的功能,你連外掛的型別名稱都不知道,你怎麼例項化這個物件呢?因為程式是支援外掛的(第三方的),在開發的時候並不知道 。所以無法在程式碼中 New出來 ,但反射可以,通過反射,動態載入程式集,然後讀出類,檢查標記之後再例項化物件,就可以獲得正確的類例項。

3)在編碼階段不知道那個類名,要在執行期從配置檔案讀取類名, 這時候就沒有辦法硬編碼new ClassName(),而必須用到反射才能建立這個物件.反射的目的就是為了擴充套件未知的應用

三、反射的原理(how)

Java中是如何實現反射的?

小結:其實,用最簡單的方式來說。反射的實現原理,就和我們生活中的鏡面反射是一個道理,怎麼實現鏡面的反射?一個最簡單的做法,就是用一面鏡子,把隱藏在另外一個地方的資訊,通過利用鏡面把這些被封裝好,隱藏好的資訊反射出來,這樣我們通過鏡子就可以看到原本我們不能看到的東西。所以,在Java中,實現反射也是同樣的道理,詳情,可以參見Java中反射框架的實現。通過將class檔案(位元組碼檔案物件)中的所有資訊,通過遍歷的方式對其進行分門別類,獲取這些資訊,並分門別類的進行儲存(賦值給某個物件),然後再返回這些資訊。通過JDK本身提供的框架,我們就能夠獲取到這些資訊。簡單來說,可以分為以下幾步:
  

  1. 獲得Class物件,就是獲取到指定的名稱的位元組碼檔案物件。

  2. 例項化物件(建立指定類的物件),獲得類的屬性、方法或建構函式。

  3. 獲取類中的方法,屬性,建構函式

參考部落格:
http://www.cnblogs.com/makaruila/p/4852554.html

http://www.sczyh30.com/posts/Java/java-reflection-2/

四、如何使用反射?(how)

例項演示地址(java8官網提供)

https://docs.oracle.com/javase/tutorial/reflect/index.html

Java反射使用指南(網友整理)

http://ifeve.com/java-reflection/

深入淺出反射(網友博文)

https://zhuanlan.zhihu.com/p/21423208

反射的使用注意事項(避坑指南)

反射的缺點:

任何事物,都有兩面性,反射的優點,也同是就是它的缺點,所以,沒有好與壞,只有最合適的場景,一陰一陽,才是天道平衡的條件。

(1)使用反射的效能較低
(2)使用反射相對來說不安全
(3)破壞了類的封裝性,可以通過反射獲取這個類的私有方法和屬性

程式設計師在自己的業務開發中應該儘量的遠離反射

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

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

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

反射呼叫方法時可以忽略許可權檢查,因此可能會破壞封裝性而導致安全問題。

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

擴充問題:

要實現動態程式設計,除了反射還有……?

AOP?

嚴格意義上來說,AOP不是實現動態程式設計的技術,AOP是一種程式設計思想,要實現AOP需要利用一些動態程式設計的技術,例如:反射、動態編譯等動態程式設計的手段和技術,來達到動態控制程式的效果。

IoC?

同AOP的解釋,需要用到動態程式設計技術來實現,但IoC本身並不是動態程式設計技術。要實現IoC需要利用一些動態程式設計的技術,例如:反射、動態編譯等動態程式設計的手段和技術,來達到動態控制程式的效果。

語言級別動態

動態語言的示例有:Lisp、Smalltalk、JavaScript、PHP、Ruby、Python、ColdFusion、Lua、Cobra 和 Groovy。

大多數動態語言都會向開發人員提供以下優點:

  • 可以使用快速反饋迴圈(REPL 或讀取-計算-列印迴圈)。 這樣,您就可以在輸入幾條語句之後立即執行它們以檢視結果。
  • 同時支援自上而下的開發和更傳統的自下而上的開發。 例如,當您使用自上而下的方法時,可以呼叫尚未實現的函式,然後在需要時新增基礎實現。
  • 更易於進行重構和程式碼修改操作,原因是您不必在程式碼中四處更改靜態型別宣告。
  • 利用動態語言可以生成優秀的指令碼語言。 利用新的命令和功能,客戶可以輕鬆地擴充套件使用動態語言建立的應用程式。
  • 動態語言還經常用於建立網站和測試工具、維護伺服器場、開發各種實用工具以及執行資料轉換。

Java中對動態程式設計的支援

主要有以下三個方面的支援

  • 反射

  • 動態編譯(生成位元組碼)

  • 呼叫JavaScript引擎

http://blog.csdn.net/star_sky_red/article/details/54586145

https://www.ibm.com/developerworks/cn/java/coretech/java-dynamic.html

.NET平臺對動態程式設計的支援

https://msdn.microsoft.com/zh-cn/library/650ax5cx(v=vs.110).aspx

反射可被其他技術替代嗎?

答案是:既可以,又不可以。

說它可以,是技術的可行性上來說,技術上可以做到在反射的某些應用場景可以使用其他技術來達到和反射一樣的效果。哪些應用場景?在動態生成一個類、物件的時候,例如這篇文章中提到的,當反射成為了應用程式的效能瓶頸時,可以考慮使用程式碼生成來取代反射:https://www.ibm.com/developerworks/cn/java/j-dyn0610/

說它不可用,是說應用場景的不一樣,反射有很多的其他的應用場景,其作用是不可替代的。不僅僅是侷限在執行時建立和呼叫型別例項。在執行時,檢視類、物件、方法、屬性等資訊,也是反射所提供的一大好處。具體的應用:Java提供的註解機制,就利用了反射的這個功能,在執行時獲取類、物件的屬性和方法來完成相關的邏輯判斷和處理。

相關文章