Java高階特性之反射學習總結

soul_code發表於2016-01-11

老規矩我們還是先提出幾個問題,一門技術必然要能解決一定的問題,才有去學習掌握它的價值

  • 一、 什麼是反射?
  • 二、反射能做什麼?

一、 什麼是反射?

用在Java身上指的是我們可以於執行時載入、探知、使用編譯期間完全未知的classes。換句話說,Java程式可以載入一個執行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其物件實體、或對其fields設值、或喚起其methods。

如果你是一個Android Developer,前輩們都會教導你儘量少用反射,效率太低,太慢。“射”嘛,射的太快就不好了,所以反射雖然慢點,但是偶爾射一下還是很”爽”的。

二、反射能做什麼?

1、新建類的例項

我們知道所有的類都繼承子頂級父類Object,而Object中有hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一個Class 物件。我們這裡就需要使用的Class物件,注意C是大寫,我們可以通過一下方式來獲取Class物件

1.Class.forName(“類名字串”) (注意:類名字串必須是全稱,包名+類名)
2.類名.class
3.例項物件.getClass()

在Class類中有一個非常重要的方法

 public T newInstance() throws InstantiationException, IllegalAccessException {
        return newInstanceImpl();
    }

 private native T newInstanceImpl() throws IllegalAccessException, InstantiationException;

檢視Api可以看到呼叫newInstace方法可以返回當前class對應的例項物件。接下來看一個小的Demo

public class Reflection {

