Java反射機(jī)制:跟著代碼學(xué)反射

微信搜索:碼農(nóng)StayUp
主頁(yè)地址:https://gozhuyinglong.github.io
源碼分享:https://github.com/gozhuyinglong/blog-demos

1. 前言

在OOP的世界里,萬(wàn)物皆對(duì)象灵临。也就是說(shuō),我們可以將任何東西抽象成一個(gè)對(duì)象。

比如人洪鸭,可以抽象成一個(gè)Person類了牛,通過(guò)new Person()來(lái)實(shí)例化一個(gè)對(duì)象;再比如鴨子,可以抽象成一個(gè)Duck類艘绍,也可以對(duì)其進(jìn)行實(shí)例化……那么這一個(gè)個(gè)類本身是不是也可以抽象成一個(gè)類呢?Java提供了一個(gè)特殊的類Class秫筏,用來(lái)描述類的內(nèi)部信息诱鞠,是反射的核心類。

下圖是本篇講述內(nèi)容:

2. Java反射機(jī)制概述

Java反射(Reflection)允許應(yīng)用程序在運(yùn)行時(shí)借助于反射API这敬,來(lái)獲取所有類或接口的內(nèi)部信息航夺,并且能直接操作任意對(duì)象的內(nèi)部屬性及方法。反射機(jī)制的核心類為java.lang.Class崔涂。

  • 類加載完后晶密,會(huì)在堆內(nèi)存的方法區(qū)中產(chǎn)生一個(gè)Class類型的對(duì)象侨赡。
  • Class類沒(méi)有公開的構(gòu)造函數(shù)淡诗,是由類加載器的defineClass方法構(gòu)造而成痘番。所以Class對(duì)象不是“new”出來(lái)的,而是通過(guò)方法來(lái)獲取的蝙茶。
  • 這個(gè)Class對(duì)象具有類的完整結(jié)構(gòu)信息艺骂,并且一個(gè)類只有一個(gè)Class對(duì)象。

3. 獲取Class對(duì)象

獲取Class對(duì)象有以下四種方式:

  1. 通過(guò)類對(duì)象獲仁ⅰ彻亲;
  2. 通過(guò)類直接調(diào)用class獲仍谐吮廉;
  3. 通過(guò)Class.forName獲取畸肆;
  4. 通過(guò)類加載器獲取宦芦。

下面使用代碼展示獲取 Person 類的Class對(duì)象的四種方式:

@Test
public void testClassFor() {

    // 1.通過(guò)類實(shí)例獲取
    Person person = new Person();
    Class<? extends Person> clazz1 = person.getClass();
    System.out.println("01 - " + clazz1);

    // 2.通過(guò)類直接調(diào)用class獲取
    Class<Person> clazz2 = Person.class;
    System.out.println("02 - " + clazz2);

    // 3.通過(guò)Class.forName獲取
    Class<?> clazz3 = null;
    try {
        clazz3 = Class.forName("io.github.gozhuyinglong.reflection.Person");
    } catch (ClassNotFoundException e) {
        // 當(dāng)找不到指定類時(shí),會(huì)拋出此異常
        e.printStackTrace();
    }
    System.out.println("03 - " + clazz3);

    // 4.通過(guò)類加載器獲取
    ClassLoader classLoader = this.getClass().getClassLoader();
    Class<?> clazz4 = null;
    try {
        clazz4 = classLoader.loadClass("io.github.gozhuyinglong.reflection.Person");
    } catch (ClassNotFoundException e) {
        // 當(dāng)找不到指定類時(shí)轴脐,會(huì)拋出此異常
        e.printStackTrace();
    }
    System.out.println("04 - " + clazz4);

    // hashCode相等调卑,說(shuō)明這四種方式獲取的是同一個(gè)實(shí)例
    System.out.println("05 - " + clazz1.hashCode());
    System.out.println("06 - " + clazz2.hashCode());
    System.out.println("07 - " + clazz3.hashCode());
    System.out.println("08 - " + clazz4.hashCode());

}

輸出結(jié)果:

01 - class io.github.gozhuyinglong.reflection.Person
02 - class io.github.gozhuyinglong.reflection.Person
03 - class io.github.gozhuyinglong.reflection.Person
04 - class io.github.gozhuyinglong.reflection.Person
05 - 721748895
06 - 721748895
07 - 721748895
08 - 721748895

