1. 程式人生 > 實用技巧 >Java學習筆記(5)反射

Java學習筆記(5)反射

反射是框架設計的靈魂

(使用的前提條件:必須先得到代表的位元組碼的Class,Class類用於表示.class檔案(位元組碼))

一、反射的概述

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

要想解剖一個類,必須先要獲取到該類的位元組碼檔案物件。而解剖使用的就是Class類中的方法.所以先要獲取到每一個位元組碼檔案對應的Class型別的物件.

以上的總結就是什麼是反射

反射就是把java類中的各種成分對映成一個個的Java物件

例如:一個類有:成員變數、方法、構造方法、包等等資訊,利用反射技術可以對一個類進行解剖,把個個組成部分對映成一個個物件。

(其實:一個類中這些成員方法、構造方法、在加入類中都有一個類來描述)

如圖是類的正常載入過程:反射的原理在與class物件。

熟悉一下載入的時候:Class物件的由來是將class檔案讀入記憶體,併為之建立一個Class物件。

二、通過反射獲取Class類的例項三種方法

1.建立Person類,定義Move Study兩個介面,定義Student類繼承了Person類來實現Move Study兩個介面

Peron類

public class Person {
    
public String name; int age; }

Student類

package Java基礎.反射;

public class Student extends Person implements Move,Study{

    public Student(){
        System.out.println("呼叫的是public Student()");
    }

    public Student(String school){
        this.school = school;
        System.out.println(
"呼叫的是public Student(String school)"); } private Student(String name,int age){ this.name = name; this.age = age; System.out.println("呼叫的是private Student(String name,int age)"); } public String school; private String privateField; public void showInfo(){ System.out.println("學校:"+this.school); } private void test(String name){ System.out.println("這是私有方法test(String name)"); } public String getSchool(){ return this.school; } public void setInfo(int age){ this.age = age; System.out.println("這是呼叫過載方法 setInfo(int age)"); } public void setInfo(String name,String school){ this.name = name; this.school = school; System.out.println("這是setInfo(String name,String school)方法"); } @Override public void moveType() { System.out.println("坐地鐵去上班"); } @Override public void studyInfo() { System.out.println("學習java反射"); } }

Move 介面

package Java基礎.反射;

public interface Move {
    void moveType();
}

Study介面

package Java基礎.反射;

public interface Study {
    void studyInfo();
}

Test 測試類:獲取Class類的例項3種方法

package Java基礎.反射;

