天天看點

JAVA反射詳解(JAVA Reflection)

JAVA Reflection 定義

  • 反射是Java被視為動态語言的關鍵,反射機制允許程式在執行過程中借助于Reflection API取得任何類的内部資訊,并能直接操作任意對象的内部屬性及方法。
  • 類加載完成後,在堆記憶體的方法區中就産生了一個Class類型的對象(一個類隻有一個Class對象),這個對象就包含了完整的類的結構資訊。我們可以通過這個對象看到類的機構。這個對象就像一面鏡子,透過這個鏡子能看到類的結構,是以,被形象的稱為**“反射”** 。
    • 優點:可以實作動态建立對象和編譯,展現出很大的靈活性;
    • 缺點:對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼并且它滿足我們的要求。這類操作總是慢于直接執行相同的操作。

Class類

  • 定義:

    對象照鏡子後可以得到的資訊:某個類的屬性、方法和構造器、某個類到底實作了哪些接口。對于每個類而言,JRE都為其保留了一個不變的Class類型的對象。一個Class對象包含了特定某個結構的有關資訊。

  • 特點:
    • Class本身也是一個類,繼承自Object
    • Class對象隻能由系統建立
    • 一個加載的類在JVM中隻會有一個Class執行個體
    • 一個Class執行個體對應的是一個加載到JVM中的一個.class檔案
    • 每個類的執行個體都會記得自己是由哪個Class執行個體所生成
    • 通過Class可以完整地得到一個類中的所有被加載的結構
    • Class類是Reflection的根源,針對任何你想動态加載、運作的類,唯有先獲得Class對象
  • Class類的執行個體的擷取方式
    • 若已經知道具體的類,可以通過類的class屬性擷取,該方法最為安全可靠,性能最高
    • 若已經知道某個類的執行個體,調用該執行個體的getClass()方法,可以擷取到Class對象
    • 若已經知道某個類的全類名,且該類在類路徑下,可以通過Class類的靜态方法forName()進行擷取,此方法可能抛出ClassNotFoundException
    • 内置的基本資料類型,可以直接用類名.Type
    • 還可以利用ClassLoader來進行擷取
  • 可以有class的類型:
    @Test
    public void test02() {
        Class<Object> c1 = Object.class;//類
        Class<Comparable> c2 = Comparable.class;//接口
        Class<String[]> c3 = String[].class;//一維數組
        Class<int[][]> c4 = int[][].class;//二維數組
        Class<Override> c5 = Override.class;//注解
        Class<ElementType> c6 = ElementType.class;//枚舉
        Class<Integer> c7 = Integer.class;//基本資料類型
        Class<Void> c8 = void.class;//void
        Class<Class> c9 = Class.class;//Class類
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
    }
               
    class java.lang.Object
    interface java.lang.Comparable
    class [Ljava.lang.String;
    class [[I
    interface java.lang.Override
    class java.lang.annotation.ElementType
    class java.lang.Integer
    void
    class java.lang.Class
               

類的加載與ClassLoader

  • 加載:将class位元組碼檔案中内容加載到記憶體中,并将這些靜态資料轉換為方法區中的運作時資料結構,然後生成一個代表這個類的java.lang.Class對象
  • 連結:将Java類的二進制代碼合并到JVM的運作狀态之中的過程
    • 驗證:確定加載的類資訊符合JVM規範,沒有安全性方面的問題
    • 準備:正式為類變量(static)配置設定記憶體并設定類變量預設初始值的階段,這些記憶體都在方法區中進行配置設定
    • 解析:虛拟機常量池内的符号引用(常量名)替換為直接引用(位址)的過程
  • 初始化
    • 執行類構造器()方法的過程。類構造器方法是由編譯器自動收集類中所有類變量的指派動作和靜态代碼塊中的語句合并産生的。(類構造器是構造類資訊的,不是構造該類對象的構造器)
    • 當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化
    • 虛拟機會保證一個類的 ()方法在多線程環境中被正确加鎖和同步
  • 類加載器的作用:用來把類(class)裝載進記憶體的。JVM規範定義了如下類型的ClassLoader:
    • 引導類加載器:用C++編寫,是JVM自帶的類加載器,負責Java平台核心庫,用來裝載核心類庫。該ClassLoader無法直接擷取
    • 擴充類加載器:負責jre/lib/ext 目錄下的jar包或者-D java.ext.dirs指定的目錄下的jar包裝入工作庫
    • 系統類加載器:負責java -classpath或者-D java.class.path所指的目錄下的類與jar包裝如工作庫,是最常用的ClassLoader

Refection的用法:

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Person {

    public String name ;
    private Integer age ;
    private Integer level ;

}

@AllArgsConstructor
@NoArgsConstructor
@Data
public class MiniPerson extends Person {
    public String email ;
    private Integer num ;
}
           
@Test
public void test3() {
    Class c1 = MiniPerson.class ;
    //擷取本類及父類中聲明的所有的pulbic的field
    Field[] fields = c1.getFields();
    for (Field field : fields) {
        System.out.println(field);
    }
    System.out.println("------------------------------------------------------");
    //擷取本類中聲明的所有的field
    fields = c1.getDeclaredFields();
    for (Field field : fields) {
        System.out.println(field);
    }
    System.out.println("------------------------------------------------------");
    //擷取本類及父類中所有聲明的public的method
    Method[] methods = c1.getMethods();
    for (Method method : methods) {
        System.out.println(method);
    }
    System.out.println("------------------------------------------------------");
    //擷取本類中所有聲明的method
    methods = c1.getDeclaredMethods();
    for (Method method : methods) {
        System.out.println(method);
    }
    System.out.println("------------------------------------------------------");
    //擷取本類中所有public的constructor,無法擷取到父類的constructor
    Constructor[] constructors = c1.getConstructors();
    for (Constructor constructor : constructors) {
        System.out.println(constructor);
    }
    System.out.println("------------------------------------------------------");
    //擷取本類中所有的constructor
    constructors = c1.getDeclaredConstructors();
    for (Constructor constructor : constructors) {
        System.out.println(constructor);
    }
}
           
public java.lang.String com.huwc.test.reflect.MiniPerson.email
public java.lang.String com.huwc.test.reflect.Person.name
------------------------------------------------------
public java.lang.String com.huwc.test.reflect.MiniPerson.email
private java.lang.Integer com.huwc.test.reflect.MiniPerson.num
------------------------------------------------------
public boolean com.huwc.test.reflect.MiniPerson.equals(java.lang.Object)
public java.lang.String com.huwc.test.reflect.MiniPerson.toString()
public int com.huwc.test.reflect.MiniPerson.hashCode()
public void com.huwc.test.reflect.MiniPerson.setEmail(java.lang.String)
public void com.huwc.test.reflect.MiniPerson.setNum(java.lang.Integer)
public java.lang.String com.huwc.test.reflect.MiniPerson.getEmail()
public java.lang.Integer com.huwc.test.reflect.MiniPerson.getNum()
public java.lang.String com.huwc.test.reflect.Person.getName()
public void com.huwc.test.reflect.Person.setName(java.lang.String)
public java.lang.Integer com.huwc.test.reflect.Person.getLevel()
public java.lang.Integer com.huwc.test.reflect.Person.getAge()
public void com.huwc.test.reflect.Person.setLevel(java.lang.Integer)
public void com.huwc.test.reflect.Person.setAge(java.lang.Integer)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
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 native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
------------------------------------------------------
public boolean com.huwc.test.reflect.MiniPerson.equals(java.lang.Object)
public java.lang.String com.huwc.test.reflect.MiniPerson.toString()
public int com.huwc.test.reflect.MiniPerson.hashCode()
public void com.huwc.test.reflect.MiniPerson.setEmail(java.lang.String)
public void com.huwc.test.reflect.MiniPerson.setNum(java.lang.Integer)
public java.lang.String com.huwc.test.reflect.MiniPerson.getEmail()
protected boolean com.huwc.test.reflect.MiniPerson.canEqual(java.lang.Object)
public java.lang.Integer com.huwc.test.reflect.MiniPerson.getNum()
------------------------------------------------------
public com.huwc.test.reflect.MiniPerson(java.lang.String,java.lang.Integer)
public com.huwc.test.reflect.MiniPerson()
------------------------------------------------------
public com.huwc.test.reflect.MiniPerson(java.lang.String,java.lang.Integer)
public com.huwc.test.reflect.MiniPerson()

           

反射中擷取泛型

@AllArgsConstructor
@NoArgsConstructor
@Data
public class MiniPerson extends Person {
    public String email ;
    private Integer num ;


    public List<String> testGeneric(Map miniMap, Map<String, Object> map, List<String> list){
        return list ;
    }
}


@Test
public void test4() throws Exception {
    Class c1 = MiniPerson.class;
    Method method = c1.getDeclaredMethod("testGeneric", Map.class, Map.class, List.class);

    //擷取所有的參數類型
    Class<?>[] parameterTypes = method.getParameterTypes();
    for (Class<?> parameterType : parameterTypes) {
        System.out.println(parameterType);
    }
    System.out.println("--------------------------------------------------------------");
    //擷取所有的參數類型,如果有泛型,顯示帶泛型的參數類型
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    for (Type genericParameterType : genericParameterTypes) {
        System.out.println(genericParameterType);
        System.out.println("-----------------------------------------------------------");
        //如果是泛型化類型,擷取泛型中的實際類型
        if(genericParameterType instanceof ParameterizedType){
            ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument.getClass());
            }
            System.out.println("-----------------------------------------------------------");
        }
    }
}
           
interface java.util.Map
interface java.util.Map
interface java.util.List
--------------------------------------------------------------
interface java.util.Map
-----------------------------------------------------------
java.util.Map<java.lang.String, java.lang.Object>
-----------------------------------------------------------
class java.lang.Class
class java.lang.Class
-----------------------------------------------------------
java.util.List<java.lang.String>
-----------------------------------------------------------
class java.lang.Class
-----------------------------------------------------------

           

反射與注解的結合

@HuwcTable("hwc_users")
public class User {
    @HuwcField(colName = "id", type = "int", length = 10)
    private int id ;
    @HuwcField(colName = "name", type = "int", length = 30)
    private String name ;
    @HuwcField(colName = "password", type = "int", length = 20)
    private String password ;

    public static void main(String[] args) throws NoSuchFieldException {
        Class c1 = User.class;
        //擷取類注解
        HuwcTable huwcTable = (HuwcTable) c1.getAnnotation(HuwcTable.class);
        System.out.println(huwcTable.value());

        Field idField = c1.getDeclaredField("id");
        Field nameField = c1.getDeclaredField("name");
        Field passwordField = c1.getDeclaredField("password");

        HuwcField anno1 = idField.getAnnotation(HuwcField.class);
        System.out.println("colName : " + anno1.colName());
        System.out.println("type : " + anno1.type());
        System.out.println("length : " + anno1.length());
        System.out.println("---------------------------------------------");
        HuwcField anno2 = nameField.getAnnotation(HuwcField.class);
        System.out.println("colName : " + anno2.colName());
        System.out.println("type : " + anno2.type());
        System.out.println("length : " + anno2.length());
        System.out.println("---------------------------------------------");
        HuwcField anno3 = passwordField.getAnnotation(HuwcField.class);
        System.out.println("colName : " + anno3.colName());
        System.out.println("type : " + anno3.type());
        System.out.println("length : " + anno3.length());
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface HuwcTable {
    String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface HuwcField {
    String colName();
    int length();
    String type();
}
           
hwc_users
colName : id
type : int
length : 10
---------------------------------------------
colName : name
type : int
length : 30
---------------------------------------------
colName : password
type : int
length : 20
           

反射性能相關

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Person {

    public String name ;
    private Integer age ;
    private Integer level ;

}

@Test
public void test5() throws NoSuchFieldException, IllegalAccessException {
    Person person = new Person();
    Class c1 = person.getClass();
    Field nameField = c1.getField("name");

    long cnt = 1000000000;
    long start = System.currentTimeMillis();
    for (long i = 0; i < cnt; i++) {
        person.getName();
    }
    long end = System.currentTimeMillis();
    System.out.println("不使用反射的時間:" + (end - start));

    start = System.currentTimeMillis();
    for (long i = 0; i < cnt; i++) {
        nameField.get(person);
    }
    end = System.currentTimeMillis();
    System.out.println("使用反射的時間:" + (end - start));


    start = System.currentTimeMillis();
    nameField.setAccessible(true);
    for (long i = 0; i < cnt; i++) {
        nameField.get(person);
    }
    end = System.currentTimeMillis();
    System.out.println("使用反射并且解除安全校驗的時間:" + (end - start));
}

           
不使用反射的時間:899
使用反射的時間:8587
使用反射并且解除安全校驗的時間:6598