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

山禾說發表於2020-11-13

1. 反射是什麼?

反射是一種機制,是一種能力,是指JVM在執行過程中,對於任意一個類,都可以知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫他的任意方法和屬性

2. 反射的原理?

如果你對 JVM 的類載入機制有所瞭解(這裡不瞭解也沒關係),就會知道,類在編譯的時候,會生成一個 .class 檔案,當類被使用的時候,這個 class 檔案就會被讀取並被載入到虛擬機器中,生成對應型別的 Class 物件,這個被建立出的 Class 物件中就包含了我們原本的類中一切資訊,通過這個物件,我們就可以去獲取這個類的屬性和方法,並且把這些屬性和方法轉換成一個物件,從而達到改變/執行的目的。

3. 反射的使用?

使用反射的第一步,是獲取一個 Class 物件,我們可以通過以下幾種方式去獲取一個 Class 物件

獲取 Class 物件

// 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 類來供我們使用

@Data
class Test {

    private String id;

    private String name;

    public String code;
}

這裡需要⚠️注意,我在這裡新建了兩個private的欄位和一個public 的欄位,是為了給大家展示,即使是反射,也無法獲取private的屬性~

// 獲取 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的欄位並沒有出現在我們列印的列表中。

拿到了方法,我們就可以用反射去執行方法

Method setName = test.getMethod("setName", String.class);
Test t = test.newInstance();
setName.invoke(t,"shanhe");
System.out.println(t.getName());

使用Methodinvoke方法,就可以去執行setName方法,這樣就可以達到給物件賦值的目的~

關於反射的使用方法,到這裡就告一段落,下面我們來看一下在專案中,我們如何使用反射去達到我們的目的,完成自動化的操作。

4. 反射的實際應用場景

在我們的實際業務開發中,我們很少用到反射去實現我們的業務功能,因為在業務執行的過程中,我們使用反射會使程式碼的執行效率變得很低,所以反射一般是應用在我們設計框架的時候去使用,我們平時使用到的一些框架和技術,其底層都是基於反射去實現的,簡而言之,反射是用來造輪子的,那麼反射造的輪子,我們日常專案開發中經常用到的有哪些呢?

說起 Spring 的特性,大多數人腦海中浮現的的下面這兩個名字,其實這兩個特性都是基於反射去實現的。

AOP

基於我們對 AOP 的瞭解,我們不難知道(如果不知道,請等待設計模式之代理模式的文章),AOP是基於動態代理去實現對方法的增強處理,那麼動態代理類一般都會去繼承InvocationHandler這個介面,然後通過重寫介面中的invoke方法,就可以實現AOP所要達到的目的。

IOC

IOC的實現,實際上就是通過工廠模式+反射來完成的,首先,我們通過註解或者xml的方式,將類(Bean)註冊到BeanFatory中,然後程式在執行的過程中,通過bean的全路徑名,使用反射去獲取一個物件例項

當然,這裡沒有列舉出來的還有很多很多,比如檔案的反編譯,比如java的agent機制,比如tomcat,比如動態生成類的框架,幾乎我們所能見到的框架中,都或多或少使用了反射這個神器來實現,反射的使用熟練程度,通常也是用來區分CRUD程式設計師和造輪子程式設計師的最大區別,也是進階路上必不可少的一道坎兒,掌握了反射,我們就可以有更多的思路去實現,去優化我們的程式碼~

學會使用反射+設計模式這是一個成長的標誌,所以,如果你有學到,請給我點贊+關注,這是給一個堅持原創的人最大的支援和鼓勵,另外設計模式系列部落格預熱中,敬請期待~

相關文章