public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        Class clazz = p.getClass();// clazz物件中包含物件p所屬的Person類所有的資訊

        //獲取Class類的例項3種方法
        Class c0 = Person.class;// 通過類名.class建立指定類的例項
        Class c1 = p.getClass();// 通過一個類的例項物件的getClass()方法
        try {
            // 通過Class的靜態方法forName(String className)來獲取一個class例項
            // forName(String className) 引數為你要獲取的class例項的類的全路徑(包名.類名)
            Class c2 = Class.forName("Java基礎.反射.Person");// 此種方法最常用
            System.out.println(c2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

三、通過反射獲取構造器、介面、建立物件、方法、屬性、指定方法、指定屬性

package Java基礎.反射;
import java.lang.reflect.*;
import java.util.Arrays;

public class Test1 {
    public static void main(String[] args) {
        //通過forname()獲取Class 例項
        try {
            Class clazz = Class.forName("Java基礎.反射.Student");// 獲取Student類的Class例項

            Class superClass = clazz.getSuperclass();// 獲取父類
            System.out.println(superClass.getName());

            Class[] interfaces = clazz.getInterfaces();// 獲取當前類的所有介面
            for(Class c:interfaces){
                System.out.println("介面:"+c.getName());
            }

            Constructor[] cons = clazz.getConstructors();// 獲取類的公有構造方法
            System.out.println(Arrays.toString(cons));
            for(Constructor c:cons){
                System.out.println("構造方法:"+c.getName());// 獲取方法名稱
                // getModifiers(),返回數字1代表public,數字2代表 private
                System.out.println("構造方法的修飾符:"+c.getModifiers());// 獲取方法修飾符
                Class[] paramsClazz = c.getParameterTypes();// 獲取引數型別
                for(Class pc:paramsClazz){
                    System.out.println("引數型別"+pc.getName());
                }

            }
            System.out.println("--------------------------");
            Constructor[] cons1 = clazz.getDeclaredConstructors(); // 獲取類的所有構造方法,包含公有和私有
            System.out.println(Arrays.toString(cons1));
            for(Constructor c:cons1){
                System.out.println("構造方法1:"+c.getName());// 獲取方法名稱
                System.out.println("構造方法的修飾符1:"+c.getModifiers());// 獲取方法修飾符
                Class[] paramsClazz = c.getParameterTypes();// 獲取引數型別
                for(Class pc:paramsClazz){
                    System.out.println("引數型別1"+pc.getName());
                }
            }

            // 使用反射的構造方法來建立物件
            try {

                Object obj = clazz.newInstance();// 相當於呼叫Student的無參公有的構造方法
                Student stu = (Student) obj;// 將object強制轉換為Student

                Constructor c1 = clazz.getConstructor(String.class);// 指定獲取有一個引數並且為String型別的公有構造方法,必須使用xxx.class格式
                Student stu1 = (Student) c1.newInstance("第一中學");// newInstance例項化物件
                System.out.println(stu1.school);

                // 通過反射機制,可以強制呼叫私有的構造方法
                Constructor c2 = clazz.getDeclaredConstructor(String.class,int.class);// 指定有兩個引數並且為String和int型別
                c2.setAccessible(true);// 解除私有的封裝,然後就可以呼叫私有方法了
                Student stu2 = (Student) c2.newInstance("Changsha",12);
                System.out.println(stu2.name);

            } catch (Exception e) {
                e.printStackTrace();
            }

            // 獲取類的方法
//            Method[] methods = clazz.getMethods();// 獲取類的所有公有方法,包含了類自有的方法(如toString等方法)
            Method[] methods = clazz.getDeclaredMethods();// 獲取類的宣告的所有方法,包含私有和公有,不包含類自有的方法
            for(Method m:methods){
                System.out.println("方法名:"+m.getName());
                System.out.println("返回值型別:"+m.getReturnType());
                System.out.println("修飾符:"+m.getModifiers());

                Class[] pcs = m.getParameterTypes();// 獲取引數的型別,是一個數組,方法有幾個引數,資料就有幾個引數型別
                if(pcs != null && pcs.length >0){
                    for(Class pc:pcs){
                        System.out.println("引數型別:"+pc.getName());
                    }
                }
                System.out.println("=======================");
            }

            // 獲取類的屬性
//            Field[] fs = clazz.getFields();// 獲取類的公有屬性,包含父類的公有屬性
            Field[] fs = clazz.getDeclaredFields();// 獲取本類(不包含父類的屬性)的所有屬性,包含私有
            for(Field f:fs){
                System.out.println("修飾符:"+f.getModifiers());
                System.out.println("屬性的型別:"+f.getType());
                System.out.println("屬性的名稱:"+f.getName());
            }

            // 獲取類所在的包
            Package p = clazz.getPackage();// 獲取
            System.out.println("包的名稱:"+p.getName());

            /*
            * 注意:下面不論是反射呼叫setInfo還是test方法
            * 都是呼叫的obj物件的方法,obj物件時間上就是Stutdent物件
            *
            * */
            // 呼叫指定公有方法
            Constructor con = clazz.getConstructor();// 獲取無參構造方法
            Object obj = con.newInstance();// 使用無參構造 建立物件
            Method m = clazz.getMethod("setInfo", String.class, String.class);// 得到名稱為setInfo,引數是String,String
            m.invoke(obj, "zhangsa","第一中學");// 通過invoke方法呼叫,引數1 需要例項化的物件,引數2 方法所有的實際引數

            // 呼叫指定私有方法
            Method m1 = clazz.getDeclaredMethod("test", String.class);//得到名稱為test,引數是String
            m1.setAccessible(true); // 解除私有封裝,強制呼叫私有方法
            m1.invoke(obj,"李四");

            // 呼叫指定過載方法
            Method m2 = clazz.getMethod("setInfo", int.class);//setInfo 過載方法
            m2.invoke(obj, 1);

            // 呼叫有返回值方法,無入參型別
            Method m3 = clazz.getMethod("getSchool");// 獲取方法名為getSchool,沒有入參
            String school = (String)m3.invoke(obj);// 呼叫有返回值方法,但是沒有引數;由於m3.invoke(obj)返回Object型別,需要通過String強制轉換
            System.out.println(school);

            // 呼叫指定公有屬性
            Constructor con1 = clazz.getConstructor();
            Student stu1 = (Student)con1.newInstance();
            Field f1 = clazz.getField("school"); // 獲取名稱為school的屬性
            f1.set(stu1, "第三中學");// 對stu1物件的school 屬性設定值
            String school1 = (String)f1.get(stu1); // 獲取stu1物件的school屬性的值
            System.out.println(school1);

            // 呼叫指定私有屬性
            Field f2 = clazz.getDeclaredField("privateField");
            f2.setAccessible(true);// 強制解除私有封裝
            f2.set(obj, "測試私有屬性");
            String name = (String)f2.get(obj);
            System.out.println(name);


        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}