1. 程式人生 > >面試官:五年經驗,我不問問反射說不過去吧?

面試官:五年經驗,我不問問反射說不過去吧?

### 1. 反射是什麼? 反射是一種機制,是一種能力,是指**JVM**在執行過程中,對於任意一個類,都可以知道這個類的**所有屬性和方法**;對於任意一個物件,都能夠**呼叫他的任意方法和屬性**。 ### 2. 反射的原理? 如果你對 JVM 的類載入機制有所瞭解(這裡不瞭解也沒關係),就會知道,類在編譯的時候,會生成一個 .class 檔案,當類被使用的時候,這個 class 檔案就會被讀取並被載入到虛擬機器中,生成對應型別的 Class 物件,這個被創建出的 Class 物件中就包含了我們原本的類中一切資訊,通過這個物件,我們就可以去獲取這個類的屬性和方法,並且把這些屬性和方法轉換成一個物件,從而達到改變/執行的目的。 ![](https://viyoungblog.oss-cn-beijing.aliyuncs.com/blog/2020-11-11-100959.png) ### 3. 反射的使用? 使用反射的第一步,是獲取一個 Class 物件,我們可以通過以下幾種方式去獲取一個 Class 物件 #### 獲取 Class 物件 ```java // 1. 根據類的全限定名 Class str1 = Class.forName("java.lang.String"); // 2. .class獲取 Class str2 = String.class; // 3. 繼承自 Object 類的 getClass 方法 String s = new String(); Class str3 = s.getClass(); ``` 拿到了 Class 物件,下面我就可以對這個 Class 物件進行進一步的剖析,獲取它的構造方法,欄位和一般方法 #### 獲取Constructor,Method,Filed 在獲取之前,我們首先來新建個一個 `Test` 類來供我們使用 ```java @Data class Test { private String id; private String name; public String code; } ``` 這裡需要⚠️注意,我在這裡新建了兩個`private`的欄位和一個`public` 的欄位,是為了給大家展示,即使是反射,也無法獲取`private`的屬性~ ```java // 獲取 constructor Constructor[] constructors = test.getConstructors(); Arrays.stream(constructors).forEach(System.out::println); System.out.println("------------------"); // 獲取 filed Field[] fields = test.getFields(); Arrays.stream(fields).forEach(System.out::println); System.out.println("------------------"); // 獲取 method Method[] methods = test.getMethods(); Arrays.stream(methods).forEach(System.out::println); ``` 列印的結果如下: ``` public show.shanhe.interview.reflection.Test() ------------------ public java.lang.String show.shanhe.interview.reflection.Test.code ------------------ public boolean show.shanhe.interview.reflection.Test.equals(java.lang.Object) public java.lang.String show.shanhe.interview.reflection.Test.toString() public int show.shanhe.interview.reflection.Test.hashCode() public java.lang.String show.shanhe.interview.reflection.Test.getName() public void show.shanhe.interview.reflection.Test.setName(java.lang.String) public java.lang.String show.shanhe.interview.reflection.Test.getId() public java.lang.String show.shanhe.interview.reflection.Test.getCode() public void show.shanhe.interview.reflection.Test.setCode(java.lang.String) public void show.shanhe.interview.reflection.Test.setId(java.lang.String) public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public final void java.lang.Object.wait() throws java.lang.InterruptedException public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll() ``` 在`Filed`的列印中,我們可以看到只有`code`欄位被列印了出來,其餘兩個`private`的欄位並沒有出現在我們列印的列表中。 拿到了方法,我們就可以用反射去執行方法 ```java Method setName = test.getMethod("setName", String.class); Test t = test.newInstance(); setName.invoke(t,"shanhe"); System.out.println(t.getName()); ``` 使用`Method`的`invoke`方法,就可以去執行`setName`方法,這樣就可以達到給物件賦值的目的~ 關於反射的使用方法,到這裡就告一段落,下面我們來看一下在專案中,我們如何使用反射去達到我們的目的,完成自動化的操作。 ### 4. 反射的實際應用場景 在我們的實際業務開發中,我們很少用到反射去實現我們的業務功能,因為在業務執行的過程中,我們使用反射會使程式碼的執行效率變得很低,所以反射一般是應用在我們設計框架的時候去使用,我們平時使用到的一些框架和技術,其底層都是基於反射去實現的,簡而言之,反射是用來**造輪子**的,那麼反射造的輪子,我們日常專案開發中經常用到的有哪些呢? 說起 Spring 的特性,大多數人腦海中浮現的的下面這兩個名字,其實這兩個特性都是基於反射去實現的。 #### AOP 基於我們對 AOP 的瞭解,我們不難知道(如果不知道,請等待設計模式之代理模式的文章),AOP是基於動態代理去實現對方法的增強處理,那麼動態代理類一般都會去繼承`InvocationHandler`這個介面,然後通過重寫介面中的`invoke`方法,就可以實現AOP所要達到的目的。 ![](https://viyoungblog.oss-cn-beijing.aliyuncs.com/blog/2020-11-12-043551.png) #### IOC IOC的實現,實際上就是通過工廠模式+反射來完成的,首先,我們通過註解或者xml的方式,將類(Bean)註冊到BeanFatory中,然後程式在執行的過程中,通過bean的全路徑名,使用反射去獲取一個物件例項 ![](https://viyoungblog.oss-cn-beijing.aliyuncs.com/blog/2020-11-12-043834.png) 當然,這裡沒有列舉出來的還有很多很多,比如**檔案的反編譯**,比如**java的agent機制**,比如**tomcat**,比如**動態生成類的框架**,幾乎我們所能見到的框架中,都或多或少使用了反射這個神器來實現,反射的使用熟練程度,通常也是用來區分CRUD程式設計師和造輪子程式設計師的最大區別,也是進階路上必不可少的一道坎兒,掌握了反射,我們就可以有更多的思路去實現,去優化我們的程式碼~ **學會使用反射+設計模式**這是一個成長的標誌,所以,如果你有學到,請給我**點贊+關注**,這是給一個**堅持原創**的人最大的支援和鼓勵,另外設計模式系列部落格預熱中,敬請