Java開發——反射機制

前言:

在面向對象的世界裏,萬事萬物皆對象。但是在java語言中,靜態的成員、普通數據類型除外,靜態成員屬於類,而不是對象,而普通數據類型有對應的包裝類來彌補它。類是不是對象呢?類是(哪個類的對象呢?)誰的對象呢?類是對象,類是java.lang.Class類的實例對象

一、反射的概念:

主要是指程序可以訪問,檢測和修改它本身狀態或行爲的一種能力,並能根據自身行爲的狀態和結果,調整或修改應用所描述行爲的狀態和相關的語義。
反射是java中一種強大的工具,能夠使我們很方便的創建靈活的代碼,這些代碼可以再運行時裝配,無需在組件之間進行源代碼鏈接。但是反射使用不當會成本很高!
只看概念會幾乎毫無感覺,通俗的講可以這樣理解:
反射的過程認爲是將.class文件(也就是字節碼文件)反編譯爲.java文件(Java源文件)。然後利用反射機制來得到類的屬性、方法、構造方法。

二、Class類是什麼:

一下通過代碼來演示:

        Test test = new Test();
        //官網說,以下三種方式c1 ,c2 ,c3表示了Test類的類類型(class type)
       //任何一個類都是Class的一個實例對象,Class就是類類型
        // 這個實例對象有三種表示方式:

        //第一種表示方式:這是實際在告訴我們,任何一個類都有一個隱含的靜態成員變量class;
        Class c1 =  Test.class;
        //第二種表示方式:已經類的實例,通過類的實例來獲取Class的實例對象
        Class c2 = test.getClass();

        //第三種表示方式:
        try {
            Class c3 = Class.forName("Test");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //結果是true,表明不管使用哪一種方式,一個類只能是Class類的一個實例對象
        System.out.println(c1==c2);//true
        System.out.println(c1==c2);//true

        //我們可以通過類類型,創建該類的實例對象
        //即我們可以通過c1,c2,c3,來創建Test類的實例對象
        try {
            Test test1 = (Test)c1.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

三、java 動態加載類

上面提到的第三種方法,Class.forName(“類的全稱”),不僅表示了類的類類型,還代表了動態加載類。
我們一定要區分編譯、運行,編譯時刻加載類是靜態加載類、運行時刻加載類是動態加載類。
下面演示一個Java動態加載類的簡單實例:
我們創建一個接口Subject,表示考試科目,並創建兩個科目Mathmatic和Physics來實現Subject接口。

package com.tong.reflect;

public interface Subject {
    void printGrade();//打印該科目成績
}
package com.tong.reflect;

public class Mathmatic implements Subject {
    @Override
    public void printGrade() {
        System.out.println("打印數學成績");
    }
}
package com.tong.reflect;

public class physics implements Subject {
    @Override
    public void printGrade() {
        System.out.println("打印物理成績");
    }
}

然後就可以利用Java反射機制來動態加載類,並創建類的實例。在後續過程中如果還需要增加科目,只要實現Subject接口,並編譯爲字節碼文件。一下爲測試代碼:

 public static void main(String[] args) {
        try {
            //動態加載類
            Class c1 = Class.forName(args[0]);
            //動態創建類的實例
            Subject subject = (Subject) c1.newInstance();
            subject.printGrade();//打印成績
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

四、通過Java反射獲取類的成員變量、方法、構造函數

定義類ClassUtils用來獲取類的方法:

public class ClassUtils {
    /*
    獲取類的方法
     */
    public void printMethod(Object obj){
        Class cl = obj.getClass();
        System.out.println("打印類的名稱:" + cl.getName());
        /*
        getMethods()獲得的是類的所有的public方法,包括從父類繼承來的方法
        getDeclaredMethods()獲取的是自己聲明的方法,不問訪問權限
         */
        Method[] method = cl.getMethods();
        for(Method m:method){
            Class returnType = m.getReturnType();
            //打印返回值類型
            System.out.print(returnType.getName() + " ");
            //打印方法名
            System.out.print(m.getName() + "(");
            Class[] parameterType = m.getParameterTypes();
            for(int i = 0;i<parameterType.length;i++){
                if(i==parameterType.length-1)
                    System.out.print(parameterType[i].getName());
                else
                    System.out.print(parameterType[i].getName() + ",");
            }
            System.out.println(");");
        }
    }
    /*
    獲取類的成員變量
     */
    public void printField(Object obj){

        Class cl = obj.getClass();
        System.out.println("獲取類" + cl.getName() +  "的成員變量:");
        Field[] fields = cl.getFields();
        for(Field f:fields){
            Class type = f.getType();
            System.out.println(type.getName() + " " + f.getName());
        }
    }
    /*
    獲取類的構造方法
     */
    public void printConstructor(Object obj){

        Class cl = obj.getClass();
        System.out.println("獲取類" + cl.getName() +  "的構造方法:");
        Constructor[] constructors  = cl.getDeclaredConstructors();
        for(Constructor con:constructors){
            System.out.print(con.getName() + "(");
            Class[] parametertype = con.getParameterTypes();
            for(Class para:parametertype){
                System.out.print(para.getName() + ",");
            }
            System.out.println(")");
        }
    }

測試:

 public static void main(String[] args) {
        ClassUtils classUtils = new ClassUtils();
        try {
            //打印類中的方法
            classUtils.printMethod(new Integer(1));
            classUtils.printField(new Integer(1));
            classUtils.printConstructor(new Integer(1));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

運行結果:

打印類的名稱:java.lang.Integer
int numberOfLeadingZeros(int);
int numberOfTrailingZeros(int);
int bitCount(int);
boolean equals(java.lang.Object);
。。。省略。。。。
獲取類java.lang.Integer的成員變量:
int MIN_VALUE
int MAX_VALUE
java.lang.Class TYPE
int SIZE
int BYTES
獲取類java.lang.Integer的構造方法:
java.lang.Integer(int,)
java.lang.Integer(java.lang.String,)

五、方法的反射

即通過反射來運行類中的方法。
方法反射的操作
method.invoke(對象,參數列表)

public class A {
    //調用無參數的print
    public void print(){
        System.out.println("輸入參數爲空。。。。。");
    }
    //調用輸入爲整形的print
    public void print(int a,int b){
        System.out.println("輸入參數爲:" + a + "  " + b);
    }
    //調用輸入參數爲字符串的print
    public void print(String a,String b){
        System.out.println("輸入參數爲:" + a + "  " + b);
    }
}

測試代碼:

   public static void main(String[] args) {
        A a = new A();
        Class cl = a.getClass();
        Method m = null;
        Object obj = null;
        try {
            //調用無參數的print
            m = cl.getMethod("print",new Class[]{});
            obj = m.invoke(a,new Object[]{});
            //調用輸入爲整形的print
            m = cl.getMethod("print",new Class[]{int.class,int.class});
            obj = m.invoke(a,new Object[]{1,2});
            //調用輸入參數爲字符串的print
            m = cl.getMethod("print",new Class[]{String.class,String.class});
            obj = m.invoke(a,new Object[]{"Hello","World"});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

六、通過反射來認識泛型的本質

Java泛型是JDK 5引入的一個特性,它允許我們定義類和接口的時候使用參數類型,泛型在集合框架中被廣泛使用。Java中泛型是防止錯誤輸入的,永遠記住,泛型是一個編譯時的概念,只在編譯階段有效,繞過編譯泛型就無效了,來看下面的代碼。

若是往list1中添加String類型元素,就會編譯報錯,如下代碼:

     ArrayList<Integer> list1 = new ArrayList<Integer>();
     list1.add(100);
     list1.add(200);
     //此時如果往list1中添加String類型,就會報錯,編譯不通過
     //list1.add("Hello");
     ArrayList<String> list2 = new ArrayList<String>();
     list2.add("AAAA");
     list2.add("BBBB");
     list2.add("CCCC");

但是執行下語句時,發現list1和list2竟然指向同一個類類型。說明一個類只對一個類類型。

//輸出爲true
System.out.println(list1.getClass()==list2.getClass());

當通過反射機制來運行方法,就可以往聲明爲存放Integer類型的ArrayList中存放String類型數據。即繞過了編譯就繞過了泛型

ArrayList<Integer> list1 = new ArrayList<Integer>();
        list1.add(100);
        list1.add(200);
        //此時如果往list1中添加String類型,就會報錯,編譯不通過
        //list1.add("Hello");
        ArrayList<String> list2 = new ArrayList<String>();
        list2.add("AAAA");
        list2.add("BBBB");
        list2.add("CCCC");
        Class cl = list1.getClass();
        try {
            Method m = cl.getMethod("add",new Class[]{Object.class});
            Object obj = m.invoke(list1,new Object[]{"Hello"});
            System.out.println("list1的大小:"+list1.size());
            System.out.println("list1的內容:"+list1);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

運行結果:

list1的大小:3
list1的內容:[100, 200, Hello]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章