1. 程式人生 > >Java--反射的逐步理解

Java--反射的逐步理解

層層引入反射的作用

一.類型別的概念:所有類都是物件,是Class類的例項物件,這個物件我們成為該類的類型別

1.下面是一個小的test,以產生3種方式的類型別:

public class test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        foo user = new foo();
        //任何一個類都包含一個隱含的靜態成員變數class
        Class c1 = foo.class;
        Class c2 = user.getClass();
Class c3 = null; try { c3 = Class.forName("test.foo"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); }
        //c1 c2,c3是foo類的類型別
    }
class foo{ void print(){ System.out.println("haha"); } }

2.其實資料型別也有自己的類型別

  Class class1 = int.class;//int的類型別
  Class class2 = String.class;
  Class class3 = double.class;
  Class class4 = Double.class;
  System.out.println(class1);
  System.out.println(class2.getSimpleName());
  System.out.println(class3);
  System.out.println(class4);

3.類型別可以建立類的例項物件

try {
    foo h =(foo)c1.newInstance();//類型別可以建立該類的例項物件
    //需要有無引數的構造方法
    h.print();
} catch (InstantiationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

二.動態載入類

1.我們有這樣的疑惑,如果一個主函式中有好多個類,其中有一個類出現錯誤,其他類都沒法用,所以我們就想我們要用那個類就讓那個類執行,並且不想在重新編譯主類,這時候動態載入類就派上了用場,不用動態類的時候會出現類似這樣的問題:

當然這種情況只會出現在我們手動採用javac編譯導致的,開發工具讓我們看不到這些細節的東西。.

2.我們先引出動態載入類的概念,class.forName("")是動態載入類,而new class()是靜態載入類,本質區別是,靜態載入類的時候,編譯時類必須存在,而動態載入類不一定存在。

public class sum {
    public static void main(String[] args) {
        try
        {
            //動態載入類
            //args[0]表示的是在main方法裡面傳進來的第一個引數
            Class c = Class.forName(args[0]);

        }
        catch(Exception e)
        {

            e.printStackTrace();
        }
    }
}

三.類對類的操作

1.通過Method類獲取成員方法

/**
     * 獲取成員方法
     * Method類,方法物件
     * 一個成員方法就是一個Method物件
     * getMethods()方法獲取所有的public的函式,包括父類繼承而來的
     * getDeclaredMethods()獲取的是所有該類自己宣告的方法,不問訪問許可權
     * @param obj
     */
    public static void  printClassMessage(Object obj) {
        Class c1 = obj.getClass();
        System.out.println(c1.getName());
        Method[] ms = c1.getMethods();//c.getDeclaredMethods()
        for (int i = 0; i < ms.length; i++) {
            //得到方法的返回值型別的類型別
            Class returnType = ms[i].getReturnType();
            System.out.print(returnType.getName()+"");
            //得到方法的名稱
            System.out.print(ms[i].getName()+"(");
            //獲取引數型別--->得到的是引數列表的型別的類型別
            Class[] paramTypes = ms[i].getParameterTypes();
            for (Class class1 : paramTypes) {
                System.out.println(class1.getName());
            }
            System.out.println(")");
        }
        
    }

2.通過Field類獲取成員變數

/**
     * 獲取成員變數
     * 成員變數也是物件
     * java.lang.reflect.Field
     * Filed類封裝了關於成員變數的操作
     * getFileds()方法獲取的是所有的public的成員變數的資訊
     * getDeclaredFiled獲取的是該類自己宣告的成員變數的資訊
     * @param obj
     */

    public static void printFieldMethod(Object obj) {
        Class c1 = obj.getClass();
        Field[] fs =c1.getDeclaredFields();
        for (Field field : fs) {
            //得到成員變數的型別的類型別
            Class fieldType =field.getType();
            String typeName = fieldType.getName();
            //得到成員變數的名稱
            String fieldName=field.getName();
            System.out.println(typeName+" "+fieldName);
        }
    }

3.通過Constructor類獲取建構函式

/**
     * 列印物件的建構函式的資訊
     * java.lang.Constructor中封裝了建構函式的資訊
     * getConstructors獲取所有的public的建構函式
     * getDeclaredConstructors得到所有的建構函式
     * @param obj
     */
    public static void printConMessage(Object obj){
        Class c = obj.getClass();
        //Constructor cs = c.getConstructors();
        Constructor[] cs =c.getDeclaredConstructors();
        for (Constructor constructor : cs) {
            System.out.println(constructor.getName()+"(");
            Class[] paramTypes =constructor.getParameterTypes();
            for (Class class1 : paramTypes) {
                System.out.println(class1.getName()+",");
            }
            System.out.println(")");
        }
    }

四.反射

1.反射的作用是指定方法名稱呼叫方法,demo如下:

public class demo {

    public static void main(String[] args) {
        //獲取類的資訊,就得先獲取類的類型別
        A a1 = new A();
        Class c1 = a1.getClass();
        /*
         * 獲取方法,名稱和引數列表來決定
         * getMethod獲取的是public的方法
         * getDelcaredMethod獲取的的是自己宣告的方法
         * 
         */
        try {
            Method method = c1.getMethod("print", int.class,int.class);
            
            /*
             * 方法的反射操作
             * a1.print(10,20);方法的反射操作是用m物件來進行方法呼叫和a1.print呼叫的效果相同
             * 方法如何沒有返回值返回null,有返回值返回具體的返回值
             * ...表示有幾個引數傳幾個
             */
             Object object =method.invoke(a1, 10,20);
             Method method2 =c1.getMethod("print", new Class[]{String.class,String.class});
             method2.invoke(a1, "hello","world");
             Method method3 =c1.getMethod("print",null);
             Object a = method3.invoke(a1);
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
}
class A{
    public void print() {
        System.out.println("hello world");
    }
    
    public void print(int a,int b){
        System.out.println(a+b);
    }
    public void print(String a,String b){
        
        System.out.println(a.toUpperCase()+b.toLowerCase());
    }
    
    
}

2.案例,通過標準JavaBean的屬性名獲取其屬性值

五.泛型

1.泛型是什麼?看下面兩個宣告

//只能傳String型別的數
        ArrayList<String> list = new ArrayList<String>();
        ArrayList list2 = new ArrayList();

2.泛型的本質,看下面的demo

public class fanxing {
/**
 * 通過Class,Method來認識泛型的本質
 * @param args
 */
    public static void main(String[] args) {
        //只能傳String型別的數
        ArrayList<String> list = new ArrayList<String>();
        ArrayList list2 = new ArrayList();
        list.add("hello");
        //list.add(20);
        Class c1 = list.getClass();
        Class c2 =list2.getClass();
        System.out.println(c1==c2);
       }
}

由執行結果可以得出下面結論:

反射的操作都是編譯之後的操作

c1==c2結果返回true說明編譯之後的泛型是去泛型化的

Java中集合的泛型,是防止錯誤輸入的,只是在編譯階段有效

3.利用方法的反射來操作,繞過編譯

try {
            Method a1 =c1.getMethod("add", Object.class);
            Object a2 = a1.invoke(list, 2);
         System.out.println(list.size());
         System.out.println(list);
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

程式碼的執行結果驗證了泛型,得出結論,繞過編譯操作就繞過了泛型

最後謝謝大家的閱讀