1. 程式人生 > >Java 高級開發必修知識---反射

Java 高級開發必修知識---反射

增強 函數參數 一個 catch family blog lac 下午 成員

文章開始之前 提一下:

java反射操作其實就是主要圍繞Class,Field,Methon等幾個類來操作其中的方法

Class類的使用

1) 在面向對象的世界裏,萬事萬物皆對象

A. Java語言中,普通數據類型,靜態成員不是對象,其他皆對象

B. 每一個類也是對象

C. 類是java.lang.Class類的實例對象

There is a class named Class

對象的表示:

普通類對象表示:

Foo foo = new Foo();

Class類實例對象表示:

//Foo也是一個實例對象,是Class類的實例對象

任何一個類都是Class的實例對象,這個實例對象有三種表達方式

1. 任何一個類都有一個隱含的靜態成員變量class

Class c1 = Foo.class;

2. 已經指定該類的對象通過getClass方法

Foo foo = new Foo();

Class c2 = foo.getClass();

官網:c1/c2 表示了Foo類的類類型(class type

一個類,本身就是一個對象,它是Class類的對象

萬事萬物皆對象,類也是對象,是Class類的實例對象,這個對象我們稱為該類的類類型

3. ForName(“類的全稱”);

Class c3 = null;

c3=Class.forName(“包名+.某類名”);

不管是那種表達方式,都是

Class類的對象,都是Foo類的類類型。所以:

C1=c2=c3

完全可以通過類的類類型創建該類的對象(創建Foo的實例對象,但要做強制類型轉換,向下轉型

前提要求:需要有無參數的構造方法

Comont comont = new Comont();
Class c1 = comont.getClass();
Class c2 = Comont.class;
try {
Comont c = (Comont) c2.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace(); } System.out.println(c1 == c2); comont.start();

動態加載類

Class.forName(“類的全稱”);

1. 不僅表示了類的類類型,還代表動態加載類

2. 編譯時刻加載類是靜態加載類,運行時刻加載類是動態加載類

(1) New 對象是靜態加載類,在變異時刻就需要加載所有的可能使用到的類

(2) 動態加載類,在運行時刻加載

編譯不會報錯

運行時刻報錯。找不到該類

Class c = Class.forName(arg[0]);

共同實現接口類 cc = (共同實現接口類) c.newInstance();

3. 使用記事本開發可明顯區分

Java 類 要運行類:動態加載類,不需要重新編譯測試類,直接運行即可

功能性的類:盡量使用動態加載

基本數據類型也有類類型

Class c1 = int.class;
Class c2 = String.class;//String類的字節碼

數據類型和包裝類的類類型不同

Void也是類

Class c3 = void.class;

基本數據類型

Void關鍵字

都存在類類型

方法也是對象,方法是Method的對象

反射:某類的字節碼表示

獲取方法信息

1. c.getName()

(1) 基本數據類型返回類型名

(2) 類返回包名+類名類的名稱

2. c1.getSimpleName()

(1) 返回不帶包名的類的名稱

栗子:通過反射可以獲取到任何類的信息

需求:打印類的信息,獲取類的 成員函數

package cn.pro;

import java.lang.reflect.Method;

/**
 * 
 * @author: 房上的貓
 * 
 * @time: 下午5:34:45
 * 
 * @博客地址: https://www.cnblogs.com/lsy131479/
 *
 */

public class mymain {
    public static void printClassMessage(Object obj) {
        // 要獲取類的信息 首先要獲取類的類類型
        // 形參obj 該對象所屬類的信息
        Class c = obj.getClass();// 傳遞的是哪個子類的對象 c就是該        子類的類類型
        // getClass()方法:native修飾代表 存在不是java 代碼 ,調度        外用 ///該方法java內調用底層c語言實現

        // 獲取累的名稱
        System.out.println("類的名稱是:" + c.getName());
        // Method類是方法對象
        // 一個成員方法就是一個Method
        // getMethods()方法獲取的是所有的public修飾的函數,包括父類        繼承而來的
        Method[] ms = c.getMethods();
        // c.getDeclaredMethods()獲取的是所有該類自己聲明的方法,不        問訪問權限.所有。所有。所有
        String[] name = new String[ms.length];
        for (int i = 0; i < ms.length; i++) {
            // 得到方法的返回值類型--得到的是返回值類型的類類型
            Class returnType = ms[i].getReturnType();
            // 得到返回值名字
            String returnName = returnType.getName();
            // 得到方法的名稱
            name[i] = ms[i].getName();
            // 獲取參數列表類型--得到的是參數列表的類型的類類型
            Class[] parameterTypes = ms[i].getParameterTypes();
Int params=parameterTypes.length;            
String[] paramNames = new String[params];
            for (int j = 0; j < params; j++) {
                // 得到參數列表名
                paramNames[j] = ms[j].getName();
            }
        }
    }
}

通過反射可以獲取任何類的類信息

比較牛逼

獲取類的成員變量構造函數信息

成員變量也是對象

Java.lang.reflect.Field的對象

Field類封裝了關於成員變量的操作

栗子:通過反射可以獲取到任何類的信息

需求:打印類的信息,獲取類的成員變量

package cn.reflect;

import java.lang.reflect.Field;

/**
 * 
 * @author: 房上的貓
 * 
 * @time: 下午3:49:32
 * 
 * @博客地址: https://www.cnblogs.com/lsy131479/
 *
 */

public class myField {
    public static void printFieldMessage(Object obj) {
        Class c = obj.getClass();
        // getFidlds() 方法獲取的是類的所有的public的成員變量信息
        Field[] fs = c.getFields();

        // getDeclaredFields() 獲取的是該類自己聲明的成員信息 不問訪問權限.所有。所有。所有
        // Field[] fs = c.getDeclaredFields();

        for (Field field : fs) {
            // 得到成員變量的類型的類類型
            Class fieldType = field.getType();
            // 得到成員變量類型的名字
            String typeName = fieldType.getName();
            // 得到成員變量的名字
            String fieldName = field.getName();
            //

        }

    }
}

構造函數也是對象

Java.lang.Constructor的對象 其中封裝了構造函數的信息

栗子:通過反射可以獲取到任何類的信息

需求:打印類的信息,獲取類的構造函數信息

package cn.reflect;

import java.lang.reflect.Constructor;

/**
 * 
 * @author: 房上的貓
 * 
 * @time: 下午3:49:32
 * 
 * @博客地址: https://www.cnblogs.com/lsy131479/
 *
 */

public class myCon {
    public static void printConMessage(Object obj) {
        Class c = obj.getClass();
        // getConstructors() 獲得所有的共有的構造方法
        Constructor[] cs = c.getConstructors();
        // getDeclaredConstructors() 得到所有的構造方法(必須是自己聲明的) 不問訪問權限.所有。所有。所有
        // Constructor[] cs = c.getDeclaredConstructors();
        for (Constructor constructor : cs) {
            // 獲取構造函數名
            String name = constructor.getName();
            // 獲取構造函數的參數列表------>得到的是參數列表的類類型
            Class[] parameterTypes = constructor.getParameterTypes();
            for (Class class1 : parameterTypes) {
                // 獲取構造函數參數列表名
                String name2 = class1.getName();
            }
        }
    }
}

其他

Class類還可以獲取類的其他信息,這裏將不再詳細講解

想要了解的可以創建Class類的實例對象

Class c = obj.getClass();

c.get...

自行查看並嘗試

或閱讀幫助文檔,查看Class類的所有API

記住一點:在任何情況下想要獲取一個類的信息,首先要得到這個類的類類型

得到類的類類型,得到這個類的類信息就輕而易舉得到了

方法的反射

1. 如何獲取某個方法?

方法的名稱和方法的參數列表才能唯一決定某個方法

2. 方法反射的操作

Method.invoke(對象,參數列表)

栗子:

package cn.reflect;

import java.lang.reflect.Method;

/**
 * 
 * @author: 房上的貓
 * 
 * @time: 下午4:25:25
 * 
 * @博客地址: https://www.cnblogs.com/lsy131479/
 *
 */

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

        // 要獲取print(int,int)方法
        A a = new A();
        /*
         * 1.要獲取一個方法就是要獲取一個類的信息,獲取類的信息首先要獲取類的類類型
         */

        Class c = a.getClass();
        /*
         * 2.獲取方法 名稱和參數列表
         * 
         * getMethod(name, parameterTypes)獲取的是public的方法
         * 
         * c.getDeclaredMethod(name, parameterTypes)獲取的是所有自己聲明的方法 不問訪問權限
         * 
         * 
         * 參數解析: 1.name:方法名 2.parameterTypes:參數列表類型(0或多個)
         */
        Method m = c.getMethod("print", new Class[] { int.class, int.class });

        /*
         * 方法的反射操作
         * 
         * a.print(10,20);方法的反射操作是用m對象來進行方法調用 和a.print調用的效果
         * 
         * 方法如果沒有返回值返回null 有返回值返回具體的返回值 返回object類型,需要做強制類型轉換
         */

        Object o = m.invoke(a, new Object[] { 10, 20 });
        // 就等於調用了print(int,int)方法

        /*
         * 如果方法無參數列表則:
         * 
         * c.getMethod("print"); m.invoke(a);
         */

    }
}

