1. 程式人生 > >【Java】反射 & 反射效能 & 反射操作泛型 & 反射操作註解

【Java】反射 & 反射效能 & 反射操作泛型 & 反射操作註解

反射機制

  • 執行時載入,探知,使用編譯期間完全未知的類。
  • 程式在執行狀態中,可以動態載入一個只有名稱的類,對於任意一個已載入的類,都能夠知道這個類的所有屬性和方法,對於任意一個物件,都能夠呼叫他的任意一個方法和屬性。
  • jvm載入完類之後,在堆記憶體中,就產生了一個對應的Class型別的物件,這個物件就包含了完整的類的結構資訊。Class(大寫c)是一個類。
  • 一個類只有一個Class反射物件。

反射的使用

首先定義一個類User作為例子

​
package Reflection;

public class User {

    public int id;
    private int age;
    String name;

    public User(){

    }

    public User(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

有三種方式來獲得某一個類的Class物件

/方式1
Class<?> demo1 = Class.forName(path);
//方式2
Class demo2 = Reflection.User.class;
//方式3
Class demo3 = path.getClass();

獲取類的名字 

demo1.getName();//獲得類名+包名
demo1.getSimpleName();//獲得類名

獲得屬性資訊 

Field[] fields1 = demo1.getFields();//只能獲得public的屬性
Field[] fields2 = demo1.getDeclaredFields();//獲得所有屬性
Field field3 = demo1.getDeclaredField("name");//獲取指定屬性

 獲取方法資訊同屬性,把Field換為Method
如果方法有引數,必須傳入引數型別對應的Class物件

Method method = demo1.getDeclaredMethod("setAge", int.class);

通過反射API呼叫構造方法,構造物件 

User u1 = (User)demo1.newInstance();//其實是呼叫了User的無參構造
Constructor constructor = demo1.getDeclaredConstructor(int.class,int.class,String.class);
User u2 = (User) constructor.newInstance(001,18,"yst");//利用有參構造,利用newInstance傳參

通過反射API呼叫普通方法,好處是可以動態的呼叫方法,例如從其他地方傳過來再放到引數中 

User u3 = (User)demo1.newInstance();
Method method2 = demo1.getDeclaredMethod("setName", String.class);
method2.invoke(u3,"yst222");

通過反射API操作屬性
因為定義age屬性為private,所以set不能訪問,要用setAccessible取消安全檢查 

User u4 = (User)demo1.newInstance();
Field f = demo1.getDeclaredField("age");
f.setAccessible(true);
f.set(u4,22);

反射效能

經過測試,使用反射呼叫方法所用時間遠大於直接呼叫方法,所以當需要頻繁呼叫反射的時候可以取消安全檢查,這樣使效率提高4倍。

Class.setAccessible(true); 這樣就可以取消安全檢查。

反射操作泛型

package Reflection;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class ReflectionGenerics {

    public void test1(Map<String,Reflection.User> map, List<Reflection.User> list){
        System.out.println("test1");
    }

    public Map<Integer,Reflection.User>test2(){
        System.out.println("test2");
        return null;
    }

    public static void main(String[] args) {
        try {
            //獲得指定方法引數泛型資訊
            Method m = ReflectionGenerics.class.getMethod("test1",Map.class,List.class);
            //獲得泛型引數型別
            Type[] t = m.getGenericParameterTypes();
            for (Type x: t) {
                System.out.println("#"+x);
                //如果是一個引數型別
                if(x instanceof ParameterizedType){
                    //強制型別轉換
                    Type[] genericTypes = ((ParameterizedType)x).getActualTypeArguments();
                    for(Type y : genericTypes){
                        System.out.println("泛型型別:"+y);
                    }
                }
            }

            //獲得指定方法返回值泛型資訊
            Method m2 = ReflectionGenerics.class.getMethod("test02", null);
            Type returnType = m2.getGenericReturnType();
            if(returnType instanceof ParameterizedType){
                Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments();

                for (Type genericType : genericTypes) {
                    System.out.println("返回值,泛型型別:"+genericType);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

反射操作註解