JAVA——反射——內省機制

  • 反射的概念

    JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。

    一切事物都是對象,構造方法,成員變量,成員方法都是事物,分別對應它的類,對應對象。通過類的字節碼,可以分別調用方法得到構造方法對象,成員變量對象,成員方法對象,有對象可以調用各種類裏面方法。

    ——通俗點,反射就是通過獲取類的字節碼文件對象,調用方法得到類的一切東西,運行的是字節碼(動態),這個通過動態獲取類的信息方式或者說機制叫做Java語言反射機制。

  • 反射的特點

    Java的反射機制是在編譯並不確定是哪個類被加載了,而是在程序運行的時候才加載、探知、自審。使用在編譯期並不知道的類。這樣的特點就是反射。

    反射是框架的靈魂! 反射的靈魂是字節碼文件對象。反射其實就是將運行時一個類的各個部分重新拼裝成一個類的過程。

  • 反射的作用

    • 在運行時判斷任意一個對象所屬的類
    • 在運行時構造任意一個類的對象(不用new,直接使用字節碼文件對象就能給你創建對象)
    • 在運行時判斷任意一個類所具有的成員變量和方法
    • 在運行時調用任意一個對象的方法。通過反射甚至可以調用到private方法。
    • 生成動態代理(代理模式中的一種)

      • 假如有兩個開發者,開發者一在寫程序的時候,需要使用第開發者二所寫的類,但開發者二並沒完成他所寫的類。那麼第開發者一的代碼能否通過編譯呢?這是不能通過編譯的。利用Java反射的機制,就可以讓開發者一在沒有得到開發者二所寫的類的時候,來完成自身代碼的編譯。

      • Java的反射機制它知道類的基本結構,這種對Java類結構探知的能力,我們稱爲Java類的“自審”。大家都用過eclipse和idea。當我們構建出一個對象的時候,去調用該對象的方法和屬性的時候。一按點,編譯工具就會自動的把該對象能夠使用的所有的方法和屬性全部都列出來,供用戶進行選擇。這就是利用了Java反射的原理,是對我們創建對象的探知、自審。

  • 反射底層原理

這裏寫圖片描述

藍色實線部分爲編譯階段,紅色實線部分爲運行階段,當一個類經過編譯器編譯後就會生成該類的字節碼文件。當程序運行時,如果在代碼中使用到類的字節碼文件對象的時候(注意是一個對象,字節碼文件對象可以通過:類名.class、Class.forName(“類的全限定名”)、類名.getClass()獲取),JVM就會調用分配給用戶使用的ClassLoader來對該類進行加載,將這個類的字節碼文件加載成一個字節碼文件對象保存到數據區中,ClassLoader將類中的構造函數、成員變量、成員方法分別轉化成對象存放到對應的數據區中,而後,就可以通過獲取這些對象來進行對相應的操作。


小例子:通過反射調用User(JavaBean)類的構造函數創建對象、遍歷輸出成員變量、方法的名字,改變某個(私有)共有成員變量的值、調用某個(私有)公有的成員方法。


例子用到的User類

public class User {

    private String name;
    private int age;
    private int idCard;
    private String sex;
    private String description;

    public User() {

    }

    public User(String name, int age, int idCard, String sex, String description) {

        this.name = name;
        this.age = age;
        this.idCard = idCard;
        this.sex = sex;
        this.description = description;
    }

    private void method1(){
        System.out.println("這是一個無參數私有的方法");
    }

    private void method2(String str){
        System.out.println("這是一個有一個String類型參數私有的方法,參數是:"+str);
    }

    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;
    }

    public int getIdCard() {
        return idCard;
    }

    public void setIdCard(int idCard) {
        this.idCard = idCard;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "User [name=" + name + ", age=" + age + ", idCard=" + idCard + ", sex=" + sex + ", description="
                + description + "]";
    }
}

測試ReflectTest類