通過(guò)上面的輸出結(jié)果可以看出抡砂,這四個(gè)Class對(duì)象的hashCode相同,說(shuō)明使用這四種方式獲取的是同一個(gè)對(duì)象恬涧。

4. 一些特殊的類和接口的Class對(duì)象

在源碼注釋中提到一些特殊的類和接口:

  • 枚舉是一種類注益。
  • 注解是一種接口。
  • 數(shù)組也屬于一個(gè)反映為Class對(duì)象的類溯捆。具有相同元素類型和維數(shù)的數(shù)組丑搔,也具有相同的Class對(duì)象(也就是說(shuō),元素類型不同提揍,或數(shù)組維數(shù)不同啤月,其Class對(duì)象也不同)。
  • 原始Java類型(boolean, byte, char, short, int, long, float,double)和關(guān)鍵字 void 也表示為Class對(duì)象劳跃。

下面通過(guò)代碼來(lái)驗(yàn)證:

@Test
public void testClassOther() {

    // 枚舉是一種類
    Class<PersonEnum> clazz1 = PersonEnum.class;
    System.out.println("01 - " + clazz1);

    // 注解是一種接口
    Class<PersonAnnotation> clazz2 = PersonAnnotation.class;
    System.out.println("02 - " + clazz2);

    // 數(shù)組也屬于一個(gè)反應(yīng) Class 實(shí)例的類
    Person[] personArray3 = new Person[1];
    Class<? extends Person[]> clazz3 = personArray3.getClass();
    System.out.println("03 - " + clazz3);

    // 具有相同元素類型和維數(shù)的數(shù)組谎仲,也具有相同的 Class 實(shí)例
    Person[] personArray4 = new Person[4];
    Class<?> clazz4 = personArray4.getClass();

    Person[][] personArray5 = new Person[1][];
    Class<?> clazz5 = personArray5.getClass();

    // 兩個(gè)一維數(shù)組的 hashCode 相等,說(shuō)明是同一實(shí)例
    System.out.println("04 - " + clazz3.hashCode());
    System.out.println("05 - " + clazz4.hashCode());
    // 一維數(shù)組與二維數(shù)組的 hashCode 不相等刨仑,說(shuō)明不是同一實(shí)例
    System.out.println("06 - " + clazz5.hashCode());

    // 原始 Java 類型和關(guān)鍵字 void 也表示為 Class 實(shí)例
    Class<Integer> clazz6 = int.class;
    System.out.println("07 - " + clazz6);

    Class<Double> clazz7 = double.class;
    System.out.println("08 - " + clazz7);

    Class<Void> clazz8 = void.class;
    System.out.println("09 - " + clazz8);

}

輸出結(jié)果:

