1. 程式人生 > >不學無數——反射和內省的區別

不學無數——反射和內省的區別

反射和內省的區別

內省是基於反射實現的,主要用來操作JavaBean,通過內省可以很方便的動態獲得bean的set/get方法,屬性,方法名,他相當於是反射的工具類一樣

1. 反射

反射其實簡單來說就是通過類的名字獲得對於這個類的描述,這種描述包括方法、構造器、屬性的描述。舉個例子來說就是通過類名可以進行例項化物件、對類中的方法的呼叫、對類中屬性的賦值。在許多的框架中反射是經常被應用到的技術,下面舉個利用反射進行物件A的屬性賦值到物件B。

詳細的反射詳解,可以看不學無數——初識反射

其中A屬性如下

public class A {
    private String a;
    private String b;
	---------get.set方法
}

其中B屬性如下,B是繼承A的。所以B中也有A種a、b兩個變數

public class B extends A{
    private String c;
    private String d;
    ---------get.set方法
}

將A中a、b屬性的值賦值給B中的a、b兩個屬性

public class IntrospectorAndReflect {
    public static void main(String[] args) throws Exception {
        Class classA = Class.forName("Practice.Day05.A"); -- 獲得A的Class物件
        Class classB = Class.forName("Practice.Day05.B"); -- 獲得B的Class物件
        A a = (A) classA.newInstance(); -- 例項化A物件
        B b = (B) classB.newInstance(); -- 例項化B物件
        a.setA("a");
        a.setB("b");
        fatherToChild(a,b);
        System.out.println(b.getA());
    }
    public static <T>void fatherToChild(T father,T child) throws Exception {
        if (child.getClass().getSuperclass()!=father.getClass()){
            throw new Exception("child 不是 father 的子類");
        }
        Class<?> fatherClass = father.getClass(); --通過反射獲得Class物件
        Field[] declaredFields = fatherClass.getDeclaredFields(); --獲得此Class物件的屬性資訊
        for (int i = 0; i < declaredFields.length; i++) {
            Field field=declaredFields[i];
            //獲得屬性的get方法
            Method method=fatherClass.getDeclaredMethod("get"+upperHeadChar(field.getName()));
            Object obj = method.invoke(father);
            field.setAccessible(true);--解除方法的私有限定
            field.set(child,obj);--執行set方法
        }
    }
    /**
     * 首字母大寫,in:deleteDate,out:DeleteDate
     */
    public static String upperHeadChar(String in) {
        String head = in.substring(0, 1);
        String out = head.toUpperCase() + in.substring(1, in.length());
        return out;
    }
}

上面演示瞭如何通過類的全路徑名獲得類的Class物件,並且將Class物件進行例項化類的過程。其實這就是反射。其實有時候我們會想,直接new一個物件這麼簡單的事情,何必要用反射這麼麻煩呢?因為反射最大的應用就是動態載入。舉個簡單的例子如下,現在有A、B兩個類,根據執行的需要進行載入不同的類

if (條件1)
	載入A類
if (條件2)
	載入B類

執行時確定型別,繫結物件。動態編譯最大限度地發揮了Java的靈活性,體現了多型的應用,可以減低類之間的耦合性。

2. 內省(Introspector)

2.1 內省是什麼

內省是什麼?我們可以看關於java api文件中的介紹

The Introspector class provides a standard way for tools to learn about the properties, events, and methods supported by a target Java Bean.

For each of those three kinds of information, the Introspector will separately analyze the bean's class and superclasses looking for either explicit or implicit information and use that information to build a BeanInfo object that comprehensively describes the target bean.

簡單理解就是內省是對於java Bean的預設處理,既比如在一個實體類中,有nama、address屬性,那麼系統會預設在此類中會有get/set方法進行獲得和設定這兩個值的方法。並且通過上面的介紹可以得知,內省是通過BeanInfo類進行操作類中的屬性和方法的。例項如下:

public class IntrospectorDemo {

    public static void main(String[] args) throws IntrospectionException {
        BeanInfo beanInfo = Introspector.getBeanInfo(A.class);
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors();
        BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
        for (PropertyDescriptor x:propertyDescriptors){
            System.out.println(x.getName());
            System.out.println(x.getReadMethod());
        }
        System.out.println("-----------------------");
        for (MethodDescriptor y:methodDescriptors){
            System.out.println(y.getName());
        }
    }
}

可以想成內省是對於反射的更一層的封裝,讓我們更加容易的得到類的資訊並且操作它

2.2 內省相關類介紹

在內省中常用的類有四個

  • Introspector:將JavaBean中的屬性封裝起來進行操作。在程式把一個類當做JavaBean來看,就是呼叫Introspector.getBeanInfo()方法,得到的BeanInfo物件封裝了把這個類當做JavaBean看的結果資訊,即屬性的資訊
  • BeanInfo:將類中的資訊封裝到BeanInfo類中,獲得了BeanInfo物件就相當於獲得了類中的所有屬性資訊。呼叫getPropertyDescriptors()方法獲得屬性描述器,即獲得了所有的屬性資訊。呼叫
  • PropertyDescriptorPropertyDescriptor例項封裝了每個屬性特有的一些性質,比如呼叫getReadMethod()方法就能獲得這個屬性的get方法Method,呼叫getWriteMethod()方法就能獲得這個屬性的set方法Method。