Java反射機制詳解


作用

  反射的定義:在運行狀態中,能獲取任意一個類的所有方法和屬性;能調用一個對象的所有方法和屬性。這種動態獲取類信息和動態調用對象方法和屬性的功能就是Java的反射機制。

  注意定義中的措辭,是所有的方法和屬性,即使是私有的也能調用。所以功能是非常強大的。但在我們日常開發中很少會用到反射機制,因為正是這種強大的機制反而會破壞我們應用代碼的封裝性。日常中不用不代表就沒用,很多框架的設計其實都用到了反射機制。如果熟悉Spring的話不難發現,Spring中有大量關於反射的應用。比如我們在配置XML時就會被要求寫對應類的全限定名,在Spring的BeanFactor中就會讀取這些配置文件,並通過反射去創建Class對象。連接數據庫時,會要求指定驅動程序,比如com.mysql.jdbc.Driver,其實也通過反射機制加載的。

原理

  在此之前先要明白,在Java中萬物皆對象,某個類本身可以是個對象,類里面的結構,比如屬性、方法等也可以是個對象。在JVM進行類加載的過程中,在第一步也就是加載步驟,會在內存中生成一個代表該類的Class對象。需要注意這個Class對象和該類的實例對象不是同一個東西。Class對象的類型就是Class,大家可以看看Class的API或者直接看它的源碼。我們使用Class.forName("類的全限定名")時其實就是獲取這個類的Class對象。具體的API可以看下面。通過這個Class對象我們就可以獲取這個類的屬性、方法、注解等信息。那么是如何獲取這些信息的?首先Class這個類提供了獲取這些信息的方法,既然提供了獲取的方法,那說明這些信息肯定存在於某些地方。還是得從JVM層面來說,一個類的靜態成員,即靜態方法和屬性都是存在方法區的,這些成員在類加載時就生成了,所以它們與該類的實例對象沒有什么關系,只與這個類本身也就是Class對象相關。而那些非靜態成員會隨着實例對象的創建一起存進Java堆當中,所以我們獲取這些非靜態成員時,一定是根據某個實例對象來獲取的。這兩種情況會在下面的示例代碼中展現出來。

API

  下面的API摘自:https://www.jianshu.com/p/9be58ee20dee,感興趣的可以去看看。

  與Java反射相關的類如下:

類名

用途

Class類

代表類的實體,在運行的Java應用程序中表示類和接口

Field類

代表類的成員變量(成員變量也稱為類的屬性)

Method類

代表類的方法

Constructor類

代表類的構造方法

 

  Class類

  Class代表類的實體,在運行的Java應用程序中表示類和接口。在這個類中提供了很多有用的方法,這里對他們簡單的分類介紹。

  • 獲得類相關的方法

方法

用途

asSubclass(Class<U> clazz)

把傳遞的類的對象轉換成代表其子類的對象

Cast

把對象轉換成代表類或是接口的對象

getClassLoader()

獲得類的加載器

getClasses()

返回一個數組,數組中包含該類中所有公共類和接口類的對象

getDeclaredClasses()

返回一個數組,數組中包含該類中所有類和接口類的對象

forName(String className)

根據類名返回類的對象

getName()

獲得類的完整路徑名字

newInstance()

創建類的實例

getPackage()

獲得類的包

getSimpleName()

獲得類的名字

getSuperclass()

獲得當前類繼承的父類的名字

getInterfaces()

獲得當前類實現的類或是接口

 

  • 獲得類中屬性相關的方法

方法

用途

getField(String name)

獲得某個公有的屬性對象

getFields()

獲得所有公有的屬性對象

getDeclaredField(String name)

獲得某個屬性對象

getDeclaredFields()

獲得所有屬性對象

 

  • 獲得類中注解相關的方法

方法

用途

getAnnotation(Class<A> annotationClass)

返回該類中與參數類型匹配的公有注解對象

getAnnotations()

返回該類所有的公有注解對象

getDeclaredAnnotation(Class<A> annotationClass)

返回該類中與參數類型匹配的所有注解對象

getDeclaredAnnotations()

返回該類所有的注解對象

 

  • 獲得類中構造器相關的方法

方法

用途

getConstructor(Class...<?> parameterTypes)

獲得該類中與參數類型匹配的公有構造方法

getConstructors()

獲得該類的所有公有構造方法

getDeclaredConstructor(Class...<?> parameterTypes)

獲得該類中與參數類型匹配的構造方法

getDeclaredConstructors()

獲得該類所有構造方法

 

  • 獲得類中方法相關的方法

方法

用途

getMethod(String name, Class...<?> parameterTypes)

獲得該類某個公有的方法

getMethods()

獲得該類所有公有的方法

getDeclaredMethod(String name, Class...<?> parameterTypes)

獲得該類某個方法

getDeclaredMethods()

獲得該類所有方法

 

  • 類中其他重要的方法

方法

用途