01 - class io.github.gozhuyinglong.reflection.PersonEnum
02 - interface io.github.gozhuyinglong.reflection.PersonAnnotation
03 - class [Lio.github.gozhuyinglong.reflection.Person;
04 - 721748895
05 - 721748895
06 - 1642534850
07 - int
08 - double
09 - void

通過(guò)輸出結(jié)果可以看出郑诺,確如源碼中描述那樣。

5. Java反射API

Java提供了一套反射API杉武,該API由Class類與java.lang.reflect類庫(kù)組成间景。該類庫(kù)包含了FieldMethod艺智、Constructor等類倘要。這些類型的對(duì)象是由JVM在運(yùn)行時(shí)創(chuàng)建的,用以表示未知類里對(duì)應(yīng)的成員十拣。

反射允許以編程的方式訪問(wèn)已加載類的字段封拧、方法和構(gòu)造函數(shù)信息,并在安全限制內(nèi)利用反射對(duì)其進(jìn)行操作夭问。

下面將介紹一些常用的類:

5.1 Class(類)

java.lang.Class類用來(lái)描述類的內(nèi)部信息泽西,Class的實(shí)例可以獲取類的包、注解缰趋、修飾符捧杉、名稱、超類秘血、接口等味抖。

@Test
public void testClass() throws Exception {
    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");

    // 獲取該類所在包路徑
    Package aPackage = clazz.getPackage();
    System.out.println("01 - " + aPackage);

    // 獲取該類上所有注解
    Annotation[] declaredAnnotations = clazz.getDeclaredAnnotations();
    for (Annotation temp : declaredAnnotations) {
        System.out.println("02 - " + temp);
    }

    // 獲取類上的修飾符
    int modifiers = clazz.getModifiers();
    String modifier = Modifier.toString(modifiers);
    System.out.println("03 - " + modifier);

    // 獲取類名稱
    String name = clazz.getName();
    System.out.println("04 - " + name);
    // 獲取簡(jiǎn)單類名
    String simpleName = clazz.getSimpleName();
    System.out.println("05 - " + simpleName);

    // 獲取直屬超類
    Type genericSuperclass = clazz.getGenericSuperclass();
    System.out.println("06 - " + genericSuperclass);

    // 獲取直屬實(shí)現(xiàn)的接口
    Type[] genericInterfaces = clazz.getGenericInterfaces();
    for (Type temp : genericInterfaces) {
        System.out.println("07 - " + temp);
    }

}

輸出結(jié)果:

01 - package io.github.gozhuyinglong.reflection
02 - @io.github.gozhuyinglong.reflection.PersonAnnotation()
03 - public final
04 - io.github.gozhuyinglong.reflection.Person
05 - Person
06 - class io.github.gozhuyinglong.reflection.PersonParent
07 - interface io.github.gozhuyinglong.reflection.PersonInterface

5.2 Constructor(構(gòu)造函數(shù))

java.lang.reflect.Constructor提供了類的構(gòu)造函數(shù)信息』伊福可以獲取構(gòu)造函數(shù)上的注解信息仔涩、參數(shù)類型等。

@Test
public void testConstructor() throws Exception {
    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");

    // 獲取一個(gè)聲明為 public 構(gòu)造函數(shù)實(shí)例
    Constructor<?> constructor1 = clazz.getConstructor(String.class, int.class, PersonEnum.class);
    System.out.println("01 - " + constructor1);

    // 獲取所有聲明為 public 構(gòu)造函數(shù)實(shí)例
    Constructor<?>[] constructorArray1 = clazz.getConstructors();
    for (Constructor<?> constructor : constructorArray1) {
        System.out.println("02 - " + constructor);
    }

    // 獲取一個(gè)聲明的構(gòu)造函數(shù)實(shí)例
    Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class);
    System.out.println("03 - " + constructor2);

    // 獲取所有聲明的構(gòu)造函數(shù)實(shí)例
    Constructor<?>[] constructorArray2 = clazz.getDeclaredConstructors();
    for (Constructor<?> constructor : constructorArray2) {
        System.out.println("04 - " + constructor);
    }

    // 根據(jù)構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例
    Object o1 = constructor1.newInstance("楊過(guò)", 25, PersonEnum.MAN);
    System.out.println("05 - " + o1);

    // 將構(gòu)造函數(shù)的可訪問(wèn)標(biāo)志設(shè)為 true 后粘舟,可以通過(guò)私有構(gòu)造函數(shù)創(chuàng)建實(shí)例
    constructor2.setAccessible(true);
    Object o2 = constructor2.newInstance("小龍女");
    System.out.println("06 - " + o2);

    // 獲取該構(gòu)造函數(shù)上的所有注解
    Annotation[] annotations = constructor1.getDeclaredAnnotations();
    for (Annotation annotation : annotations) {
        System.out.println("07 - " + annotation);
    }

    // 獲取該構(gòu)造函數(shù)上的所有參數(shù)類型
    Type[] genericParameterTypes = constructor1.getGenericParameterTypes();
    for (Type genericParameterType : genericParameterTypes) {
        System.out.println("08 - " + genericParameterType);
    }

}

輸出結(jié)果:

01 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int,io.github.gozhuyinglong.reflection.PersonEnum)
02 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int,io.github.gozhuyinglong.reflection.PersonEnum)
02 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int)
02 - public io.github.gozhuyinglong.reflection.Person()
03 - private io.github.gozhuyinglong.reflection.Person(java.lang.String)
04 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int,io.github.gozhuyinglong.reflection.PersonEnum)
04 - public io.github.gozhuyinglong.reflection.Person(java.lang.String,int)
04 - private io.github.gozhuyinglong.reflection.Person(java.lang.String)
04 - public io.github.gozhuyinglong.reflection.Person()
05 - Person{name='楊過(guò)', age=25, sex='MAN'}
06 - Person{name='小龍女', age=0, sex='null'}
07 - @io.github.gozhuyinglong.reflection.PersonAnnotation()
08 - class java.lang.String
08 - int
08 - class io.github.gozhuyinglong.reflection.PersonEnum

