Java 反射

官方文檔的描述,反射通常用于需要檢查或修改 Java 虛擬機中運行的應(yīng)用程序的運行時行為的程序。反射是一種功能強大的技術(shù),可以使應(yīng)用程序執(zhí)行不可能執(zhí)行的操作。

1. 獲取類對象的幾種方式

所有反射操作的入口都是 java.lang.Class。所以,我們首先要知道怎么去獲取到 Class,即類對象。根據(jù)代碼是否有權(quán)訪問對象、類的名稱、類型或現(xiàn)有類,有幾種方法可以獲得 Class。

1.1 Object.getClass()

如果有類的實例,則可以直接 getClass() 方法獲取對應(yīng)的 ClassgetClassObejct 類中的方法,所以只要是繼承自 Obejct 的類都可以調(diào)用。

例如:

public class Box {

    public void method() {
    }
}

public static void main(String[] args) {
     Box box = new Box();
     Class clazz = box.getClass();
}

1.2 ".class"

如果沒有類的實例,但是類是可以訪問到的,那么可以通過追加 .class 來獲得這個類。這也是獲取基本類型的最簡單方法。

例如:

Class clazzBox = Box.class;
Class clazzBoolean = boolean.class; //不能通過 getClass() 獲取

1.3 Class.forName()

如果類的包名和類名是已知的,那么可以通過靜態(tài)方法 Class.forName() 獲取類對象。此方法可能會拋出 ClassNotFoundException 異常。

例如:

try {
     Class box = Class.forName("testjava.Box");
} catch (ClassNotFoundException e) {
     e.printStackTrace();
}

1.4 TYPT 屬性

對于基本類型,最方便快捷地獲取 Class 的方式是通過追加 .class。但是,我們還可以通過另外一種方式獲取基本類型的類對象。
我們知道,每一種基本類型都會對應(yīng)的有一個包裝類,比如,boolean 的 包裝類為 Boolean、int 的包裝類為 Integer。每一個包裝類中,都一個靜態(tài)的 TYPE 變量,它就是用來存放包裝類對應(yīng)的基本類型的類對象的。
所以,我們可以這樣獲取基本類型的類對象:

Class clazzInt = Integer.TYPE;

if (clazzInt == int.class) { //int.class 和 Integer.TYPE 是相同的
    System.out.println("true");
}

2. 獲取構(gòu)造器實例化對象和屬性信息

2.1 構(gòu)造函數(shù)

構(gòu)造函數(shù)用于創(chuàng)建類的實例,完成一些初始化操作,構(gòu)造方法不能被繼承。
構(gòu)造函數(shù)可能包含名稱、修飾符、參數(shù)和一系列的異常等。可以通過 java.lang.reflect.Constructor 提供的方法獲取到相應(yīng)的信息。

public class Box {

    private Box() {
        System.out.println("***** Box()");
    }

    public Box(String name) {
        System.out.println("***** Box(name) name:" + name);
    }
}


public static void main(String[] args) {
        Class boxClass = Box.class;
        //獲取 Box 類的所有構(gòu)造方法
        Constructor[] allConstructor = boxClass.getDeclaredConstructors();
        for (Constructor constructor : allConstructor) {
            System.out.println("***** " + constructor.toGenericString());
            //獲取構(gòu)造方法的參數(shù)
            Type[] gpTypes = constructor.getGenericParameterTypes();
            for (int j = 0; j < gpTypes.length; j++) {
                System.out.println("***** GenericParameterType " + j + " :" + gpTypes[j]);
            }
            System.out.println("***** --------------------------");
        }
    }

打印

***** private testjava.Box()
***** --------------------------
***** public testjava.Box(java.lang.String)
***** GenericParameterType 0 :class java.lang.String
***** --------------------------

通過反射實例化對象,有兩種方式:

  • 直接使用 Class.newInstance() 實例化對象。
  • 先獲取到類的構(gòu)造方法 Constructor,然后通過 Constructor.newInstance() 實例化對象。

兩者相比,通常建議使用后者,因為前者調(diào)用的是無參數(shù)的構(gòu)造函數(shù)實例化對象,并且需要無參構(gòu)造函數(shù)是可訪問的。而后者對參數(shù)沒有要求,并且就算是 private 的也是可以調(diào)用的。

例如:

