四大神器之反射

Java反射機制,是一個基礎的內容章節,在對於之後的框架學習中起到了至關重要的作用,現在比較流行的是spring

框架 ,其中的IOC(自動注入)以及AOP(動態代理),在AOC中代理模式又分爲動態代理和byteCode

instrument(插樁)或者是CGLIB 。

在學習Java反射機制前,首先問一個問題:在Java運行時,對於任意一個類,能否知道這個類有那些屬性和方法?對

 

於任意一個對象,能否調用它任意一個方法?

 

答案是肯定的。可以!

 

在這裏要去區別一個事情:如果說在自己寫的類中去改一個數據類型或者說屬性,那只是在編譯時,並不是在運行

 

時。

 

反射機制的幾大功能:

 

  • 在運行時,判斷任意一個對象所屬類。
  • 在運行時,構造任意一個對象。
  • 在運行時,判斷任意一個類所具有的成員變量和方法
  • 在運行時,調用任意一個對象的方法。

 

一般而言:程序運行時,允許改變程序結構和變量類型成爲動態語言。由此可見Java並不是動態語言,但是Java有一

 

個動態相關的機制Reflection,可以運行時加載、探知、使用編譯期間完全未知的classes(但是methods定義不知

 

道)。所以Reflection是Java視爲動態語言的關鍵性質,允許在程序運行時通過Reflection APIs 取得任何一個已知名

 

稱的class的內部信息。(包括父類,接口,變量,方法等)。

 

接下來我們用代碼來舉一個例子。

 

public class DumpMethods {
    //從命令行接受一個字符串(該字符串是某個類的全名)
    //打印該類的所有方法

    public static void main(String[] args) throws ClassNotFoundException {
        //class類是所有反射的入口
        Class<?> classType = Class.forName(args[0]);//編譯時不知道args是什麼?

        Method[] methods = classType.getDeclaredMethods();

        for (Method method : methods) {
            System.out.println(method);
        }

    }
}

 

 

 

在這裏,我們可以看到,Java虛擬機並不知道你所要傳入的參數是什麼,這個時候我們類的全類型名

 

稱 Java.lang.String輸入進去,我們可以看到如下圖片

 

結果.jpg

 

其中,標紅的那一行可以看到,直接將私有的方法也可顯示出來。這一個是非常有用的。那麼這些代碼就可以很明顯

 

的讓我們知道,在Java運行時,就可以判斷任意一個對象所屬類。

 

接下來,我們來看一下反射的相關代碼:

 

public class InvokeTester {
    public int add(int param1, int param2) {
        return param1 + param2;
    }

    public String echo(String msg) {
        return msg + "hello";
    }

    public static void main(String[] args) throws Exception {
        Class<?> classType = InvokeTester.class; //獲取類對象

        Object invokeTester = classType.newInstance();//創建此 Class 對象所表示的類的一個新實例。
        //調用前提是 必須有一個不帶參數的構造器

        //以上兩行代碼就相當於new invokeTester

        Method addmethod = classType.getMethod("add", int.class, int.class);//確定這個方法需要輸入 方法名 參數

        Object result = addmethod.invoke(invokeTester, new Object[]{100, 200});
        //以上兩行相當於i.add(100,200)
        System.out.println((Integer) result);

        Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
        Object result2 = echoMethod.invoke(invokeTester, "hello world");
        System.out.println((String) result2);
    }
}

 

 

在這裏,着重說獲取類對象時,創建的class對象新的實例時,必須有一個不帶參數的構造器,否則會報錯,那麼怎

 

沒有應該怎麼辦呢?下面會講到。確認一個方法,我們通過重載機制就可以知道,需要通過這個方法的名字和他的參

 

數來確認。上一個我們通過classType.getDeclareMethods()來獲取了所有方法,而現在我們可以通過

 

classType.getMethods()方法來獲得這個我們需要已知方法的名字,在這裏參數類型是int.class,String.class.在之

 

後,我們通過revoke方法,來獲得我們調用方法的結果,invoke方法中的兩個參數,第一個參數是我們需要調用的對

 

象,第二個參數就是調用方法的參數,這個參數應該與getmethod方法的參數所寫的類型一致,下面我們來看一下結

 

果:

Snipaste_2020-01-10_11-55-11.jpg

 

我們通過這個例子,就可以發現,通過反射機制我們完全可以調用一個方法。

 

接下來,整一個複雜點的!

 

public class ReflectTester {


    public Object copy(Object object) throws Exception {
        Class<?> classType = object.getClass();

        System.out.println(classType.getName());

        Object objectCopy = classType.getConstructor(new Class[]{})
                .newInstance(new Object[]{});
        //相當於是 object object2 = classType.newInstance();

        //獲得對象的所有屬性
        Field[] fields = classType.getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];

            String fieldName = field.getName();

            //獲得屬性的首字母並轉換爲大寫
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            //獲得和屬性對應的getxxx()方法
            String getMethodName = "get" + firstLetter + fieldName.substring(1);
            //獲得個屬性對應的setxxx()方法
            String setMethodName = "set" + firstLetter + fieldName.substring(1);

            //獲得get方法
            Method getMethod = classType.getMethod(getMethodName, new Class[]{});

            //獲得set方法
            Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()});


            Object value = getMethod.invoke(object, new Object[]{});

            System.out.println(fieldName + ":" + value);

            setMethod.invoke(objectCopy, new Object[]{value});
        }
        return objectCopy;
    }

    public static void main(String[] args) throws Exception {

        Customer customer = new Customer();

        customer.setId(new Long(1));
        customer.setName("zhangsan");
        customer.setAge(20);

        Customer customerCopy = (Customer) new ReflectTester().copy(customer);

        System.out.println(customerCopy.getId() + "," + customerCopy.getName() + "," + customerCopy.getAge());
    }

}


class Customer {
    private long id;

    private String name;

    private int age;

    public Customer() {
    }

    public long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

 

 

以上代碼創造了一個Customer類,並通過反射進行了一次copy,調用getter,setter方法,成功。此處一定要在

 

Customer類中寫入構造器,否則容易造成權限不通過導致沒有辦法讀取。我們可以看到  Object object copy =

 

classType.getConstructor(new Class[]{}) .newInstance(new Object[]{});這一行代碼就相當於是object object2 =

 

classType.newInstance();如果沒有無參構造器時,則可運用這個方法

Snipaste_2020-01-10_11-59-09.jpg

 

 

 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章