5.3 Field(屬性)

java.lang.reflect.Field提供了類的屬性信息熔脂∨逖校可以獲取屬性上的注解、修飾符霞揉、屬性類型旬薯、屬性名等。

@Test
public void testField() throws Exception {

    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");

    // 獲取一個(gè)該類或父類中聲明為 public 的屬性
    Field field1 = clazz.getField("hobby");
    System.out.println("01 - " + field1);

    // 獲取該類及父類中所有聲明為 public 的屬性
    Field[] fieldArray1 = clazz.getFields();
    for (Field field : fieldArray1) {
        System.out.println("02 - " + field);
    }

    // 獲取一個(gè)該類中聲明的屬性
    Field field2 = clazz.getDeclaredField("name");
    System.out.println("03 - " + field2);

    // 獲取該類中所有聲明的屬性
    Field[] fieldArray2 = clazz.getDeclaredFields();
    for (Field field : fieldArray2) {
        System.out.println("04 - " + field);
    }

    // 獲取該屬性上的所有注解
    Annotation[] declaredAnnotations = field2.getDeclaredAnnotations();
    for (Annotation declaredAnnotation : declaredAnnotations) {
        System.out.println("05 - " + declaredAnnotation);
    }

    // 獲取修飾符
    String modifier = Modifier.toString(field2.getModifiers());
    System.out.println("06 - " + modifier);

    // 獲取屬性類型适秩,返回類對(duì)象
    Class<?> type = field2.getType();
    System.out.println("07 - " + type);
    // 獲取屬性類型袍暴,返回Type對(duì)象
    Type genericType = field2.getGenericType();
    System.out.println("08 - " + genericType);

    // 獲取屬性名稱
    String name = field2.getName();
    System.out.println("09 - " + name);

}

輸出結(jié)果:

01 - public java.lang.String io.github.gozhuyinglong.reflection.PersonParent.hobby
02 - public int io.github.gozhuyinglong.reflection.Person.height
02 - public java.lang.String io.github.gozhuyinglong.reflection.PersonParent.hobby
03 - private java.lang.String io.github.gozhuyinglong.reflection.Person.name
04 - private java.lang.String io.github.gozhuyinglong.reflection.Person.name
04 - private int io.github.gozhuyinglong.reflection.Person.age
04 - public int io.github.gozhuyinglong.reflection.Person.height
05 - @io.github.gozhuyinglong.reflection.PersonAnnotation()
06 - private
07 - class java.lang.String
08 - class java.lang.String
09 - name

5.4 Method(方法)

java.lang.reflect.Method提供了類的方法信息×ブⅲ可以獲取方法上的注解政模、修飾符、返回值類型蚂会、方法名稱淋样、所有參數(shù)。

@Test
public void testMethod() throws Exception {

    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");

    // 獲取一個(gè)該類及父類中聲明為 public 的方法胁住,需要指定方法的入?yún)㈩愋?    Method method = clazz.getMethod("setName", String.class);
    System.out.println("01 - " + method);

    // 獲取該類及父類中所有聲明為 public 的方法
    Method[] methods = clazz.getMethods();
    for (Method temp : methods) {
        System.out.println("02 - " + temp);
    }

    // 獲取一個(gè)在該類中聲明的方法
    Method declaredMethod = clazz.getDeclaredMethod("display");
    System.out.println("03 - " + declaredMethod);

    // 獲取所有在該類中聲明的方法
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (Method temp : declaredMethods) {
        System.out.println("04 - " + temp);
    }

    // 獲取該方法上的所有注解
    Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
    for (Annotation temp : declaredAnnotations) {
        System.out.println("05 - " + temp);
    }

    // 獲取修飾符
    String modifier = Modifier.toString(method.getModifiers());
    System.out.println("06 - " + modifier);

    // 獲取返回值類型趁猴,返回類對(duì)象
    Class<?> returnType = method.getReturnType();
    System.out.println("07 - " + returnType);
    // 獲取返回值類型,返回Type對(duì)象
    Type genericReturnType = method.getGenericReturnType();
    System.out.println("08 - " + genericReturnType);

    // 獲取方法名稱
    String name = method.getName();
    System.out.println("09 - " + name);

    // 獲取所有入?yún)?    Parameter[] parameters = method.getParameters();
    for (Parameter temp : parameters) {
        System.out.println("10 - " + temp);
    }

}

