1. 程式人生 > >JavaReflect(Java反射機制)

JavaReflect(Java反射機制)

在我們做專案時有時為了優化開發效率通常會使用一些第三方框架,這些框架有網路請求、圖片處理、json解析、註解框架等等,尤其在我們想簡化程式碼提高開發效率時就會想到使用第三方的註解框架,比較流行的有butterknife、annotations、xutils等,在我們使用這些框架為我們帶來便利的同時,總是想了解一下它們的執行原理和機制,拿butterknife來說,想要寫出這樣一套框架出來必定要用到一些Java知識,其中就包括註解原理、Java的反射機制。

這篇文章先初步瞭解一下Java的反射機制:

1. 反射機制是什麼

反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性;這種動態獲取的資訊以及動態呼叫物件的方法的功能稱為java語言的反射機制。

2. 反射機制能做什麼

反射機制主要提供了以下功能:

  • 在執行時判斷任意一個物件所屬的類;
  • 在執行時構造任意一個類的物件;
  • 在執行時判斷任意一個類所具有的成員變數和方法;
  • 在執行時呼叫任意一個物件的方法;
  • 生成動態代理。

3.反射機制的相關API

java.lang.Class;
java.lang.reflect.Constructor; java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;

以下將通過具體程式碼實現來說明反射機制可以做哪些事情

  • 通過一個物件(類)獲得完整的包名和類名

ReflectClass 程式碼

package com.yd.test.javareflect;

import java.io.Serializable;

public class ReflectClass extends BaseReflectClass implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private int intValue = 0;
    private String stringValue = null
; public void method01(){ System.out.println("ReflectClass_01"); } public void method02(){ System.out.println("ReflectClass_02"); } public void method03(int intValue, String stringValue){ System.out.println("ReflectClass_Method03"+" intValue: "+intValue+" stringValue: "+ stringValue); } public void method04(){ System.out.println("intValue: "+intValue+"stringValue: "+stringValue); } }

BaseReflectClass 程式碼

package com.yd.test.javareflect;

public class BaseReflectClass {

    int BaseClassMod = 3;

    public void baseMethod(){

    }
}
  • 獲取類的完整包名
public static void main(String[] args) {
/** 獲取類的完整包名 */
ReflectClass reflectClass = new ReflectClass();     
        System.out.println(reflectClass.getClass().getName());
}

輸出:
com.yd.test.javareflect.ReflectClass
Class..getClass().getName()方法可獲取類的完整包名

  • 例項化物件
