java核心基礎之java反射機制詳解

(一)什麼是反射?

反射就是將類的各個組成部分封裝爲其他對象。

在詳細瞭解反射機制之前,我們先來了解一下java代碼在計算機中的運行過程:

比如當我們編寫好一個類:Student.java,裏面包含學生的姓名和年齡,構造方法,其他方法。

第一個階段:Source階段

javac會把我們寫的代碼編譯成.class字節碼文件,保存在硬盤中,這個文件中保存着該類的類名,成員名,構造方法等等。

第二個階段:Class階段

Class階段會把字節碼文件中的信息轉化成class類對象,比如成員變量用Field[]保存,構造方法用Constructor[]保存,成員方法用Method[]保存

第三個階段:Runtime階段

通過new Student(),根據第二個階段的類對象創建出Student對象

這裏的第二個階段,將類的各個組成部分封裝爲其他對象就是反射機制。

反射的好處:

1.可以在程序運行過程中操作對象。

2.解耦,提高程序可擴展性。

(二)獲取字節碼Class對象的三種方式

獲取Class對象有三種方式,分別對應於前面的三個階段:

1.對應於第一個階段的方法是將字節碼文件加載進內存中:

class.forname("全類名");

2.第二個階段已經生成了class類對象,因此方法如下:

類名.class;

3.第三個階段生成了對象,方法如下:

對象.getclass();

注意:

同一個字節碼文件(.class)在一次程序運行過程中只會被加載一次,通過以上三種方法創建的class對象是同一個。

通過代碼演示:

新建Student類:

public class Student {
    private String name;
    private int age;
    //方便後期測試的成員變量
    public int a;
    public Student(){}
    public Student(String name, int age,int a) {
        this.name = name;
        this.age = age;
        this.a=a;
    }
    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;
    }

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

測試獲取Class對象的三種方式

public class reflectTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //方法一
        Class cls1 = Class.forName("com.sdxb.reflect.Student");
        System.out.println(cls1);
        //方法二
        Class cls2 = Student.class;
        System.out.println(cls2);
        //方法三
        Student student=new Student();
        Class cls3 = student.getClass();
        System.out.println(cls3);
        //判斷是否是同一對象
        System.out.println(cls1==cls2);
        System.out.println(cls1==cls3);
    }
}

(三)Class獲取對象方法

1.1 獲取成員變量

Field getField(String name) //獲取指定名稱public修飾的成員變量
Field[] getFields()  //獲取所有public修飾的成員變量
Field getDeclaredField(String name) //獲取指定名稱成員變量
Field[] getDeclaredFields()  //獲取所有成員變量

1.2 操作成員變量

Object get(Object obj)  //通過Field獲取對象
void set(Object obj, Object value)  //修改Field的值

通過代碼演示

public class FieldTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class cls = Class.forName("com.sdxb.reflect.Student");
        //1.獲取所有public的成員變量
        Field[] fields = cls.getFields();
        for (Field field:fields) {
            System.out.println(field);
        }
        //2.獲取指定名字的public成員變量
        Field a = cls.getField("a");
        Student student=new Student();
        //3.操作Field的方法,get和set
        System.out.println(a.get(student));
        a.set(student,10);
        System.out.println(student);
        //4.獲取所有成員變量
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field f : declaredFields) {
            System.out.println(f);
        }
    }
}

這裏的兩個操作Field的方法只能操作public修飾的變量,如果需要訪問其他修飾符修飾的元素,則要添加安全許可:

a.setAccessible(true);

2.1 獲取構造方法

Constructor<T> getConstructor(Class<?>... parameterTypes)  //根據參數不同獲取指定的public構造方法
Constructor<?>[] getConstructors()  //獲取所有public構造方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //根據參數不同獲取指定的構造方法
Constructor<?>[] getDeclaredConstructors() //獲取所有構造方法

2.2 操作構造方法

T newInstance(Object... initargs)  //創建對象

通過代碼演示其中一兩種操作:

public class reflectTest2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class cls = Class.forName("com.sdxb.reflect.Student");
        //有參構造方法
        Constructor constructor = cls.getConstructor(String.class, int.class, int.class);
        Object student = constructor.newInstance("sdxb", 24, 1);
        System.out.println(student);
        //無參構造方法
        Constructor constructor2 = cls.getConstructor();
        Object student2 = constructor2.newInstance();
        System.out.println(student2);
        //無參構造方法可以用下面的方式代替
        cls.newInstance();
    }
}

3.1 獲取成員方法

Method getMethod(String name, Class<?>... parameterTypes) //根據名稱和參數類型獲取public方法
Method[] getMethods()  //獲取所有public方法
Method getDeclaredMethod(String name, Class<?>... parameterTypes) //根據名稱和參數類型獲取方法
Method[] getDeclaredMethods()  //獲取所有方法

3.2 成員方法的操作

Object invoke(Object obj, Object... args)  //執行成員方法

爲了方便測試,我爲Student類增加了兩個成員方法:

public void run(){
    System.out.println("run");
}
public void run(int speed){
    System.out.println("run"+speed);
}

接着對成員方法的反射進行測試:

public class reflectTest3 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class cls = Student.class;
        Student student=new Student();
        //無參方法
        Method run = cls.getMethod("run");
        run.invoke(student);
        //帶參數方法
        Method run2=cls.getMethod("run", int.class);
        run.invoke(student,1);
    }
}

(四)總結

java的反射機制在框架中應用十分廣泛,被譽爲是框架的靈魂。原因是框架是一個半成品,我們無法通過new去創建框架中定義的類,因此反射起到了很大的作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章