輸出結(jié)果:

01 - public void io.github.gozhuyinglong.reflection.Person.setName(java.lang.String)
02 - public java.lang.String io.github.gozhuyinglong.reflection.Person.toString()
02 - public java.lang.String io.github.gozhuyinglong.reflection.Person.getName()
02 - public void io.github.gozhuyinglong.reflection.Person.setName(java.lang.String)
02 - public int io.github.gozhuyinglong.reflection.Person.getAge()
02 - public void io.github.gozhuyinglong.reflection.Person.setAge(int)
02 - public java.lang.String io.github.gozhuyinglong.reflection.Person.sayHello()
02 - public io.github.gozhuyinglong.reflection.PersonEnum io.github.gozhuyinglong.reflection.PersonParent.getSex()
02 - public void io.github.gozhuyinglong.reflection.PersonParent.setSex(io.github.gozhuyinglong.reflection.PersonEnum)
02 - public final void java.lang.Object.wait() throws java.lang.InterruptedException
02 - public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
02 - public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
02 - public boolean java.lang.Object.equals(java.lang.Object)
02 - public native int java.lang.Object.hashCode()
02 - public final native java.lang.Class java.lang.Object.getClass()
02 - public final native void java.lang.Object.notify()
02 - public final native void java.lang.Object.notifyAll()
03 - private java.lang.String io.github.gozhuyinglong.reflection.Person.display()
04 - public java.lang.String io.github.gozhuyinglong.reflection.Person.toString()
04 - public java.lang.String io.github.gozhuyinglong.reflection.Person.getName()
04 - public void io.github.gozhuyinglong.reflection.Person.setName(java.lang.String)
04 - private java.lang.String io.github.gozhuyinglong.reflection.Person.display()
04 - public int io.github.gozhuyinglong.reflection.Person.getAge()
04 - public void io.github.gozhuyinglong.reflection.Person.setAge(int)
04 - public java.lang.String io.github.gozhuyinglong.reflection.Person.sayHello()
05 - @io.github.gozhuyinglong.reflection.PersonAnnotation()
06 - public
07 - void
08 - void
09 - setName
10 - java.lang.String arg0

5.5 Modifier(修飾符)

java.lang.reflect.Modifier提供了訪問(wèn)修飾符信息彪见。通過(guò)Class儡司、FieldMethod余指、Constructor等對(duì)象都可以獲取修飾符捕犬,這個(gè)訪問(wèn)修飾符是一個(gè)整數(shù),可以通過(guò)Modifier.toString方法來(lái)查看修飾符描述酵镜。并且該類提供了一些靜態(tài)方法和常量來(lái)解碼訪問(wèn)修飾符碉碉。

@Test
public void testModifier() throws Exception {
    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");

    // 獲取類的修飾符值
    int modifiers1 = clazz.getModifiers();
    System.out.println("01 - " + modifiers1);

    // 獲取屬性的修飾符值
    int modifiers2 = clazz.getDeclaredField("name").getModifiers();
    System.out.println("02 - " + modifiers2);

    // 獲取構(gòu)造函數(shù)的修飾符值
    int modifiers3 = clazz.getDeclaredConstructor(String.class).getModifiers();
    System.out.println("03 - " + modifiers3);

    // 獲取方法的修飾符值
    int modifiers4 = clazz.getDeclaredMethod("display").getModifiers();
    System.out.println("04 - " + modifiers4);

    // 判斷修飾符值是否 final 類型
    boolean isFinal = Modifier.isFinal(modifiers1);
    System.out.println("05 - " + isFinal);

    // 判斷修飾符值是否 public 類型
    boolean isPublic = Modifier.isPublic(modifiers2);
    System.out.println("06 - " + isPublic);

    // 根據(jù)修飾符值,獲取修飾符標(biāo)志的字符串
    String modifier = Modifier.toString(modifiers1);
    System.out.println("07 - " + modifier);
    System.out.println("08 - " + Modifier.toString(modifiers2));

}

輸出結(jié)果:

01 - 17
02 - 2
03 - 2
04 - 2
05 - true
06 - false
07 - public final
08 - private

5.6 Parameter(參數(shù))

java.lang.reflect.Parameter提供了方法的參數(shù)信息淮韭」噶福可以獲取方法上的注解、參數(shù)名稱靠粪、參數(shù)類型等蜡吧。