/** 三種例項化Class的方法 */
        Class class1 = null;
        Class class2 = null;
        Class class3 = null;
        try {
            class1 = Class.forName("com.yd.test.javareflect.ReflectClass");
            // java語言中任何一個java物件都有getClass 方法
            class2 = new ReflectClass().getClass();
            //java中每個型別都有class 屬性
            class3 = ReflectClass.class;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

以上三種例項化物件(類)的方式,一般我們用的比較多的也就是Class.forName(“xxx”)這種方式了,如果懶得去看包名,也可以用其它的方式。

  • 獲取物件實現的介面和繼承的類
/** 獲取物件實現的介面和繼承的類 */
        System.out.println(
                class1.getSuperclass().getName()+" / "
                        +class2.getSuperclass().getName()+" /  "
                        +class3.getSuperclass().getName()
                );

        Class classes[] = class1.getInterfaces();
        for (int i = 0; i < classes.length; i++) {
            System.out.println(classes[i].getName());
        }

輸出:
com.yd.test.javareflect.BaseReflectClass / com.yd.test.javareflect.BaseReflectClass / com.yd.test.javareflect.BaseReflectClass
java.io.Serializable
Class.getSuperclass().getName()可以獲取繼承了哪些類和實現的介面。

  • 例項化物件並給物件中的屬性賦值

User類程式碼

package com.yd.test.javareflect;

public class User {

    private int age;
    private String name;

    public User(){}

    public User(String name) {
        super();
        this.name = name;
    }

    public User(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return getName()+"/"+getAge();
    }
}
    /* 例項化物件並給物件中的屬性賦值 */
        Class c = null;
        try {
            c = Class.forName("com.yd.test.javareflect.User");
            // 例項化User物件
            User user = (User) c.newInstance();
            user.setAge(25);
            user.setName("danity");
            System.out.println(user.toString());

輸出:
danity/25

  • 獲取每個建構函式所需要的引數型別資訊
            Constructor<?> constructor[] = c.getConstructors();
            // 獲取每個建構函式所需要的引數型別資訊
            for (int i = 0; i < constructor.length; i++) {
            // getParameterTypes():獲取建構函式引數型別
                Class arrayClass[] = constructor[i].getParameterTypes();
                System.out.print("cons[" + i + "] (");
                for (int j = 0; j < arrayClass.length; j++) {
                    if (j == arrayClass.length - 1)
                        System.out.print(arrayClass[j].getName());
                    else
                        System.out.print(arrayClass[j].getName() + ", ");
                }
                System.out.println(")");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

輸出:
cons[0] (java.lang.String)
cons[1] (int, java.lang.String)
cons[2] ()

以上可以看出,利用反射可以獲取類建構函式的引數型別資訊、對一個物件的屬性進行賦值,這也是比較常用的方法。

  • 獲取類的所有屬性
/* 獲取類的所有屬性 */
        try {
            Class rClass = Class.forName("com.yd.test.javareflect.ReflectClass");
            // Field: 獲取屬性,下面還會講到獲取類的方法,注意區分
            Field field[] = rClass.getDeclaredFields();
            for (int i = 0; i < field.length; i++) {
                System.out.println(field[i].getName());
                // 獲取修飾許可權符
                int mo = field[i].getModifiers();
                System.out.println("mo: "+mo);
                String priv = Modifier.toString(mo);
                // 屬性型別
                Class type = field[i].getType();
                System.out.println(priv + " " + type.getName() + " " + field[i].getName());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

輸出:
serialVersionUID
mo: 26
private static final long serialVersionUID
intValue
mo: 2
private int intValue
stringValue
mo: 2
private java.lang.String stringValue

  • 獲取父類和實現的介面的所有屬性

/* 獲取父類和實現的介面的所有屬性 */
        Class rClass = null;
        try {
            rClass = Class.forName("com.yd.test.javareflect.ReflectClass");
            Field field[] = rClass.getFields();
            for (int j = 0; j < field.length; j++) {
                System.out.println(field[j].getName());
                // 獲取修飾許可權符
                int mo = field[j].getModifiers();
                System.out.println("mo: "+mo);
                String priv = Modifier.toString(mo);
                // 屬性型別
                Class type = field[j].getType();
                System.out.println(priv + " " + type.getName() + " " + field[j].getName());
                }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
  • 獲取類的全部方法
package com.yd.test.javareflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Main2 {

    /**
     * @param args
     */
    public static void main(String[] args) {        
        try {
            Class fClass = Class.forName("com.yd.test.javareflect.ReflectClass");
            // Method[]: 方法陣列
            Method method[] = fClass.getMethods();
            for (int i = 0; i < method.length; i++) {
                // returnType :返回型別
                Class returnType = method[i].getReturnType();
                System.out.println("ReturnType: "+returnType);
                // 獲取引數型別
                Class para[] = method[i].getParameterTypes();

                int temp = method[i].getModifiers();
                System.out.print("Modifier.toString: "+Modifier.toString(temp) + " ");
                System.out.print(returnType.getName() + "  ");
                System.out.print(method[i].getName() + " ");
                System.out.print("(");
                for (int j = 0; j < para.length; ++j) {
                    System.out.print(para[j].getName() + " " + "arg" + j);
                    if (j < para.length - 1) {
                        System.out.print(",");
                    }
                }
                // 獲取異常型別
                Class<?> exce[] = method[i].getExceptionTypes();
                if (exce.length > 0) {
                    System.out.print(") throws ");
                    for (int k = 0; k < exce.length; ++k) {
                        System.out.print(exce[k].getName() + " ");
                        if (k < exce.length - 1) {
                            System.out.print(",");
                        }
                    }
                } else {
                    System.out.print(")");
                }
                System.out.println();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }

}

輸出:
ReturnType: void
Modifier.toString: public void method01 ()
ReturnType: void
Modifier.toString: public void method02 ()
ReturnType: void
Modifier.toString: public void method03 (int arg0,java.lang.String arg1)
ReturnType: void
Modifier.toString: public void method04 ()
ReturnType: void
Modifier.toString: public void baseMethod ()
ReturnType: void
Modifier.toString: public final void wait () throws java.lang.InterruptedException
ReturnType: void
Modifier.toString: public final void wait (long arg0,int arg1) throws java.lang.InterruptedException
ReturnType: void
Modifier.toString: public final native void wait (long arg0) throws java.lang.InterruptedException
ReturnType: int
Modifier.toString: public native int hashCode ()
ReturnType: class java.lang.Class
Modifier.toString: public final native java.lang.Class getClass ()
ReturnType: boolean
Modifier.toString: public boolean equals (java.lang.Object arg0)
ReturnType: class java.lang.String
Modifier.toString: public java.lang.String toString ()
ReturnType: void
Modifier.toString: public final native void notify ()
ReturnType: void
Modifier.toString: public final native void notifyAll ()

資訊比較多,撿需要的資訊看

下面是Java反射比較重要的用法:

  • 通過反射給物件中的某個方法賦值傳值

ReflectClass 程式碼

package com.yd.test.javareflect;

import java.io.Serializable;

public class ReflectClass extends BaseReflectClass implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private int intValue = 0;
    private String stringValue = null;


    public void method01(){
        System.out.println("ReflectClass_01");
    }

    public void method02(){
        System.out.println("ReflectClass_02");
    }

    public void method03(int intValue, String stringValue){
        System.out.println("ReflectClass_Method03"+" intValue: "+intValue+" stringValue: "+ stringValue);
    }

    public void method04(){
        System.out.println("intValue: "+intValue+"stringValue: "+stringValue);
    }

}
public static void main(String[] args) {

        /**
         * 通過反射給物件類中的某個方法賦值傳值
         */
        try {
            Class<?> fclass = Class.forName("com.yd.test.javareflect.ReflectClass");
            // 呼叫ReflectClass的“method01方法”
            Method method = fclass.getMethod("method01");
            method.invoke(fclass.newInstance());
            // int.class, string.class 對應方法的引數型別,位置不能寫錯
            Method mod = fclass.getMethod("method03", int.class, String.class);
            mod.invoke(fclass.newInstance(),20,"test");

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
}

輸出:
ReflectClass_01
ReflectClass_Method03 intValue: 20 stringValue: test
從輸出內容來看,通過反射呼叫了類中的兩個方法並對方法引數進行了操作

  • 通過反射給物件類中某個屬性賦值
public static void main(String[] args) {
try {
            Class sClass = Class.forName("com.yd.test.javareflect.ReflectClass");
            //例項化這個類賦給obj
            Object obj = sClass.newInstance();
            Field field = sClass.getDeclaredField("intValue");
            //打破封裝 
            //使用反射機制可以打破封裝性,導致了java物件的屬性不安全。
            field.setAccessible(true);
            //給obj物件的intValue屬性賦值"3"
            field.set(obj, 3);
            // 列印值
            System.out.println(field.get(obj));

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
}

輸出:
3

以上是Java反射的基本用法,還有一些動態代理的用法有興趣的可以自己找找資料。
現在,離掌握註解知識又更近了一步……