1. 程式人生 > >三、Java中的反射

三、Java中的反射

1、反射技術簡介

Java反射技術應用廣泛,它能夠配置:類的全限定名、方法和引數,完成物件的初始化,甚至可以反射某些方法。這樣就大大增強了Java的可配置性,Spring IOC的基本原理也是如此。

2、Java反射機制的定義

Java反射機制是指在執行狀態中,對於任意一個類,都能夠知道這個類的屬性和方法。對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。這種動態獲取的資訊以及動態呼叫方法的功能成為Java的反射機制。用一句話總結就是Java的反射機制可以在執行狀態中知道任意一個類的屬性和方法。

優點

通過配置就可以生成物件,可以解除程式的耦合性,非常靈活。而且在專案上線後,後期可以很方便的對專案功能進行更新。

缺點

執行慢,對程式的效能有一定的影響。因為使用反射基本上是一種解釋性操作。這類操作總是慢於直接執行相同的操作。

3、Java類和類型別

類是java.lang.Class類的例項物件,而Class是所有類的類。對於普通的物件,我們一般都會這樣建立和表示:

Person p = new Person();

上面說了,所有的類都是Class的物件,那麼如何表示呢,可不可以通過如下方式呢:

Class clazz = new Class();

但是我們檢視Class的原始碼時,是這樣寫的:

private Class(ClassLoader loader) {
        
// Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader;
}

可以看到構造器是私有的,只有JVM可以建立Class的物件,因此不可以像普通類一樣new一個Class物件,雖然我們不能new一個Class物件,但是卻可以通過已有的類得到一個Class物件,共有三種方式,如下:

//這說明任何一個類都有一個隱含的靜態成員變數class,這種方式是通過獲取類的靜態成員變數class得到的
Class clazz1 = Person.class;

//這種方式是通過一個類的物件的getClass()方法獲得的
Person p = new Person();
Class<? extends Person> clazz2 = p.getClass();

//這種方法是Class類呼叫forName方法,通過一個類的全量限定名獲得
Class<?> clazz3 = Class.forName("cn.zhuangxp.reflect.Person");

這裡的clazz1、clazz2、clazz3都是Class物件,他們是完全一樣的,而且有個學名,叫做Person的類型別。類型別就是類的型別,就是描述一個類是什麼,有那些屬性和方法。所以我們可以通過類的型別,來獲取一個類屬性和方法,並且呼叫它的屬性和方法,這就是反射的基礎。

舉個簡單的例子

Person類

package cn.zhuangxp.reflect;

/**
 * @author BEAN_BAG
 * @date 2018年4月29日
 * @description Person實體類
 */
public class Person {
    private String name;
    private int age;
    private String hobby;
    public String msg = "HelloWorld";
    public String phone = "13066612345";
    private String email = "[email protected]";

    public Person() {
    }

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    private Person(String name, int age, String hobby) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
    }

    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 getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", hobby='" + hobby + '\'' +
                '}';
    }

    public void say(String name, int age) {
        System.out.println("我叫:" + name + ",今年:" + age);
    }

    private void play(String hobby) {
        System.out.println("我的愛好是:" + hobby);
    }

}

測試類

public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //這說明任何一個類都有一個隱含的靜態成員變數class,這種方式是通過獲取類的靜態成員變數class得到的
        Class clazz1 = Person.class;
        System.out.println(clazz1.getName());

        //這種方式是通過一個類的物件的getClass()方法獲得的
        Person p = new Person();
        Class<? extends Person> clazz2 = p.getClass();
        System.out.println(clazz2.getName());

        //這種方法是Class類呼叫forName方法,通過一個類的全量限定名獲得
        Class<?> clazz3 = Class.forName("cn.zhuangxp.reflect.Person");
        System.out.println(clazz3.getName());

    }
}

結果

cn.zhuangxp.reflect.Person
cn.zhuangxp.reflect.Person
cn.zhuangxp.reflect.Person

4、Java反射的相關操作

獲取指定成員方法Method

public class Demo02 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person");
        Object instance = clazz.newInstance();

        /**
         * 返回某個類的指定的公共(public)成員方法
         */
        Method method1 = clazz.getMethod("say",String.class,int.class);
        method1.invoke(instance,"BEAN_BAG",23);

        /**
         * 返回某個類的指定已宣告方法
         */
        Method method2 = clazz.getDeclaredMethod("play", String.class);
        //取消Java許可權控制檢查
        method2.setAccessible(true);
        method2.invoke(instance,"乒乓球");

    }
}