public class ReflectTest {
    /**
     * 要使用反射必須有字節碼文件對象
     * 獲取字節碼文件對象的三種方式
     * 1.Class.forName("類的全限定名"),這種方式最常使用----註冊驅動 Class<?> clazz = Class.forName("類的全限定名");
     * 2.類名.class----DBUtils中使用BeanHandler的構造函數時傳參數。Class clazz = 類名.class;
     * 3.對象名.getClass(),------在不知道類名的情況下
     */

    private Object obj;

    //使用反射調用無參構造函數創建對象
    @Test
    public void test0(){
        try {
            Class clazz =  Class.forName("com.yanghao.bean.User");
            Object o = clazz.newInstance();
            System.out.println(o);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();

        }
    }
    //使用反射調用有參構造函數創建對象
    @Test
    public void test1(){
        try {
            Class<?> clazz = Class.forName("com.yanghao.bean.User");
            Class[] params = {String.class,int.class,int.class,String.class,String.class};
            Constructor<?> constructor = clazz.getConstructor(params);
            obj = constructor.newInstance("張三",15,4417811,"男","張三是三");
            System.out.println(obj);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    //使用反射獲取類的共有成員變量遍歷輸出
    @Test
    public void test2(){
        try {
            Class clazz = Class.forName("com.yanghao.bean.User");
            Field[] fields = clazz.getFields();
            for (Field field : fields) {
                System.out.println(field.getName());
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    //使用反射獲取類的所有成員變量(公有和私有)遍歷輸出每個成員變量的值
    @Test
    public void test3(){

        try {

            //test1();
            Class<?> forName = Class.forName("com.yanghao.bean.User");
            Field[] declaredFields = forName.getDeclaredFields();
            for (Field field : declaredFields) {
                int modifiers = field.getModifiers();
                Object value = null;
                if(modifiers == Modifier.PUBLIC){
                    value = field.get(obj);
                }
                else{
                    field.setAccessible(true);
                    value = field.get(obj);
                }

                System.out.println(field.getName()+":"+value);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    //獲取User類中所有的公有方法
    @Test
    public void test4(){

        try {
            Class clazz = Class.forName("com.yanghao.bean.User");
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                System.out.println(method.getName());
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
    }

    //獲取User類中所有的方法,包括公有方法和和私有方法
    @Test
    public void test5() {

        try {
            Class clazz = Class.forName("com.yanghao.bean.User");
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method method : declaredMethods) {
                method.setAccessible(true);
                System.out.println(method.getName());
            }

        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    //調用User類裏面的私有方法Method1和Method2
    @Test
    public void test6(){

        try {
            Class<?> clazz = Class.forName("com.yanghao.bean.User");
            Object newInstance = clazz.newInstance();
            Method method1 = clazz.getDeclaredMethod("method1");
            method1.setAccessible(true);
            method1.invoke(newInstance);

            Method method2 = clazz.getDeclaredMethod("method2", String.class);
            method2.setAccessible(true);
            method2.invoke(newInstance, "我是參數");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

內省機制(Introspector)

內省機制就是通過反射區訪問JavaBean對象的屬性,通過內省機制可以很方便的調用一個JavaBean類裏面的get和set方法。使用方法如下:

  • 獲取JavaBean對象中某個成員變量的get和set方法,獲取變量的值並賦值:
//創建對象
User user=new User();
//創建屬性描述器
PropertyDescriptor descriptor=new PropertyDescriptor("name",User.class);
//獲得讀方法
Method readMethod=descriptor.getReadMethod();
//調用讀方法
readMethod.invoke(user);
//獲得寫方法
Method writeMethod=descriptor.getWriteMethod();
//調用寫方法
writeMethod.invoke(user, "hello");
  • 獲取JavaBean對象的所有的set和get方法
Class<User> clazz = (Class<User>) Class.forName("User類的全限定名");
User user = clazz.newInstance();
//獲取某個JavaBean類的所有屬性的屬性描述器
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
//遍歷出每一個屬性描述器
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
    //使用每個屬性描述器,獲取各個屬性的set和get方法
}
發佈了33 篇原創文章 · 獲贊 13 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章