@Test
public void testParameter() throws Exception {
    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");

    // 獲取構(gòu)造函數(shù)的參數(shù)
    Constructor<?> constructor = clazz.getConstructor(String.class, int.class, PersonEnum.class);
    Parameter[] parameterArray1 = constructor.getParameters();
    for (Parameter temp : parameterArray1) {
        System.out.println("01 - " + temp);
    }

    // 獲取方法的參數(shù)
    Method method = clazz.getMethod("setName", String.class);
    Parameter[] parameterArray2 = method.getParameters();
    for (Parameter temp : parameterArray2) {
        System.out.println("02 - " + temp);
    }

    Parameter parameter = parameterArray1[0];
    // 獲取參數(shù)上的注解
    Annotation[] annotationArray = parameter.getAnnotations();
    for (Annotation temp : annotationArray) {
        System.out.println("02 - " + temp);
    }

    // 獲取參數(shù)名稱
    String name = parameter.getName();
    System.out.println("03 - " + name);

    // 獲取參數(shù)類型
    Type parameterizedType = parameter.getParameterizedType();
    System.out.println("04 - " + parameterizedType);
    Class<?> type = parameter.getType();
    System.out.println("05 - " + type);

}

輸出結(jié)果:

01 - java.lang.String arg0
01 - int arg1
01 - io.github.gozhuyinglong.reflection.PersonEnum arg2
02 - java.lang.String arg0
02 - @io.github.gozhuyinglong.reflection.PersonAnnotation()
03 - arg0
04 - class java.lang.String
05 - class java.lang.String

5.7 AccessibleObject(可訪問(wèn)標(biāo)志)

java.lang.reflect.AccessibleObject類是FieldMethodConstructor類的超類占键。

該類提供了對(duì)類昔善、方法、構(gòu)造函數(shù)的訪問(wèn)控制檢查的能力(如:私有方法只允許當(dāng)前類訪問(wèn))捞慌。

訪問(wèn)檢查在設(shè)置/獲取屬性耀鸦、調(diào)用方法、創(chuàng)建/初始化類的實(shí)例時(shí)執(zhí)行啸澡。

可以通過(guò)setAccessible方法將可訪問(wèn)標(biāo)志設(shè)為true(默認(rèn)為false)袖订,會(huì)關(guān)閉訪問(wèn)檢查。這樣即使是私有的屬性嗅虏、方法或構(gòu)造函數(shù)洛姑,也可以訪問(wèn)。

6. 通過(guò)反射動(dòng)態(tài)創(chuàng)建對(duì)象并執(zhí)行方法

可以利用反射來(lái)創(chuàng)建對(duì)象皮服,并可執(zhí)行方法楞艾,下面看代碼示例:

  • 通過(guò)Class類的newInstance創(chuàng)建一個(gè)實(shí)例。(該方法調(diào)用無(wú)參構(gòu)造器)龄广。
  • 通過(guò)構(gòu)造函數(shù)Constructor類創(chuàng)建一個(gè)實(shí)例硫眯。
  • 獲取方法,再通過(guò) invoke 方法來(lái)調(diào)用择同,第一個(gè)參數(shù)為實(shí)例两入,后面參數(shù)為方法的Parameter
  • 獲取字段敲才,因?yàn)?age 字段是私有的裹纳,所以將其設(shè)置為可訪問(wèn)(不設(shè)置會(huì)報(bào)異常)。并通過(guò) set 方法來(lái)賦值紧武。
@Test
public void testInvoke() throws Exception {
    Class<?> clazz = Class.forName("io.github.gozhuyinglong.reflection.Person");

    // 通過(guò)Class類的newInstance創(chuàng)建一個(gè)實(shí)例剃氧。(該方法調(diào)用無(wú)參構(gòu)造器)
    Object o1 = clazz.newInstance();
    System.out.println("01 - " + o1.toString());

    // 通過(guò)構(gòu)造函數(shù)Constructor類創(chuàng)建一個(gè)實(shí)例
    Constructor<?> constructor = clazz.getConstructor(String.class, int.class, PersonEnum.class);
    Object o2 = constructor.newInstance("楊過(guò)", 25, PersonEnum.MAN);
    System.out.println("02 - " + o2.toString());

    // 先獲取方法,再通過(guò) invoke 方法來(lái)調(diào)用阻星,第一個(gè)參數(shù)為實(shí)例朋鞍,后面參數(shù)為方法的Parameter
    Method method = clazz.getMethod("setName", String.class);
    method.invoke(o1, "小龍女");
    System.out.println("03 - " + o1.toString());

    // 獲取字段,因?yàn)?age 字段是私有的妥箕,所以將其設(shè)置為可訪問(wèn)(不設(shè)置會(huì)報(bào)異常)番舆。并通過(guò) set 方法來(lái)賦值
    Field field = clazz.getDeclaredField("age");
    field.setAccessible(true);
    field.set(o1, 28);
    System.out.println("04 - " + o1.toString());

}