public class Box {

    private String name;

    private Box() {
        name = "Box";
        System.out.println("***** Box()");
    }

    public Box(String name) {
        this.name = name;
        System.out.println("***** Box(name) name:" + name);
    }

    public String getName() {
        return name;
    }
}

public static void main(String[] args) {
        Class boxClass = Box.class;
        //獲取 Box 類的所有構(gòu)造方法
        Constructor[] allConstructor = boxClass.getDeclaredConstructors();
        Constructor constorZeroParams = null; //無參構(gòu)造方法
        Constructor constorParams = null;     //有參構(gòu)造方法
        for (Constructor constructor : allConstructor) {
            if (constructor.getGenericParameterTypes().length == 0) {
                constorZeroParams = constructor;
            } else {
                constorParams = constructor;
            }
        }
        if (constorZeroParams != null) {
            constorZeroParams.setAccessible(true);
            try {
                Box box = (Box) constorZeroParams.newInstance();
                //Box box = Box.class.newInstance(); //會報異常 java.lang.IllegalAccessException: Class testjava.TestJava can not access a member of class testjava.Box with modifiers "private"
                System.out.println("name:" + box.getName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (constorParams != null) {
            constorParams.setAccessible(true);
            try {
                Box box = (Box) constorParams.newInstance("haha");
                System.out.println("name:" + box.getName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

打印結(jié)果:

***** Box()
name:Box
***** Box(name) name:haha
name:haha

2.2 方法

java.lang.reflect.Method 類提供了 API 去獲取方法的信息:修飾符、返回值、參數(shù)、注解信息和異常等。

public class Box {

    private String name;

    private Box() {
        name = "Box";
        System.out.println("***** Box()");
    }

    public Box(String name) {
        this.name = name;
        System.out.println("***** Box(name) name:" + name);
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("***** setName(name) name:" + name);
    }

    public String getName() {
        return name;
    }

    private void method1() {
        System.out.println("private 方法");
    }

    public void method2() {
        System.out.println("public 方法");
    }

    public int method3() {
        System.out.println("帶返回值 方法");
        return 0;
    }

    public <E extends RuntimeException> void method4() throws E {
        System.out.println("異常 方法");
    }

    public static void staticMethod() {
        System.out.println("靜態(tài) 方法");
    }
}
public static void main(String[] args) {
        Class boxClass = Box.class;
        Method[] allMethods = boxClass.getMethods();
        for (Method method : allMethods) {
            System.out.println("---------------------");
            //獲取所有的方法
            System.out.println("method: " + method.toGenericString());
            //參數(shù)類型
            Type[] gpTypes = method.getGenericParameterTypes();
            Class[] pTypes = method.getParameterTypes();
            for (int i = 0; i < gpTypes.length; i++) {
                System.out.println("pType: " + pTypes[i]);
                System.out.println("gpType: " + gpTypes[i]);
            }
            //異常類型
            Class<?>[] xType  = method.getExceptionTypes();
            Type[] gxType = method.getGenericExceptionTypes();
            for (int i = 0; i < xType.length; i++) {
                System.out.println("ExceptionType: "+ xType[i]);
                System.out.println("GenericExceptionType: " + gxType[i]);
            }
            //返回值
            Class<?> returnClazz = method.getReturnType();
            System.out.println("return class: " + returnClazz);
        }
        System.out.println("---------------------");

        //獲取指定的方法并使用
        try {
            Method method = boxClass.getDeclaredMethod("setName", String.class);
            method.setAccessible(true);
            //成員方法,需要先實例化一個對象
            Constructor constructor = null;
            Constructor[] constructors = boxClass.getDeclaredConstructors();
            for (Constructor cons : constructors) {
                if (cons.getGenericParameterTypes().length == 0) {
                    constructor = cons;
                    break;
                }
            }
            constructor.setAccessible(true);
            Box box = (Box) constructor.newInstance();
            method.invoke(box, "method");
            System.out.println("name: " + box.getName());

            //調(diào)用靜態(tài)函數(shù),不需要實例化對象
            Method staticMethod = boxClass.getDeclaredMethod("staticMethod");
            staticMethod.invoke(null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

打印結(jié)果:

---------------------
method: public java.lang.String testjava.Box.getName()
return class: class java.lang.String
---------------------
method: public void testjava.Box.setName(java.lang.String)
pType: class java.lang.String
gpType: class java.lang.String
return class: void
---------------------
method: public static void testjava.Box.staticMethod()
return class: void
---------------------
method: public int testjava.Box.method3()
return class: int
---------------------
method: public <E> void testjava.Box.method4() throws E
ExceptionType: class java.lang.RuntimeException
GenericExceptionType: E
return class: void
---------------------
method: public void testjava.Box.method2()
return class: void
---------------------
method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
pType: long
gpType: long
pType: int
gpType: int
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
pType: long
gpType: long
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public final void java.lang.Object.wait() throws java.lang.InterruptedException
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public boolean java.lang.Object.equals(java.lang.Object)
pType: class java.lang.Object
gpType: class java.lang.Object
return class: boolean
---------------------
method: public java.lang.String java.lang.Object.toString()
return class: class java.lang.String
---------------------
method: public native int java.lang.Object.hashCode()
return class: int
---------------------
method: public final native java.lang.Class<?> java.lang.Object.getClass()
return class: class java.lang.Class
---------------------
method: public final native void java.lang.Object.notify()
return class: void
---------------------
method: public final native void java.lang.Object.notifyAll()
return class: void
---------------------
***** Box()
***** setName(name) name:method
name: method
靜態(tài) 方法

屬性

同樣的,可以通過 java.lang.reflect.Field 獲取到類的所有屬性相關(guān)的信息。

public class Box {

    public static final String TAG = "Box";
    public String name = "box";
    private int age = 18;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
public static void main(String[] args) {
        Class boxClass = Box.class;
        //獲取所有屬性
        Field[] fields = boxClass.getFields();
        for (Field field : fields) {
            System.out.println("field: " + field.toGenericString());
        }

        try {
            //修改 public 的 name 變量
            Field nameField = boxClass.getField("name");
            //實例化一個 Box 對象
            Object box = boxClass.getConstructor().newInstance();
            //獲取 name 值
            String name = (String) nameField.get(box);
            System.out.println("name 修改前:" + name);
            //修改 name 值
            nameField.set(box, "filedName");
            name = (String) nameField.get(box);
            System.out.println("name 修改后:" + name);

            //修改 private 的 age 屬性
            Field ageField = boxClass.getDeclaredField("age");//getField 和 getDeclaredField 的區(qū)別是 前者只能獲取public的,后者是獲取所有的
            ageField.setAccessible(true); //設(shè)置訪問權(quán)限
            //獲取 age 的值
            int age = (int) ageField.get(box);
            System.out.println("age 修改前:" + age);
            //修改 age 的值
            ageField.set(box, 24);
            age = (int) ageField.get(box);
            System.out.println("age 修改后:" + age);

            //獲取靜態(tài)的變量 TAG 的值
            Field tagField = boxClass.getDeclaredField("TAG");
            String tag = (String) tagField.get(null); //不需要具體對象
            System.out.println("tag:" + tag);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
field: public static final java.lang.String testjava.Box.TAG
field: public java.lang.String testjava.Box.name
name 修改前:box
name 修改后:filedName
age 修改前:18
age 修改后:24
tag:Box



參考:

Trail: The Reflection API

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 深入理解Class對象 RRTI的概念以及Class對象作用 認識Class對象之前,先來了解一個概念,RTTI(...
    架構(gòu)師springboot閱讀 1,654評論 0 3
  • 一、概述 Java反射機制定義 Java反射機制是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類中的所有屬性和方法...
    CoderZS閱讀 1,699評論 0 25
  • 問題: 在運行時,對一個JAVA類,能否知道屬性和方法;能否調(diào)用它的任意方法? 答案是可以的,JAVA提供一種反射...
    糖寶_閱讀 800評論 0 1
  • 類加載機制 1 什么是反射 Java反射機制是在運行狀態(tài)中對于任意一個類,都能知道這個類的所以屬性和方法;對于任何...
    凱玲之戀閱讀 13,956評論 3 28
  • 愿晨光代替我,看著你的臉龐燦爛如花愿黑夜都穿不透我,擋住你的憂傷因為你在我的心里,是七月流火的清涼是冬雪的擁抱是春...
    ALsoon閱讀 218評論 0 0

友情鏈接更多精彩內(nèi)容