class A {
    public void print(int a, int b) {
        System.out.println(a + b);
    }

    public void print(String a, String b) {
        // a的大寫形式 and b的小寫形式
        System.out.println(a.toUpperCase() + "," + b.toLowerCase());
    }
}

升華操作:

通過反射了解集合泛型的本質

通過ClassMethod來認識泛型的本質

相信讀者們看到這裏心中一定會有這樣兩個疑問:

什麽是泛型?

泛型什麽時候有效?

那我們探討一下這兩個話題:

package cn.reflect;

import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * 
 * @author: 房上的貓
 * 
 * @time: 下午6:31:38
 * 
 * @博客地址: https://www.cnblogs.com/lsy131479/
 *
 */

public class MethodDemo2 {
    public static void main(String[] args) {
        // 普通集合
        ArrayList list = new ArrayList<>();

        // 泛型集合 集合內只能存放‘<>‘尖括號內類型的值
        ArrayList<String> list1 = new ArrayList<>();
        // 集合的泛型防止錯誤輸入

        // 利用反射了解集合泛型
        Class c1 = list.getClass();
        Class c2 = list1.getClass();
        list1.add("1");
        System.out.println(c1 == c2);
        /*
         * 反射的操作都是編譯之後的操作(運行時)
         * 
         * c1==c2結果返回true 表示兩個集合類運行時都是同一類類型
         * 
         * 說明編譯之後集合的泛型是去泛型化的(編譯完之後就沒有泛型存在了)
         * 
         * java中集合的泛型,是防止錯誤輸入的,只在編譯階段有效,繞過編譯就無效了
         * 
         * 驗證:可以通過方法的反射來操作,繞過編譯
         */
        try {
            Method m = c2.getMethod("add", Object.class);
            m.invoke(list1, 100);// 大膽猜想:繞過編譯操作就繞過了泛型
            // 集合大小返回2 .說明:繞過編譯階段後可以向泛型集合添加任何類型的值
            System.out.println(list1.size());
            // 嘗試查看泛型集合內的值 發現值已經添加進去
            System.out.println(list1);

            /*
             * 在這裏如果使用for增強遍歷將會拋異常
             * 
             * 因為後面添加的100是int類型的值
             * 
             * 而集合泛型是String類型
             * 
             * 使用for增強遍歷內部其實做出的操作是:集合的每個值都使用一個集合泛型類型的數據類型來接受遍歷
             * 而int類型使用String類型來接受,眾所周知將會報錯(這裏的泛型類型是String)
             * 
             * 會有類型轉換錯誤
             */
        } catch (NoSuchMethodException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

結論:

反射(ClassMethodField ... )的操作都是繞過編譯,都是在運行時刻來執行的

ok...到此為止!!!後續如果有新的想法將會繼續總結。。敬請期待

(C) 房上的貓 。 保留所有權利。
https://www.cnblogs.com/lsy131479/

如需轉載,請註明出處!!!

Java 高級開發必修知識---反射