Java 反射

来源:互联网 发布:遗传算法原理与应用 编辑:程序博客网 时间:2024/06/01 10:34

一、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

2 0
原创粉丝点击