实例理解Java反射机制

目录

  • 反射(Reflect)
    • 背景
    • 什么是反射?如何理解反射?
    • 图示正常方式与反射方式的区别
    • 如何看待动态语言和静态语言?
      • 动态语言
      • 静态语言
    • 反射的功能
    • 理解Class类并获取Class的实例
      • Class类简介
      • 获取Class的方式
        • 推荐使用第一种方式(直接调用类的class属性)获取Class对象
        • 代码测试获取Class对象
    • 如何从Class中获取类的信息?
      • 获取构造器的4种方式
      • 获取方法的4种方式
      • 获取属性的4种方式
      • 获取注释的3种方式
      • 获取内部类:
      • 获取外部类
      • 获取继承的父类后者所实现的接口
      • 判断该类是否为接口、枚举、注释类型等
    • 代码测试获取类的信息
    • 反射创建并操作运行时类的对象
      • 创建对象
      • 调用方法
      • 访问属性值
      • 关于setAccessible方法的使用
      • 代码示例
    • 操作数组
      • 代码示例
    • 反射和泛型
      • 对比泛型使用前后的差别
      • 使用反射获取泛型信息
        • 代码测试
    • 读取配置文件利用反射创建并操作运行时类的对象

反射(Reflect)

背景

学习过好几遍的反射了,但总是过一段又忘了,不知道该怎么使用,特此总结下来,并手撸代码加深印象,感觉这样很快就掌握了。

什么是反射?如何理解反射?

反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。

Reflection(反射)是被视为动态语言的关键(本身仍为静态语言,可以说是准动态语言),反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

图示正常方式与反射方式的区别

实例理解Java反射机制_第1张图片

如何看待动态语言和静态语言?

动态语言

是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。

静态语言

与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。

Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。

Java的动态性让编程的时候更加灵活!

反射的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时查看一个类的信息(成员变量、方法、注解、实现的接口、构造器、泛型、修饰符、继承关系、内部类等)
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理

理解Class类并获取Class的实例

Class类简介

  • Class本身也是一个类
  • Class 对象只能由系统建立对象
  • 一个加载的类在JVM 中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class 实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

获取Class的方式

  1. 调用某个类的class属性来获取该类对应的Class对象。例如Person.class将会返回Person类对应的Class对象
  2. 使用Class类forName()静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名)。
  3. 调用某个对象的getClass()方法,该方法是java.lang.Object类中的一个方法,所以所有Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。
  4. 使用类的加载器:ClassLoader

推荐使用第一种方式(直接调用类的class属性)获取Class对象

优势如下:

  1. 代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。
  2. 程序性能更高,因为这种方式无须调用方法,所以性能更好。

但如果我们只有一个字符串, 例如“java.lang. String”,如果需要获取该字符串对应的Class对象,则只能使用第二种方
式了,使用Class的forName方法获取Class对象时,该方法可能抛出一个ClasNotFoundException异常

代码测试获取Class对象

MyAnnotation注解Person接口Producer类为通用的,后续还会使用

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    String value() default "Annotation测试";

}
/**
 * 测试反射的接口
 */
public interface Person {
    //吃饭
    void eat(String food);
}
/**
 * 功能描述:测试反射的JavaBean
 * 生产商类
 *
 * @author RenShiWei
 * Date: 2020/6/19 9:16
 **/
@MyAnnotation("反射获取类注解信息")
public class Producer implements Person {

    @MyAnnotation("生产商姓名")
    private String name;

    int age;

    public int id;
    
    //生产的商品map
    public Map<String,String> productMap=new HashMap<>();

    public Producer () {
    }

    private Producer ( String name, int age, int id ) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    //get和set方法
    public String getName () {
        return name;
    }

    public void setName ( String name ) {
        this.name = name;
    }

    public int getAge () {
        return age;
    }

    public void setAge ( int age ) {
        this.age = age;
    }

    public int getId () {
        return id;
    }

    public void setId ( int id ) {
        this.id = id;
    }

    //生产产品
    @MyAnnotation
    public String produce ( String product ) {
        return "正在生产产品:" + product;
    }

    //查看产品
    void showProducts () {
        System.out.println("正在查看产品");
    }

    //实现接口的方法
    @Override
    public void eat ( String food ) {
        System.out.println("正在吃:" + food);
    }

    //内部类
    class Inner{}
    
    @Override
    public String toString () {
        return "Producer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}

测试获取Class对象

public static void main ( String[] args ) {
        System.out.println("**************测试获取Class的对象******************");
        System.out.println("----方式一:调用类的class属性获取----");
        Class<Producer> clazz1 = Producer.class;
        System.out.println(clazz1);

        System.out.println("---方式二:通过Class类的静态方法forName()获取----");
        Class<?> clazz2=null;
        try {
            clazz2 = Class.forName("reflectiontest.Producer");
            System.out.println(clazz2);
        } catch (ClassNotFoundException e) {
            System.out.println("没有通过全限类名找到此类,不能使用反射获取Class对象");
            e.printStackTrace();
        }

        System.out.println("---方式三:调用对象的getClass()方法(此方法为java.lang.Object的方法)获取---");
        Producer producer=new Producer();
        Class<? extends Producer> clazz3 = producer.getClass();
        System.out.println(clazz3);

        System.out.println("---方式四:通过类加载器ClassLoader获取---");
        ClassLoader classLoader=ReflectionTest.class.getClassLoader();
        Class<?> clazz4=null;
        try {
            clazz4 = classLoader.loadClass("reflectiontest.Producer");
            System.out.println(clazz4);
        } catch (ClassNotFoundException e) {
            System.out.println("没有通过全限类名找到此类,不能使用反射获取Class对象");
            e.printStackTrace();
        }
        System.out.println("对比四种方式获取的Class对象是否是同一个");
        System.out.println(clazz1==clazz2);
        System.out.println(clazz1==clazz3);
        System.out.println(clazz1==clazz4);
}

结果:

**************测试获取Class的对象******************
----方式一:调用类的class属性获取----
class reflectiontest.Producer
---方式二:通过Class类的静态方法forName()获取----
class reflectiontest.Producer
---方式三:调用对象的getClass()方法(此方法为java.lang.Object的方法)获取---
class reflectiontest.Producer
---方式四:通过类加载器ClassLoader获取---
class reflectiontest.Producer
对比四种方式获取的Class对象是否是同一个
true
true
true

如何从Class中获取类的信息?

获取构造器的4种方式

构造器 描述
Connstructor getConstructor(Class… parameterTypes) 返回此Class对象所表示的类的指定的public构造器
Constructor[] getConstructors() 返回此Class对象所表示的类的所有public 构造器。
Constructor getDeclaredConstructor(Class… parameterTypes) 返回此Class对象所表示的类的指定构造器,与构造器的访问级别无关
Constructor[] getDeclaredConstructors() 返回此Class对象所表示的类的所有构造器,与构造器的访问级别无关

获取方法的4种方式

方法 描述
Method getMethod(String name, Class… parameterTypes) 返回此Class对象所表示的类的指定public方法
Method[] getMethods() 返回此Class对象所表示的类的所有public方法
Method getDeclaredMethod(String name, Class… parameterTypes) 返回此Class对象所表示的类的指定方法,与方法的访问级别无关
Method[] getDeclaredMethods() 返回此Class对象所表示的类的全部方法,与方法的访问级别无关

获取属性的4种方式

方法 描述
Field getField(String name) 返回此Class对象所表示的类的指定的public属性( Field)
Field[] getFields() 返回此Class对象所表示的类的所有public属性(Field)
Field getDeclaredField(String name) 返回此Class对象所表示的类的指定属性( Field) ,与属性的访问级别无关。
Field] getDeclaredFields() 返回此Class对象所表示的类的全部属性(Field) ,与属性的访间级别无关

获取注释的3种方式

方法 描述
A getAnnotation(Class annotationClass) 试图获取该Class对象所表示类上.指定类型的注释;如果该类型的注释不存在则返回null
Annotation[] getAnnotations() 返回此元素上存 在的所有注释
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注释

获取内部类:

方法 描述
Class[ getDeclaredClasses() 返回该Class对象所对应类里包含的全部内部类

获取外部类

方法 描述
Class getDeclaringClass() 返回该Class对象所对应类所在的外部类

获取继承的父类后者所实现的接口

方法 描述
Class[] getInterfaces() 返回该Class对象对应类所实现的全部接口
int getModifiers() 返回此类或接口的所有修饰符
修饰符由public. protected、private、final、static、abstract等对应的常量组成
返回的整数应使用Modifier工具类的方法来解码,才可以获取真实的修饰符。
Package getPackage() 获取此类的包
String getName() 以字符串形式返回此Class 对象所表示的类的名称
String getSimpleName() 以字符串形式返回此Class 对象所表示的类的简称
Class getSuperclass() 返回该Class 所表示的类的超类对应的Class对象

判断该类是否为接口、枚举、注释类型等

方法 描述
boolean isAnnotation() 返回此Class 对象是否表示一个注释类型( 由@interface定义)
boolean isAnnotationPresent(Class annotationClass) 判断此Class对象,上是否使用了Annotation注释修饰
boolean isAnonymousClass() 返回此Class 对象是否是一个匿 名类
boolean isArray() 返回此Class 对象是否表示一个数组类
boolean isEnum() 返回此Class 对象是否表示一个枚举(由enum关键字定义)
boolean isInterface() 返回此Class 对象是否表示一一个接口( 使用interface定义)
boolean isInstance(Object obj) 判断obj是否是此Class对象的实例,该方法可以完全代替instanceof操作符

代码测试获取类的信息

MyAnnotation注解Person接口Producer类还采用上文的。

public static void main ( String[] args ) throws Exception {
        Class<Producer> clazz = Producer.class;
        System.out.println("************测试获取类的信息****************");
        System.out.println("---测试获取Class对象的全部构造器---");
        Constructor<?>[] constructors = clazz.getDeclaredConstructors();
        for (Constructor<?> c : constructors) {
            System.out.println(c);
        }

        System.out.println("---测试获取Class对象的全部为public的构造器---");
        Constructor<?>[] publicConstructors = clazz.getConstructors();
        for (Constructor<?> c : publicConstructors) {
            System.out.println(c);
        }

        System.out.println("---测试获取全部方法---");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println(m);
        }

        System.out.println("---测试获取指定的方法---");
        Method method = clazz.getMethod("produce", String.class);
        System.out.println(method);

        System.out.println("---测试获取类的全部注解---");
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation a : annotations) {
            System.out.println(a);
        }

        System.out.println("---测试获取元素的注解---\n" + Arrays.toString(clazz.getAnnotationsByType(MyAnnotation.class)));

        System.out.println("---测试获取全部的内部类---");
        Class<?>[] declaringClass = clazz.getDeclaredClasses();
        for (Class<?> inner : declaringClass) {
            System.out.println(inner);
        }

        System.out.println("---测试使用Class的forName的方法获取内部类---");
        System.out.println(Class.forName("reflectiontest.Producer$Inner"));

        //还可以获取很多信息,暂略.....
    }

结果:

************测试获取类的信息****************
---测试获取Class对象的全部构造器---
public reflectiontest.Producer()
private reflectiontest.Producer(java.lang.String,int,int)
---测试获取Class对象的全部为public的构造器---
public reflectiontest.Producer()
---测试获取全部方法---
public java.lang.String reflectiontest.Producer.getName()
public int reflectiontest.Producer.getId()
public void reflectiontest.Producer.setName(java.lang.String)
public void reflectiontest.Producer.setId(int)
public void reflectiontest.Producer.setAge(int)
public java.lang.String reflectiontest.Producer.produce(java.lang.String)
void reflectiontest.Producer.showProducts()
public int reflectiontest.Producer.getAge()
public void reflectiontest.Producer.eat(java.lang.String)
---测试获取指定的方法---
public java.lang.String reflectiontest.Producer.produce(java.lang.String)
---测试获取类的全部注解---
@reflectiontest.MyAnnotation(value=反射获取类注解信息)
---测试获取元素的注解---
[@reflectiontest.MyAnnotation(value=反射获取类注解信息)]
---测试获取全部的内部类---
class reflectiontest.Producer$Inner
---测试使用Class的forName的方法获取内部类---
class reflectiontest.Producer$Inner

反射创建并操作运行时类的对象

Class对象可以获得该类里的成分包括方法(由Method对象表示)、构造器(由Constructor 对象表示)、Field (由Field 对象表示),这三个类都定义在java.lang.reflect 包下,并实现了java.lang.reflect.Member接口。程序可以通过Method对象来执行对应的方法,通过Constructor对象来调用对应的构造器创建对象,能通过Field对象直接访问并修改对象的属性值。

创建对象

  1. 使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行newlnstance()方法时实际上是利用默认构造器来创建该类的实例。
  2. 先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创
    建该Class对象对应类的实例。通过这种方式可以选择使用某个类的指定构造器来创建实例。

通过第一种方式来创建对象是比较常见的情形(更加通用)。因为在很多JavaEE框架中都需要根据配置文件信息来创建Java对象,从配置文件读取的只是某个类的字符串类名,程序就需要根据该字符串来创建对应的实例,就必须使用反射。

第二种方式:

1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3)通过Constructor实例化对象。

调用方法

当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或者getMethod()方法来获取全部方法或指定方法一这两个方法的返回值是Method对象数组,或者Method对象。每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用对应方法。

之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。

说明:

  1. Object 对应原方法的返回值,若原方法无返回值,此时返回null
  2. 若原方法若为静态方法,此时形参Object obj可为null
  3. 若原方法形参列表为空,则Object[] args为null
  4. 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

访问属性值

通过Class对象的getFields()或getField(方法可以获取该类所包括的全部Field(属性)或指定Field。通过Field类提供的set()和
get()方法就可以完成设置和取得属性内容的操作。

  • public Field getField(String name)返回此Class对象表示的类或接口的指定的public的Field。
  • public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。

Field提供了如下两组方法来访问属性:

  • getXxx(Object obj): 获取obj对象该Field的属性值。此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消get后面的Xxx。
  • setXxx(Object obj,Xxx val):将obj对象的该Field设置成val值。此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消set后面的Xxx。使用这两个方法可以随意地访问指定对象的所有属性,包括private访问控制的属性。

关于setAccessible方法的使用

  • Method和Field、Constructor对象都有setAccessible()方法。
  • setAccessible启动和禁用访问安全检查的开关。
  • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
    • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
    • 使得原本无法访问的私有成员也可以访问
  • 参数值为false则指示反射的对象应该实施Java语言访问检查

代码示例

MyAnnotation注解Person接口Producer类还采用上文的。

public static void main ( String[] args ) throws Exception {
        System.out.println("*****测试创建运行时类的对象,并通过此对象调用方法和访问属性*****");
        System.out.println("---测试创建运行时类的对象---");
        //获取Class对象
        Class<Producer> clazz = Producer.class;
        //获取构造器,这里先使用默认的空参构造器(也可以使用指定的构造器)
        Constructor<Producer> constructor = clazz.getConstructor();
        //通过调用构造器的newInstance方法创建对象
        Producer producer = constructor.newInstance();
        System.out.println(producer);
        //可以通过这个对象操作数据
        System.out.println("------使用这个对象操作类的属性和方法------");
        producer.setAge(22);
        producer.showProducts();
        System.out.println(producer);

        System.out.println("---测试通过反射调用方法---");
        Method method = clazz.getDeclaredMethod("showProducts");
        method.setAccessible(true);
        //传入的是创建的运行时类的对象
        method.invoke(producer);

        System.out.println("---测试通过反射访问属性---");
        Field field = clazz.getDeclaredField("name");
        field.setAccessible(true);
        field.set(producer,"我叫反射");
        System.out.println(field.get(producer));
        System.out.println(producer);
    }

结果:

*****测试创建运行时类的对象,并通过此对象调用方法和访问属性*****
---测试创建运行时类的对象---
Producer{name='null', age=0, id=0}
------使用这个对象操作类的属性和方法------
正在查看产品
Producer{name='null', age=22, id=0}
---测试通过反射调用方法---
正在查看产品
---测试通过反射访问属性---
Producer{name='我叫反射', age=22, id=0}
我叫反射

操作数组

在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array来动态地创建数组,操作数组元素等。

Array提供了如下几类方法:

  • static Object newlnstance(Class componentT ype, int... length):创建- -个 具有指定的元素类型、指定维度的新数组。
  • static xxx getXxx(Object array, int index):返回array数组中第index个元素。其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变为get(Object array, int index)
  • static void setXxx(Object array, int index, xXXx val):将array数组中第index元素的值设为val。其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变成set(Object array,int index, Object val)

代码示例

public static void main ( String[] args ) {
        System.out.println("*******测试反射操作数组********");
        System.out.println("---操作一维数组---");
        //创建数组
        Object arr = Array.newInstance(String.class, 10);
        //赋值
        Array.set(arr, 2, "测试反射");
        Array.set(arr, 4, "测试反射操作数组");
        //获取指定元素的值
        System.out.println(Array.get(arr, 2));
        System.out.println(Array.get(arr, 4));
        System.out.println(Array.get(arr, 6));

        //三维数组其实是数组元素为二维数组的特殊一维数组
        System.out.println("---操作三维数组---");
        Object arr3 = Array.newInstance(String.class, 3, 4, 10);
        //
        System.out.println("------获取index为2的数组元素,应该是一个二维数组------");
        //获取arr3index为2的数组元素,应该是一个二维数组
        Object arrObj = Array.get(arr3, 2);
        //赋值
        Array.set(arrObj,2,new String[]{"反射测试1","反射测试2"});
        //获取arrObj数组的第三个元素,应该是一维数组
        Object anArr = Array.get(arrObj, 3);
        Array.set(anArr,8,"测试一维反射");
        String[][][] cast= (String[][][]) arr3;
        System.out.println(cast[2][3][8]);
        System.out.println(cast[2][2][0]);
        System.out.println(cast[2][2][1]);
    }

结果:

*******测试反射操作数组********
---操作一维数组---
测试反射
测试反射操作数组
null
---操作三维数组---
------获取index为2的数组元素,应该是一个二维数组------
测试一维反射
反射测试1
反射测试2

反射和泛型

在反射中使用泛型,可以避免使用反射的对象需要强制转换类型,从而引起异常。

对比泛型使用前后的差别

public static void main ( String[] args ) throws Exception {
        System.out.println("*******测试反射获取泛型*******");
        System.out.println("---不使用泛型的情况---");
        Person producer = (Producer) getInstance(Producer.class);

        System.out.println("---使用泛型的情况---");
        Person producer2 = getInstance2(Producer.class);

        //false,因为一个是Object类型,一个是Producer类型
        System.out.println(producer == producer2);
    }

    /**
     * 不使用泛型的情况
     */
    public static Object getInstance ( Class clazz ) throws Exception {
        return clazz.getConstructor().newInstance();
    }

    /**
     * 使用泛型的情况
     */
    public static <T> T getInstance2 ( Class<T> clazz ) throws Exception {
        return clazz.getConstructor().newInstance();
    }

结果:

*******测试反射获取泛型*******
---不使用泛型的情况---
---使用泛型的情况---
false

使用反射获取泛型信息

通过指定类对应的Class 对象,程序可以获得该类里包括的所有Ficld, 不管该Field 使用private修饰,还是使用public修饰。获得了Field对象后,就可以很容易地获得该Field的数据类型,即使用如下代码即可获得指定Field的类型:

//获取Field对象f的类型
Class<?> a = f.getType() ;

但通过这种方式只对普通类型的Field 有效。但如果该Field 的类型是有泛型限制的类型,如Map类型,则不能准确得到该Field的泛型参数。

为了获得指定Field的泛型类型,应先使用如下方法来获取指定Field的泛型类型:

//获得Field实例f的泛型类型
Type gType = f.getGenericType() ;

然后将Type对象强制类型转换为ParameterizedType对象,ParameterizedType 代表被参数化的类型,也就是增加了泛型限制的类型。ParameterizedType 类提供了两个方法: .

  • getRawType(): 返回被泛型限制的类型。
  • getActualTypeArguments(): 返回泛型参数类型。

代码测试

producer类中添加代码:

//生产的商品map
public Map<String,String> productMap=new HashMap<>();

测试:

public static void main ( String[] args ) throws Exception {
		System.out.println("***使用反射获取泛型信息****");
        Class<Producer> clazz = Producer.class;
        Field f = clazz.getDeclaredField("productMap");
        Class<?> a = f.getType();
        System.out.println("获取参数类型" + a);
        Type genericType = f.getGenericType();
        System.out.println("获取泛型参数类型"+genericType);
        if (genericType instanceof ParameterizedType) {
            //将Type强转为ParameterizedType
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            //获取原始类型
            System.out.println("原始类型:" + parameterizedType.getRawType());
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for(Type t:actualTypeArguments){
                System.out.print(t+" ");
            }
        }
}

结果:

*******测试反射获取泛型*******
---不使用泛型的情况---
---使用泛型的情况---
false
***使用反射获取泛型信息****
获取参数类型interface java.util.Map
获取泛型参数类型java.util.Map<java.lang.String, java.lang.String>
原始类型:interface java.util.Map
class java.lang.String class java.lang.String

读取配置文件利用反射创建并操作运行时类的对象

新建配置文件Producer.properties

className=reflectiontest.Producer
methodName=showProducts
name=小明
public static void main ( String[] args ) throws Exception {
        //加载配置文件
        Properties properties = new Properties();
        InputStream is = ReflectionTest6.class.getClassLoader().getResourceAsStream("Producer.properties");
        assert is != null;
        //InputStreamReader将字节流转为字符流(解决乱码问题)
        InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
        properties.load(isr);

        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");
        String argsType = properties.getProperty("argsType");
        String name = properties.getProperty("name");

        //创建运行时类的对象
        Class<?> clazz = Class.forName(className);
        Producer producer = (Producer) clazz.newInstance();

        Method method = clazz.getDeclaredMethod(methodName);
        method.invoke(producer);
        Field field = clazz.getDeclaredField("name");
        field.setAccessible(true);
        field.set(producer, name);
        System.out.println(producer);

        isr.close();
    }

结果:

正在查看产品
Producer{name='小明', age=0, id=0}

你可能感兴趣的:(Java基础,反射,class,java)