結果

我叫:BEAN_BAG,今年:23
我的愛好是:乒乓球

獲取所有方法

public class Demo03 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person");
        /**
         * 返回某個類的所有公共(public)方法,,包括其繼承類的公共方法,
         * 也包括它所實現的介面的方法。
         */
        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            System.out.println(m.getName());
        }
        System.out.println("=================================================");

        /**
         * 返回某個類的所有方法,包括公共、保護、預設訪問和私有的方法。
         * 但是不包括繼承的方法,當然也包括它所實現的介面的方法。
         */
        Method[] methods1 = clazz.getDeclaredMethods();
        for (Method m : methods1){
            System.out.println(m.getName());
        }

    }
}

結果

toString
getName
setName
setMsg
say
setHobby
getMsg
setAge
getHobby
getAge
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll
=================================================
toString
getName
setName
play
setMsg
say
setHobby
getMsg
setAge
getHobby
getAge

獲取成員變數Field

public class Demo04 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person");
        Object instance = clazz.newInstance();
        /**
         * 獲得該類中指定的public成員變數,包括其父類變數
         */
        Field field = clazz.getField("msg");
        Object msg = field.get(instance);
        System.out.println(msg.toString());

        System.out.println("===============================================");

        /**
         * 獲得該類中所有的自身宣告的變數,不包括其父類的變數
         */
        Field field1 = clazz.getDeclaredField("email");
        field1.setAccessible(true);
        Object email = field1.get(instance);
        System.out.println(email.toString());
    }
}

結果

HelloWorld
===============================================
[email protected]163.com

獲取所有的成員變數

public class Demo05 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person");
        Object instance = clazz.newInstance();
        /**
         * 獲得該類所有的public成員變數,包括其父類變數
         */
        Field[] fields = clazz.getFields();
        for (Field f : fields){
            Object o = f.get(instance);
            System.out.println(o.toString());
        }
        System.out.println("===========================================");
        /**
         * 獲得該類自身宣告的所有變數,不包括其父類的變數
         */
        Field[] fields1 = clazz.getDeclaredFields();
        for (Field f : fields1){
            System.out.println(f.getName());
        }
    }
}

結果

HelloWorld
13066612345
===========================================
name
age
hobby
msg
phone
email

獲取構造器

public class Demo06 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person");
        Object instance = clazz.newInstance();

        /**
         * 獲得該類的public構造器
         */
        Constructor<?> constructor = clazz.getConstructor(String.class,int.class);
        Object o = constructor.newInstance("BEAN_BAG", 23);
        System.out.println(o);
        System.out.println("=========================================");
        /**
         * 獲得該類所有的構造器
         */
        Constructor<?> constructor1 = clazz.getDeclaredConstructor(String.class,int.class,String.class);
        constructor1.setAccessible(true);
        Object o1 = constructor1.newInstance("mm",22,"English");
        System.out.println(o1);
    }
}

結果

Person{name='BEAN_BAG', age=23, hobby='null'}
=========================================
Person{name='mm', age=22, hobby='English'}

獲取所有的構造器

public class Demo07 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("cn.zhuangxp.reflect.Person");
        Object instance = clazz.newInstance();
        /**
         * 獲得該類所有的public構造器
         */
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor c : constructors){
            System.out.println(c.getName());
        }
        System.out.println("====================================");
        /**
         * 獲得該類所有的構造器
         */
        Constructor<?>[] constructors1 = clazz.getDeclaredConstructors();
        for (Constructor c : constructors1){
            System.out.println(c.getName());
        }

    }
}

結果

cn.zhuangxp.reflect.Person
cn.zhuangxp.reflect.Person
====================================
cn.zhuangxp.reflect.Person
cn.zhuangxp.reflect.Person
cn.zhuangxp.reflect.Person

5、通過反射了解泛型的本質

Java中集合的泛型,是防止錯誤輸入的,只在編譯階段有效。如果繞過編譯期,到執行期就無效了。所以,我們可以通過Java的反射繞過泛型檢查,完整程式碼如下:

public class Demo08 {
    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList<String>();
        list.add("zhuang14");//允許新增
        //list.add(25);//新增int型別報錯,因為泛型為String
        //獲取list的類型別
        Class<? extends List> clazz = list.getClass();
        //獲取list集合的add方法
        Method addMethod = clazz.getMethod("add", Object.class);
        addMethod.invoke(list,25);
        //結果
        System.out.println(list);//[zhuang14, 25]

    }
}