• java 反射(Reflection)-干货


    看了很多关于java 反射的文章,自己把所看到的总结一下。对自己,对他人或多或少有帮助吧。

    • Java Reflection是什么?

    首先来看看官方文档Oracle里面对Reflection的描述:

    Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    简单的讲:

    • 反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们。

    再简单一点的讲:

    • 我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。

    举个例子:

    Java是面向对象语言,我们可以把老虎,狮子等具有相同性质的动物归类(抽象)为猫科动物,他们具有牙齿,胡须等一些属性。同时具有吃肉()的动作。

    同样的道理,我们所接触到的类Class,也可以把他们抽象出来,有类名,成员变量,方法等。

    那么既然能够把类看做是对象,那么java就可以对其进行处理。

    • Java反射(Reflection)框架主要提供以下功能:

    1. 在运行时判断任意一个对象所属的类;
    2. 在运行时构造任意一个类的对象;
    3. 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
    4. 在运行时调用任意一个对象的方法
    • Java反射(Reflection)的主要用途

    1. 工厂模式:Factory类中用反射的话,添加了一个新的类之后,就不需要再修改工厂类Factory了
    2. 数据库JDBC中通过Class.forName(Driver).来获得数据库连接驱动
    3. 分析类文件:毕竟能得到类中的方法等等
    4. 访问一些不能访问的变量或属性:破解别人代码
    • Java反射(Reflection)的基本运用

      获取Class有一下三种方法:

    1. 使用Class类的forName静态方法

     1 //在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机),   
     2 //这通过java.lang.Class类的静态方法forName(String  className)实现。   
     3 //例如:   
     4 try{   
     5     //加载MySql的驱动类   
     6     Class.forName("com.mysql.jdbc.Driver") ;   
     7 }catch(ClassNotFoundException e){   
     8     //System.out.println("找不到驱动程序类 ,加载驱动失败!");   
     9     e.printStackTrace() ;   
    10 }   
    11 //成功加载后,会将Driver类的实例注册到DriverManager类中。

    2. 直接获取某一个对象的class

    1 Class<?> klass = int.class; 
    2 Class<?> classInt = Integer.TYPE;

    3. 调用某个对象的getClass()方法

    1 public class Test{
    2     public static void main(String[] args) {
    3         Demo demo=new Demo();
    4         System.out.println(demo.getClass().getName());
    5     }
    6 }

     下面是一个完整的Demo展示Java Reflection的操作:

      1 package com.b510.hongten.test.reflex;
      2 
      3 import java.lang.reflect.Constructor;
      4 import java.lang.reflect.Field;
      5 import java.lang.reflect.InvocationTargetException;
      6 import java.lang.reflect.Method;
      7 import java.lang.reflect.Modifier;
      8 
      9 public class ReflectionDemo {
     10     /**
     11      * 为了看清楚Java反射部分代码,所有异常我都最后抛出来给虚拟机处理!
     12      * 
     13      * @param args
     14      * @throws ClassNotFoundException
     15      * @throws InstantiationException
     16      * @throws IllegalAccessException
     17      * @throws InvocationTargetException
     18      * @throws IllegalArgumentException
     19      * @throws NoSuchFieldException
     20      * @throws SecurityException
     21      * @throws NoSuchMethodException
     22      */
     23     public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchFieldException, NoSuchMethodException {
     24         // Demo1. 通过Java反射机制得到类的包名和类名
     25         Demo1();
     26         System.out.println("===============================================");
     27 
     28         // Demo2. 验证所有的类都是Class类的实例对象
     29         Demo2();
     30         System.out.println("===============================================");
     31 
     32         // Demo3. 通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在],无参构造
     33         Demo3();
     34         System.out.println("===============================================");
     35 
     36         // Demo4: 通过Java反射机制得到一个类的构造函数,并实现构造带参实例对象
     37         Demo4();
     38         System.out.println("===============================================");
     39 
     40         // Demo5: 通过Java反射机制操作成员变量, set 和 get
     41         Demo5();
     42         System.out.println("===============================================");
     43 
     44         // Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
     45         Demo6();
     46         System.out.println("===============================================");
     47 
     48         // Demo7: 通过Java反射机制调用类中方法
     49         Demo7();
     50         System.out.println("===============================================");
     51 
     52         // Demo8: 通过Java反射机制获得类加载器
     53         Demo8();
     54         System.out.println("===============================================");
     55 
     56     }
     57 
     58     /**
     59      * Demo1: 通过Java反射机制得到类的包名和类名
     60      */
     61     public static void Demo1() {
     62         Person person = new Person();
     63         System.out.println("Demo1: 包名: " + person.getClass().getPackage().getName() + "," + "完整类名: " + person.getClass().getName());
     64         
     65         /**
     66         运行结果:
     67         Demo1: 包名: com.b510.hongten.test.reflex,完整类名: com.b510.hongten.test.reflex.Person
     68         */
     69     }
     70 
     71     /**
     72      * Demo2: 验证所有的类都是Class类的实例对象 
     73      * 
     74      * @throws ClassNotFoundException
     75      */
     76     public static void Demo2() throws ClassNotFoundException {
     77         // 定义两个类型都未知的Class , 设置初值为null, 看看如何给它们赋值成Person类
     78         Class<?> class1 = null;
     79         Class<?> class2 = null;
     80 
     81         // 写法1, 可能抛出 ClassNotFoundException [多用这个写法]
     82         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
     83         System.out.println("Demo2:(写法1) 包名: " + class1.getPackage().getName() + "," + "完整类名: " + class1.getName());
     84 
     85         /**
     86         运行结果:
     87         Demo2:(写法1) 包名: com.b510.hongten.test.reflex,完整类名: com.b510.hongten.test.reflex.Person
     88         */
     89         
     90         // 写法2
     91         class2 = Person.class;
     92         System.out.println("Demo2:(写法2) 包名: " + class2.getPackage().getName() + "," + "完整类名: " + class2.getName());
     93         
     94         /**
     95         运行结果:
     96         Demo2:(写法2) 包名: com.b510.hongten.test.reflex,完整类名: com.b510.hongten.test.reflex.Person
     97         */
     98     }
     99 
    100     /**
    101      * Demo3: 通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在]
    102      * 
    103      * @throws ClassNotFoundException
    104      * @throws IllegalAccessException
    105      * @throws InstantiationException
    106      */
    107     public static void Demo3() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    108         Class<?> class1 = null;
    109         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
    110         // 由于这里不能带参数,所以你要实例化的这个类Person,一定要有无参构造函数哈~
    111         Person person = (Person) class1.newInstance();
    112         person.setAge(20);
    113         person.setName("Hongten");
    114         System.out.println("Demo3: " + person.getName() + " : " + person.getAge());
    115         
    116         /**
    117         运行结果:
    118         Demo3: Hongten : 20
    119         */
    120     }
    121 
    122     /**
    123      * Demo4: 通过Java反射机制得到一个类的构造函数,并实现创建带参实例对象
    124      * 
    125      * @throws ClassNotFoundException
    126      * @throws InvocationTargetException
    127      * @throws IllegalAccessException
    128      * @throws InstantiationException
    129      * @throws IllegalArgumentException
    130      */
    131     public static void Demo4() throws ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
    132         Class<?> class1 = null;
    133         Person person1 = null;
    134         Person person2 = null;
    135 
    136         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
    137         // 得到一系列构造函数集合
    138         Constructor<?>[] constructors = class1.getConstructors();
    139 
    140         person1 = (Person) constructors[0].newInstance();
    141         person1.setAge(30);
    142         person1.setName("Hongten");
    143 
    144         person2 = (Person) constructors[1].newInstance(20, "Hongten");
    145 
    146         System.out.println("Demo4: " + person1.getName() + " : " + person1.getAge() + "  ,   " + person2.getName() + " : " + person2.getAge());
    147 
    148         /**
    149         运行结果:
    150         Demo4: Hongten : 30  ,   Hongten : 20
    151         */
    152     }
    153 
    154     /**
    155      * Demo5: 通过Java反射机制操作成员变量, set 和 get
    156      * 
    157      * @throws IllegalAccessException
    158      * @throws IllegalArgumentException
    159      * @throws NoSuchFieldException
    160      * @throws SecurityException
    161      * @throws InstantiationException
    162      * @throws ClassNotFoundException
    163      */
    164     public static void Demo5() throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException, InstantiationException, ClassNotFoundException {
    165         Class<?> class1 = null;
    166         class1 = Class.forName("com.b510.hongten.test.reflex.Person");
    167         Object obj = class1.newInstance();
    168 
    169         Field personNameField = class1.getDeclaredField("name");
    170         personNameField.setAccessible(true);
    171         personNameField.set(obj, "HONGTEN");
    172 
    173         System.out.println("Demo5: 修改属性之后得到属性变量的值:" + personNameField.get(obj));
    174 
    175         /**
    176         运行结果:
    177         Demo5: 修改属性之后得到属性变量的值:HONGTEN
    178         */
    179     }
    180 
    181     /**
    182      * Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等
    183      * 
    184      * @throws ClassNotFoundException
    185      */
    186     public static void Demo6() throws ClassNotFoundException {
    187         Class<?> class1 = null;
    188         class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan");
    189 
    190         // 取得父类名称
    191         Class<?> superClass = class1.getSuperclass();
    192         System.out.println("Demo6:  SuperMan类的父类名: " + superClass.getName());
    193 
    194         /**
    195         运行结果:
    196         Demo6:  SuperMan类的父类名: com.b510.hongten.test.reflex.Person
    197         */
    198         
    199         System.out.println("===============================================");
    200 
    201         Field[] fields = class1.getDeclaredFields();
    202         for (int i = 0; i < fields.length; i++) {
    203             System.out.println("类中的成员: " + fields[i]);
    204         }
    205         /**
    206         运行结果:
    207         类中的成员: private boolean com.b510.hongten.test.reflex.SuperMan.BlueBriefs
    208         */
    209         
    210         System.out.println("===============================================");
    211 
    212         // 取得类方法
    213         Method[] methods = class1.getDeclaredMethods();
    214         for (int i = 0; i < methods.length; i++) {
    215             System.out.println("Demo6,取得SuperMan类的方法:");
    216             System.out.println("函数名:" + methods[i].getName());
    217             System.out.println("函数返回类型:" + methods[i].getReturnType());
    218             System.out.println("函数访问修饰符:" + Modifier.toString(methods[i].getModifiers()));
    219             System.out.println("函数代码写法: " + methods[i]);
    220         }
    221 
    222         /**
    223         运行结果:
    224         Demo6,取得SuperMan类的方法:
    225         函数名:isBlueBriefs
    226         函数返回类型:boolean
    227         函数访问修饰符:public
    228         函数代码写法: public boolean com.b510.hongten.test.reflex.SuperMan.isBlueBriefs()
    229         Demo6,取得SuperMan类的方法:
    230         函数名:setBlueBriefs
    231         函数返回类型:void
    232         函数访问修饰符:public
    233         函数代码写法: public void com.b510.hongten.test.reflex.SuperMan.setBlueBriefs(boolean)
    234         Demo6,取得SuperMan类的方法:
    235         函数名:fly
    236         函数返回类型:void
    237         函数访问修饰符:public
    238         函数代码写法: public void com.b510.hongten.test.reflex.SuperMan.fly()
    239         Demo6,取得SuperMan类的方法:
    240         函数名:walk
    241         函数返回类型:void
    242         函数访问修饰符:public
    243         函数代码写法: public void com.b510.hongten.test.reflex.SuperMan.walk(int)
    244         */
    245         
    246         System.out.println("===============================================");
    247 
    248         // 取得类实现的接口,因为接口类也属于Class,所以得到接口中的方法也是一样的方法得到哈
    249         Class<?> interfaces[] = class1.getInterfaces();
    250         for (int i = 0; i < interfaces.length; i++) {
    251             System.out.println("实现的接口类名: " + interfaces[i].getName());
    252         }
    253         
    254         /**
    255         运行结果:
    256         实现的接口类名: com.b510.hongten.test.reflex.ActionInterface
    257         */
    258 
    259     }
    260 
    261     /**
    262      * Demo7: 通过Java反射机制调用类方法
    263      * 
    264      * @throws ClassNotFoundException
    265      * @throws NoSuchMethodException
    266      * @throws SecurityException
    267      * @throws InvocationTargetException
    268      * @throws IllegalAccessException
    269      * @throws IllegalArgumentException
    270      * @throws InstantiationException
    271      */
    272     public static void Demo7() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
    273         Class<?> class1 = null;
    274         class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan");
    275 
    276         System.out.println("Demo7: 
    调用无参方法fly()");
    277         Method method = class1.getMethod("fly");
    278         method.invoke(class1.newInstance());
    279 
    280         System.out.println("调用有参方法walk(int m)");
    281         method = class1.getMethod("walk", int.class);
    282         method.invoke(class1.newInstance(), 100);
    283         
    284         /**
    285         运行结果:
    286         Demo7: 
    287         调用无参方法fly()
    288         fly method....
    289         调用有参方法walk(int m)
    290         fly in 100 m
    291         */
    292         
    293     }
    294 
    295     /**
    296      * Demo8: 通过Java反射机制得到类加载器信息
    297      * 
    298      * 在java中有三种类类加载器。[这段资料网上截取]
    299      * 
    300      * 1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
    301      * 
    302      * 2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类
    303      * 
    304      * 3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
    305      * 
    306      * @throws ClassNotFoundException
    307      */
    308     public static void Demo8() throws ClassNotFoundException {
    309         Class<?> class1 = null;
    310         class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan");
    311         String nameString = class1.getClassLoader().getClass().getName();
    312 
    313         System.out.println("Demo8: 类加载器类名: " + nameString);
    314         
    315         /**
    316         运行结果:
    317         Demo8: 类加载器类名: sun.misc.Launcher$AppClassLoader
    318         */
    319     }
    320 
    321 }
    322 
    323 
    324 class Person {
    325     private int age;
    326     private String name;
    327 
    328     public Person() {
    329 
    330     }
    331 
    332     public Person(int age, String name) {
    333         this.age = age;
    334         this.name = name;
    335     }
    336 
    337     public int getAge() {
    338         return age;
    339     }
    340 
    341     public void setAge(int age) {
    342         this.age = age;
    343     }
    344 
    345     public String getName() {
    346         return name;
    347     }
    348 
    349     public void setName(String name) {
    350         this.name = name;
    351     }
    352 }
    353 
    354 class SuperMan extends Person implements ActionInterface {
    355     private boolean BlueBriefs;
    356 
    357     public void fly() {
    358         System.out.println("fly method....");
    359     }
    360 
    361     public boolean isBlueBriefs() {
    362         return BlueBriefs;
    363     }
    364 
    365     public void setBlueBriefs(boolean blueBriefs) {
    366         BlueBriefs = blueBriefs;
    367     }
    368 
    369     public void walk(int m) {
    370         System.out.println("fly in " + m + " m");
    371     }
    372 }
    373 
    374 interface ActionInterface {
    375     public void walk(int m);
    376 }
    •  说说工厂模式和Java 反射(Reflection)机制

     如果在工厂模式下面,我们不使用Java 反射(Reflection)机制,会是什么样子呢?

     1 package com.b510.hongten.test.reflex;
     2 
     3 /**
     4  * @author hongten
     5  * @created Apr 18, 2018
     6  */
     7 public class FactoryTest {
     8 
     9     public static void main(String[] args) {
    10         Cats cats = FactoryTest.getInstance("Tiger");
    11         cats.eatMeat();
    12 
    13         /*
    14          * The Result : The tiger eat meat.
    15          */
    16     }
    17 
    18     public static Cats getInstance(String name) {
    19         Cats cats = null;
    20         if ("Tiger".equals(name)) {
    21             cats = new Tiger();
    22         }
    23         if ("Lion".equals(name)) {
    24             cats = new Lion();
    25         }
    26         return cats;
    27     }
    28 }
    29 
    30 interface Cats {
    31     public abstract void eatMeat();
    32 }
    33 
    34 class Tiger implements Cats {
    35 
    36     public void eatMeat() {
    37         System.out.println("The tiger eat meat.");
    38     }
    39 
    40 }
    41 
    42 class Lion implements Cats {
    43 
    44     public void eatMeat() {
    45         System.out.println("The lion eat meat.");
    46     }
    47 
    48 }

    我们会发现:

    当我们在添加一个子类(Puma-美洲狮)的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改的就会很多。

    我们对程序进行修改,加入反射机制。

     1 package com.b510.hongten.test.reflex;
     2 
     3 /**
     4  * @author hongten
     5  * @created Apr 18, 2018
     6  */
     7 public class FactoryTest {
     8 
     9     public static void main(String[] args) {
    10         //Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Lion");
    11         Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Tiger");
    12         cats.eatMeat();
    13 
    14         /*
    15          * The Result : The tiger eat meat.
    16          */
    17     }
    18 
    19     public static Cats getInstance(String name) {
    20         Cats cats = null;
    21         try {
    22             try {
    23                 //use Class.forName() with java reflection
    24                 cats = (Cats) Class.forName(name).newInstance();
    25             } catch (InstantiationException e) {
    26                 e.printStackTrace();
    27             } catch (IllegalAccessException e) {
    28                 e.printStackTrace();
    29             }
    30         } catch (ClassNotFoundException e) {
    31             e.printStackTrace();
    32         }
    33         return cats;
    34     }
    35 }
    36 
    37 interface Cats {
    38     public abstract void eatMeat();
    39 }
    40 
    41 class Tiger implements Cats {
    42 
    43     public void eatMeat() {
    44         System.out.println("The tiger eat meat.");
    45     }
    46 
    47 }
    48 
    49 class Lion implements Cats {
    50 
    51     public void eatMeat() {
    52         System.out.println("The lion eat meat.");
    53     }
    54 
    55 }

    我们会发现利用了Java Reflection以后,现在就算我们添加任意多个子类的时候,工厂类就不需要修改。我们只需要传递工厂类的实现类的名称即可。

    然而,在这样的情况下面,如果我们需要运行Lion实例的时候,我们还需要去修改java代码。

    1 Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Lion");

    上面的代码虽然可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。

    能不能不需要修改java代码,同样可以完成相同的操作呢?是可以的。

    我们可以让工厂模式结合属性文件(如:*.properties, *.xml文件)

     属性文件:/reflex/cats.properties

    1 lion=com.b510.hongten.test.reflex.Lion
    2 tiger=com.b510.hongten.test.reflex.Tiger

    测试文件:/reflex/FactoryTest.java

     1 package com.b510.hongten.test.reflex;
     2 
     3 import java.io.File;
     4 import java.io.FileInputStream;
     5 import java.io.FileNotFoundException;
     6 import java.io.FileOutputStream;
     7 import java.io.IOException;
     8 import java.util.Properties;
     9 
    10 /**
    11  * @author hongten
    12  * @created Apr 18, 2018
    13  */
    14 public class FactoryTest {
    15 
    16     public static void main(String[] args) {
    17         Cats cats;
    18         try {
    19             cats = FactoryTest.getInstance(getPro().getProperty("tiger"));
    20             if (cats != null) {
    21                 cats.eatMeat();
    22             }
    23         } catch (FileNotFoundException e) {
    24             e.printStackTrace();
    25         } catch (IOException e) {
    26             e.printStackTrace();
    27         }
    28 
    29         /*
    30          * The Result : The tiger eat meat.
    31          */
    32     }
    33 
    34     public static Cats getInstance(String name) {
    35         Cats cats = null;
    36         try {
    37             try {
    38                 // use Class.forName() with java reflection
    39                 cats = (Cats) Class.forName(name).newInstance();
    40             } catch (InstantiationException e) {
    41                 e.printStackTrace();
    42             } catch (IllegalAccessException e) {
    43                 e.printStackTrace();
    44             }
    45         } catch (ClassNotFoundException e) {
    46             e.printStackTrace();
    47         }
    48         return cats;
    49     }
    50 
    51     public static Properties getPro() throws FileNotFoundException, IOException {
    52         Properties pro = new Properties();
    53         File f = new File("cats.properties");
    54         if (f.exists()) {
    55             pro.load(new FileInputStream(f));
    56         } else {
    57             pro.setProperty("lion", "com.b510.hongten.test.reflex.Lion");
    58             pro.setProperty("tiger", "com.b510.hongten.test.reflex.Tiger");
    59             pro.store(new FileOutputStream(f), "FRUIT CLASS");
    60         }
    61         return pro;
    62     }
    63 }
    64 
    65 interface Cats {
    66     public abstract void eatMeat();
    67 }
    68 
    69 class Tiger implements Cats {
    70 
    71     public void eatMeat() {
    72         System.out.println("The tiger eat meat.");
    73     }
    74 
    75 }
    76 
    77 class Lion implements Cats {
    78 
    79     public void eatMeat() {
    80         System.out.println("The lion eat meat.");
    81     }
    82 
    83 }

    作为开发者,我们只需要配置cats.properties属性文件即可。极大的提高了程序的扩展性能。

    • Java反射(Reflection)的一些注意事项

    1. 由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。
    2. 另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

    ========================================================

    More reading,and english is important.

    I'm Hongten

     

    大哥哥大姐姐,觉得有用打赏点哦!多多少少没关系,一分也是对我的支持和鼓励。谢谢。
    Hongten博客排名在100名以内。粉丝过千。
    Hongten出品,必是精品。

    E | hongtenzone@foxmail.com  B | http://www.cnblogs.com/hongten

    ========================================================

  • 相关阅读:
    TCP/IP 协议体系结构模型
    A*算法详解
    导航规划之CH算法介绍
    Crp(customizable route planning) 算法介绍
    机器学习面试题总结(笔记)
    机器学习面试问题总结
    机器学习面试常见问题
    seata-微服务集成seata
    seata-部署seata连接nacos
    Sentinel授权规则-规则管理三种模式
  • 原文地址:https://www.cnblogs.com/hongten/p/hongten_java_reflection.html
Copyright © 2020-2023  润新知