Java 反射

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 反射相关的知识,希望对你有一定的参考价值。

一、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类:提供了动态创建数组,以及访问数组的元素的静态方法
    具体如下:
    技术分享
    常用方法用法:

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
技术分享
技术分享
技术分享

四、应用

代码操作的实体类:
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  string;
    }
}


2、构造函数有参

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

}

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));

效果如下:
技术分享

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);
        //主要就是这句了
        entityClassWithParameter base = (entityClassWithParameter) con.newInstance(params);

    }
}

结果如下:
技术分享

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

演示demo下载:
http://download.csdn.net/detail/xunzaosiyecao/9631373

作者:jiankunking 出处:http://blog.csdn.net/jiankunking





















以上是关于Java 反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

OpenGL片段着色器不照亮场景