isAnnotation()

如果是注解類型則返回true

isAnnotationPresent(Class<? extends Annotation> annotationClass)

如果是指定類型注解類型則返回true

isAnonymousClass()

如果是匿名類則返回true

isArray()

如果是一個數組類則返回true

isEnum()

如果是枚舉類則返回true

isInstance(Object obj)

如果obj是該類的實例則返回true

isInterface()

如果是接口類則返回true

isLocalClass()

如果是局部類則返回true

isMemberClass()

如果是內部類則返回true

 

  Field類

  Field代表類的成員變量(成員變量也稱為類的屬性)。

方法

用途

equals(Object obj)

屬性與obj相等則返回true

get(Object obj)

獲得obj中對應的屬性值

set(Object obj, Object value)

設置obj中對應屬性值

 

  Method類

  Method代表類的方法。

方法

用途

invoke(Object obj, Object... args)

傳遞object對象及參數調用該對象對應的方法

 

  Constructor類

  Constructor代表類的構造方法。

方法

用途

newInstance(Object... initargs)

根據傳遞的參數創建類的對象

 

示例

  首先創建一個被反射的類:

public class People {
    //靜態屬性
    public static String Version="1.1.1";
    private String Name;
    private Integer Age;
    private String Habit;
    //公共構造函數
    public People(String name, Integer age, String habit) {
        Name = name;
        Age = age;
        Habit = habit;
    }
    //私有構造函數
    private People(String name){
        Name=name;
    }

    public People(){

    }
    //私有方法
    private void isPrivate(String data){
        System.out.println("這是個私有方法,傳入的參數為:"+data);
    }
    //公有方法
    public String getName() {
        return Name;
    }
    //靜態方法
    public static void StaticVoid(){
        System.out.println("這是一個靜態方法");
    }
}

  接下來通過Java提供的反射方法獲取這個類的構造函數、方法、屬性:

public class reflectionTest {
    public static void main(String[] args) {
        try {
            System.out.println("----------------------獲取Class對象-----------------------");
            //因為我這里People和reflectionTest在同一包下,如果不在同一包下需要傳入完整的類路徑
            Class clazz = Class.forName("People");
            System.out.println(clazz);
            /**
             * 還有兩種獲取Class對象的方法,這幾種方法獲取的Class對象都是同一個對象:
             * 一般都會用foeName的方式,因為下面第一種方法需要導包
             * 第二種方法,對象都創建出來了,除非是要調用私有的東西,那我還反射個毛。
             * 1.Class clazz=People.class;
             * 2.People p=new People();
             *   Class clazz=p.getClass();
             * */

            System.out.println("----------------------獲取構造函數對象-----------------------");
            //獲取所有構造函數,不包括私有的,如果想包括私有就用getDeclaredConstructors
            Constructor[] constructors = clazz.getConstructors();
            for (Constructor c : constructors) {
                System.out.println(c);
            }
            //獲取私有構造函數並調用(String.class為構造函數參數類型)
            Constructor constructor = clazz.getDeclaredConstructor(String.class);
            //忽略訪問修飾符,如果是共有的就不需要這一步
            constructor.setAccessible(true);
            //根據這個構造函數創建一個People實例對象
            People people = (People) constructor.newInstance("張三");
            //最后通過對象輸出剛剛設置的姓名
            System.out.println(people.getName());

            System.out.println("----------------------獲取方法對象-----------------------");
            //獲取所有公共方法,包括父類的方法,比如Object的equals,如果想包含私有方法就用getDeclaredMethods,但只有本類的方法
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                System.out.println(method);
            }
            //獲取一個私有方法並調用
            Method method = clazz.getDeclaredMethod("isPrivate", String.class);
            //忽略訪問修飾符
            method.setAccessible(true);
            //訪問私有方法,如果這個私有方法有返回值用一個變量接收就行了。
            //通過invoke調用該私有方法,除了要傳入這個方法本來的參數,比如這里的耶low,還需要一個該方法對應類的對象,我們就用上面反射獲取的people對象
            method.invoke(people, "耶low");
            //獲取靜態方法並調用,發現調用靜態方法不需要實例對象,這也符合原理中所說的。
            Method method2=clazz.getMethod("StaticVoid");
            method2.invoke(null);

            System.out.println("----------------------獲取屬性對象-----------------------");
            //獲取本類的所有屬性,包括私有的
            Field[] fields=clazz.getDeclaredFields();
            for(Field field:fields){
                System.out.println(field);
            }
            //獲取私有屬性並調用
            Field field=clazz.getDeclaredField("Name");
            field.setAccessible(true);
            //設置這個私有屬性
            field.set(people,"李四");
            //然后獲取一下Name,看看設置成功沒
            System.out.println(people.getName());
            //獲取靜態屬性
            Field field2=clazz.getField("Version");
            //輸出靜態屬性的值
            System.out.println(field2.get(clazz));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM