Java 反射

 2022-08-22
原文地址:https://blog.51cto.com/jiankunking/5513186

一、Java 语言的反射机制

在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?
答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
Java 反射机制主要提供了以下功能:

  1. 在运行时判断任意一个对象所属的类。
  2. 在运行时构造任意一个类的对象。
  3. 在运行时判断任意一个类所具有的成员变量和方法。
  4. 在运行时调用任意一个对象的方法

Reflection 允许程序在运行时透过Reflection APIs取得任何一个 已知名称 的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods。

那么反射这么厉害,java是不是动态语言呢?
来看一下动态语言百科上的定义:
动态语言,是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。
从这个角度看很明显,发现java不是动态语言

尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或调用其methods。

二、Java Reflection API 简介

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中

  1. Class类:代表一个类。
  2. Field 类:代表类的成员变量(成员变量也称为类的属性)。
  3. Method类:代表类的方法。
  4. Constructor 类:代表类的构造方法。
  5. Array类:提供了动态创建数组,以及访问数组的元素的静态方法
    具体如下:
  6. 202208222304533521.png
  7. 常用方法用法:

1、获得对象的类型

    Class classType=object.getClass();

在java.lang.Object 类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。Class类是Reflection API 中的核心类,它有以下方法:

  • getName():获得类的完整名字。
  • getFields():获得类的public类型的属性。
  • getDeclaredFields():获得类的所有属性。
  • getMethods():获得类的public类型的方法。
  • getDeclaredMethods():获得类的所有方法。

getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。

getConstructors():获得类的public类型的构造方法。

getConstructor(Class[] parameterTypes):
获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。

newInstance():通过类的 不带参数 的构造方法创建这个类的一个对象。

2、通过默认构造方法创建一个新对象

    Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});

以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表 默认的构造方法 ,然后调用Constructor对象的newInstance()方法构造一个实例。

3、获得对象的所有属性

    Field fields[]=classType.getDeclaredFields();

Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性。

获得每个属性相应的getXXX()和setXXX()方法,然后执行这些方法,把原来对象的属性拷贝到新的对象中

4、动态创建和访问数组

java.lang.Array 类提供了动态创建和访问数组元素的各种 静态 方法。

三、Class

众所周知Java有个Object class,是所有Java classes的继承根源,其内声明了数个应该在所有Java class中被改写的methods:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class object。

Class class十分特殊。它和一般classes一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types
(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class object。
如果您想借由“修改Java标准库源码”来观察Class object的实际生成时机(例如在Class的constructor内添加一个println()),不能够!因为Class并没有public constructor。

Class是Reflection起源。针对任何您想探勘的class,唯有先为它产生一个Class object,接下来才能经由后者唤起众多的Reflection APIs。

1、“Class” object的取得途径。

Java允许我们从多种途径为一个class生成对应的Class object

202208222304545672.png

202208222304557253.png

202208222304570994.png

四、应用

代码操作的实体类:
1、构造函数无参

    /**
     * Created by jiankunking on 2016/9/16.
     */
    public class entityClassWithoutParameter
        public int getAddResult(int a, int b)
        {
            return a + b;
        }
    
        private int getMultiplyResult(int a, int b)
        {
            return a * b;
        }
    
        public String getField1()
        {
            return field1;
        }
    
        public void setField1(String field1)
        {
            this.field1 = field1;
        }
    
        public String getField2()
        {
            return field2;
        }
    
        public void setField2(String field2)
        {
            this.field2 = field2;
        }
    
        public String field1;
        protected String field2;
        private String field3 = "我是私有的";
    
        /**
         * 测试私有函数是否可见
         * @return
        private String testPrivate(String string)
        {
            System.out.println("testPrivate:"+string);
            return

2、构造函数有参

    /**
     * Created by jiankunking on 2016/9/16.
     */
    public class entityClassWithParameter
        public entityClassWithParameter(String string, int num)
        {
            System.out.println("我是entityClassWithParameter:"

1、运行时生成instances

在Reflection 动态机制中有两种作法:一个针对“无参ctor”,一个针对“带参数ctor”。如果欲调用的是“带参数ctor“就比较麻烦些,不再调用Class的newInstance(),而是调用Constructor 的newInstance()。
无参:

    //获取 指定类
    Class classType = Class.forName("entityClassWithoutParameter");
    //创建 无参 对象
    Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});

有参:

    //创建 有参 对象
    Class cls = Class.forName("entityClassWithParameter");
    Class[] paramTypes = {String.class, int.class};
    // 方法传入的参数
    Object[] params = {"我是string参数", 23};
    Constructor con = cls.getConstructor(paramTypes);
    //主要就是这句了
    entityClassWithParameter base = (entityClassWithParameter) con.newInstance(params);

2、运行时调用methods

首先准备一个Class[]做为参数类型(本例指定其中一个是String,另一个是int),然后以此为自变量调用getMethod(),获得特定的Method object。接下来准备一个Object[]放置参数变量,然后调用上述所得之特定Method object的invoke()。

    //获取 指定类
    Class classType = Class.forName("entityClassWithoutParameter");
    //获取 指定方法
    Method addMethod = classType.getMethod("getAddResult", new Class[]{int.class, int.class});
    //调用函数 并接收结果
    Object result = addMethod.invoke(classType.newInstance(), new Object[]{new Integer(100), new Integer(200)});

为什么获得Method object时不需指定回返类型?

因为method overloading机制要求signature必须唯一,而回返类型并非signature的一个成份。换句话说,只要指定了method名称和参数列,就一定指出了一个独一无二的method。

3、运行时变更fields内容

首先调用Class的getField()并指定field名称。
获得特定的Field object之后便可直接调用Field的get()和set(),

    //获取private字段
            Field f = classType.getDeclaredField("field3");
            //修改protected、private字段的可见性 public字段不需要这句
            f.setAccessible(true);
            //创建 无参 对象
            Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
            System.out.println("获取私有字段值:" + f.get(objectCopy));
            f.set(objectCopy,"我要修改自己");
            System.out.println("获取私有字段值:" + f.get(objectCopy));

效果如下:

202208222304587885.png

4、对类的私有变量和私有方法的访问

    //获取private字段
            Field f = classType.getDeclaredField("field3");
            //修改protected、private字段的可见性 public字段不需要这句
            f.setAccessible(true);
            //创建 无参 对象
            Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
            System.out.println("获取私有字段值:" + f.get(objectCopy));
            f.set(objectCopy, "我要修改自己");
            System.out.println("获取私有字段值:" + f.get(objectCopy));
    
            //获得私有方法
            Method method = classType.getDeclaredMethod("testPrivate", new Class[]{String.class});
            //设置私有方法可以被访问
            method.setAccessible(true);
            //调用私有方法
            String str = (String) method.invoke(objectCopy, new Object[]{"测试私有方法是否可以通过反射调用"});
            System.out.println("我是私有方法返回值:"+str);//输出 测试私有方法是否可以通过反射调用

完整测试代码

    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * Created by jiankunking on 2016/7/31.
     */
    public class test
        public static void main(final String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException
        {
            //获取 指定类
            Class classType = Class.forName("entityClassWithoutParameter");
            //获取 指定方法
            Method addMethod = classType.getMethod("getAddResult", new Class[]{int.class, int.class});
            //调用函数 并接收结果
            Object result = addMethod.invoke(classType.newInstance(), new Object[]{new Integer(100), new Integer(200)});
            //输出 结果
            System.out.println("加法结果:" + (Integer) result);
    
            //获取 所有方法
            for (Method item : classType.getDeclaredMethods())
            {
                System.out.println("函数名:" + item.getName());
            }
            //获取  所有public字段
            for (Field item : classType.getFields())
            {
                System.out.println("字段名:" + item.getName());
            }
            //获取private字段
            Field f = classType.getDeclaredField("field3");
            //修改protected、private字段的可见性 public字段不需要这句
            f.setAccessible(true);
            //创建 无参 对象
            Object objectCopy = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
            System.out.println("获取私有字段值:" + f.get(objectCopy));
            f.set(objectCopy, "我要修改自己");
            System.out.println("获取私有字段值:" + f.get(objectCopy));
    
            //获得私有方法
            Method method = classType.getDeclaredMethod("testPrivate", new Class[]{String.class});
            //设置私有方法可以被访问
            method.setAccessible(true);
            //调用私有方法
            String str = (String) method.invoke(objectCopy, new Object[]{"测试私有方法是否可以通过反射调用"});
            System.out.println("我是私有方法返回值:"+str);//输出 测试私有方法是否可以通过反射调用
    
            //创建 有参 对象
            Class cls = Class.forName("entityClassWithParameter");
            Class[] paramTypes = {String.class, int.class};
            // 方法传入的参数
            Object[] params = {"我是string参数", 23};
            Constructor con = cls.getConstructor(paramTypes);
            //主要就是这句了

结果如下:

202208222304597236.png

本文部分参考百度百科及网络资料:JAVA反射机制

作者:jiankunking