執(zhí)行結(jié)果:

01 - Person{name='null', age=0, sex='null'}
02 - Person{name='楊過(guò)', age=25, sex='MAN'}
03 - Person{name='小龍女', age=0, sex='null'}
04 - Person{name='小龍女', age=28, sex='null'}

7. 反射的缺點(diǎn)

引自官方指南:https://docs.oracle.com/javase/tutorial/reflect/index.html

反射雖是強(qiáng)大的,但不可隨意使用矾踱。如果可以在不使用反射的情況下執(zhí)行操作恨狈,則應(yīng)避免使用它。因?yàn)橥ㄟ^(guò)反射訪問(wèn)代碼時(shí)呛讲,會(huì)有以下缺點(diǎn)禾怠。

7.1 性能開銷

反射包括了一些動(dòng)態(tài)類型,所以JVM無(wú)法對(duì)這些代碼進(jìn)行優(yōu)化贝搁。因此吗氏,反射操作的效率要比那些非反射操作低得多。我們應(yīng)該避免在經(jīng)常被執(zhí)行的代碼或?qū)π阅芤蠛芨叩某绦蛑惺褂梅瓷洹?/p>

7.2 安全限制

使用反射技術(shù)要求程序必須在一個(gè)沒(méi)有安全限制的環(huán)境中運(yùn)行雷逆。如果一個(gè)程序必須在有安全限制的環(huán)境中運(yùn)行弦讽,如Applet,那么這就是個(gè)問(wèn)題了。

7.3 內(nèi)部暴露

由于反射允許代碼執(zhí)行一些在正常情況下不被允許的操作往产,比如訪問(wèn)私有的屬性和方法被碗。所以使用反射可能會(huì)導(dǎo)致意料之外的副作用:代碼有功能上的錯(cuò)誤,降低可移植性仿村。反射代碼破壞了抽象性锐朴,因此當(dāng)平臺(tái)發(fā)生改變的時(shí)候,代碼的行為就有可能也隨著變化蔼囊。

8. 完整代碼

完整代碼請(qǐng)?jiān)L問(wèn)我的Github焚志,若對(duì)你有幫助,歡迎給個(gè)?畏鼓,感謝~~??????

https://github.com/gozhuyinglong/blog-demos/tree/main/java-source-analysis/src/main/java/io/github/gozhuyinglong/reflection

9. 參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末酱酬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子云矫,更是在濱河造成了極大的恐慌膳沽,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泼差,死亡現(xiàn)場(chǎng)離奇詭異贵少,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)堆缘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門滔灶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人吼肥,你說(shuō)我怎么就攤上這事录平。” “怎么了缀皱?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵斗这,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我啤斗,道長(zhǎng)表箭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任钮莲,我火速辦了婚禮免钻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘崔拥。我一直安慰自己极舔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布链瓦。 她就那樣靜靜地躺著拆魏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上渤刃,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天拥峦,我揣著相機(jī)與錄音,去河邊找鬼溪掀。 笑死事镣,一個(gè)胖子當(dāng)著我的面吹牛步鉴,可吹牛的內(nèi)容都是我干的揪胃。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼氛琢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼喊递!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起阳似,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骚勘,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后撮奏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體俏讹,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年畜吊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泽疆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡玲献,死狀恐怖殉疼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捌年,我是刑警寧澤瓢娜,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站礼预,受9級(jí)特大地震影響托酸,放射性物質(zhì)發(fā)生泄漏哈肖。R本人自食惡果不足惜淤井,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贱案。 院中可真熱鬧宝踪,春花似錦瘩燥、人聲如沸厉膀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)藻治。三九已至,卻和暖如春雏节,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寥粹。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留崭孤,地道東北人辨宠。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓泪酱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親斯撮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容