    public static void main(String[] args) {

        // 普通建立類的例項

        People p1 = new People();
        System.out.println(p1.getName());
        // 利用反射獲取類的例項
        Class clazz = People.class;
        // 常用方式,注意括號中需要放類的全路徑名
        // Class clazz = Class.forName("reflection.People");
        // Class clazz = p1.getClass();
        try {
            People p2 = (People) clazz.newInstance();
            System.out.println(p2.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

class People {

    private String name = "張三";
    private int age;

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

}

輸入結果:

張三
張三

2、獲取成員變數和方法

在講之前我們先來看這樣一個小按理,JSON資料轉JavaBaen物件,在不用解析庫的情況下,一般我們會這樣做

private void analysisDate(JSONObject response) throws JSONException {
        int announceid = response.getInt("announceid");
        String title = response.getString("title");
        String hits = response.getString("hits");
        String addtime = response.getString("addtime");
        NewsNotifyItem newsNotifyItem = new NewsNotifyItem(announceid,
                title, hits, addtime);
    }
}

每當我們需要解析額時候,都需要根據不同javabean來進行相應的解析,我們每次進行的操作都是一樣的,只是解析的資料不同而已,結合上篇帖子講到的泛型,這裡我們就可以再利用反射來自己做一個Json解析工具。

下面是我寫的一個JsonObject物件轉JavaBean的一個工具類,需要注意的是,JSON的key需要和欄位名保持一致,先說下思路

①首先通過反射獲取JavaBean中的所有欄位值的名稱
②拼接出set方法
③由於欄位名和Json的key值相同,利用自動名獲取Json中的值並填充的例項物件中

public class Json2BeanUtils {

    public static <T> T jsonToBean(JSONObject response, Class<T> clazz) {
        try {
            // 建立類的例項
            Object object = Class.forName(clazz.getName()).newInstance();
            // 獲取類中的所有成員變數
            Field[] fields = object.getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                //設定許可權
                fields[i].setAccessible(true);
                // 獲取欄位的名稱
                String fieldName = fields[i].getName();
                // 過濾掉UID
                if (fieldName.endsWith("serialVersionUID")) {
                    continue;
                }
                // 獲取欄位的型別
                String fieldType = fields[i].getGenericType().toString();
                // 拼接出JavaBean中的set方法 這裡有一個坑 後面講解
                String methodName = "set"
                        + fieldName.substring(0, 1).toUpperCase()
                        + fieldName.substring(1);
                try {
                    // 判斷變數型別
                    if (fieldType.endsWith("class java.lang.String")) {
                        // 獲取到set方法
                        Method m = object.getClass().getMethod(methodName,
                                String.class);
                        String value = null;
                        try {
                            // 從JsonObj中取出相應的值
                            value = response.getString(fieldName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            value = "";
                        }
                        if (TextUtils.isEmpty(value)) {
                            value = "";
                        } else if (value.endsWith("null")) {
                            value = "";
                        }
                        // 賦值
                        m.invoke(object, value);
                    } else if (fieldType.endsWith("int")
                            || fieldType.endsWith("class java.lang.Integer")) {
                        // int 型別
                        System.out.println();
                        Method m = object.getClass().getMethod(methodName,
                                int.class);
                        int value = -1;
                        try {
                            value = response.getInt(fieldName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            value = -1;
                        }
                        m.invoke(object, value);

                    } else if (fieldType.endsWith("boolean")
                            || fieldType
                                    .endsWith("fieldType:class java.lang.Boolean")) {
                        // boolean 型別
                        Method m = object.getClass().getMethod(methodName,
                                boolean.class);
                        boolean value = false;
                        try {
                            value = response.getBoolean(fieldName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            value = false;
                        }
                        m.invoke(object, value);
                    } else if (fieldType.endsWith("double")
                            || fieldType
                                    .endsWith("fieldType:class java.lang.Double")) {
                        // double 型別
                        Method m = object.getClass().getMethod(methodName,
                                double.class);
                        double value = -1D;
                        try {
                            value = response.getDouble(fieldName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            value = -1D;
                        }
                        m.invoke(object, value);
                    } else if (fieldType.endsWith("char")) {
                        // char型別 JSONObject 沒有char
                        Method m = object.getClass().getMethod(methodName,
                                String.class);
                        String value = "";
                        try {
                            value = response.getString(fieldName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            value = "";
                        }
                        m.invoke(object, value);
                    } else if (fieldType.endsWith("float")
                            || fieldType
                                    .endsWith("fieldType:class java.lang.Float")) {
                        // float型別
                        Method m = object.getClass().getMethod(methodName,
                                double.class);
                        double value = -1D;
                        try {
                            value = response.getDouble(fieldName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            value = -1D;
                        }
                        m.invoke(object, value);

                    } else if (fieldType.endsWith("short")
                            || fieldType
                                    .endsWith("fieldType:class java.lang.Short")) {
                        // short
                        Method m = object.getClass().getMethod(methodName,
                                short.class);
                        int value = -1;
                        try {
                            value = response.getInt(fieldName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            value = -1;
                        }
                        m.invoke(object, value);
                    } else if (fieldType.endsWith("byte")
                            || fieldType
                                    .endsWith("fieldType:class java.lang.Byte")) {
                        Method m = object.getClass().getMethod(methodName,
                                byte.class);
                        int value = -1;
                        try {
                            value = response.getInt(fieldName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            value = -1;
                        }
                        m.invoke(object, value);
                    } else if (fieldType.endsWith("long")
                            || fieldType
                                    .endsWith("fieldType:class java.lang.Long")) {
                        Method m = object.getClass().getMethod(methodName,
                                long.class);
                        Long value = -1L;
                        try {
                            value = response.getLong(fieldName);
                        } catch (Exception e) {
                            e.printStackTrace();
                            value = -1L;
                        }
                        m.invoke(object, value);
                    }  
                } catch (Exception e) {
                    // TODO: handle exception
                }
            }
            return (T) object;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

}

這裡需要注意一個坑,先來看一段程式碼

class People {

    private String name;
    private int age;
    private String mSex;

    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 String getmSex() {
        return mSex;
    }
    // 這裡就出了問題  
    public void setmSex(String mSex) {
        this.mSex = mSex;
    }

}

當我們自動生成get set方法時,會將欄位的首字母大寫,我們在上面拼接set 方法時,也是基於這樣的規則來拼裝的。但是 當我們的欄位名為 aAbbb 時,則生成的get set 方法則不會大寫。解決方案也很簡單,注意欄位命名或者在拼接時對第二個自動進行大小寫判斷。這樣我們自己寫的Json解析工具就搞定, 以後每次解析只需一行程式碼即可OK。

今天反射就暫時講到這裡。其實反射的作用遠不於此,這裡只是使用了反射的冰山一角,結合註解和泛型,反射可以做的事更多,但是還是和開頭講的一樣,反射用起來雖然很爽,但是不能亂射,和頻繁的射,“射”的太多。“性”能會出問題!(o(∩_∩)o )。

相關文章