Java反射机制详解

来源:互联网 发布:检测硬件的软件 编辑:程序博客网 时间:2024/06/13 12:55

一、反射机制的概念
指在运行状态中,能够动态获取任意一个类或者对象信息的功能叫Java语言的反射机制。

二、反射机制的作用
反射机制能让我们更加灵活的创建代码,使得代码可以在运行时装配。反射机制提供以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理

三、反射机制的图解

这里写图片描述

上图是Java的内存模型,下面将举例说明Java运行代码的流程。假如写了一段代码:Object o=new Object();首先运行代码,然后JVM会启动并将代码编译成一个.class文件,然后被类加载器加载进JVM的内存中,你的类Object将加载到方法区中,创建了Object类的Class对象将加载到堆中(Class对象是一个特殊的对象,是用来创建其它对象的对象,每个类仅有唯一的Class对象)。JVM创建对象前,会先检查类是否加载,并寻找类对应的Class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。到此,Object o=new Object()这行代码就运行完毕,然后JVM关闭退出了。
那么反射跟这有什么关系呢,由于上面的程序对象是自己new的,程序相当于写死了给JVM去跑。而反射是什么呢?当我们的程序在运行时,需要动态的加载一些类,这些类可能之前用不到所以不用加载到JVM,这时候就可以看到反射的好处了。

四、反射机制的使用

1、获取Class对象

获取Class对象有三种方法:

  • Class.forName(“类名字符串”) (注意:类名字符串必须是全称,包名+类名)

  • 类字面常量法:类名.class

  • 实例对象.getClass()

2、判断是否为某个类的实例

一般地,我们用instanceof关键字来判断是否为某个类的实例

    object instanceof Integer

3、创建实例

通过反射来生成对象主要有两种方式:

  • 使用Class对象的newInstance()方法来创建Class对象对应类的实例
    Class<?> click = String.class;    Object obj = click.newInstance();
  • 先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。
    //获取String所对应的Class对象    Class<?> click = String.class;    //获取String类带一个String参数的构造器    Constructor constructor = click.getConstructor(String.class);    //根据构造器创建实例    Object obj = constructor.newInstance("123456");

4、获取方法

获取某个Class对象的方法集合,主要有以下几个方法:

  • getDeclaredMethods()方法:返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
    public Method[] getDeclaredMethods() throws SecurityException
  • getMethods()方法:返回某个类的所有公用(public)方法,包括其继承类的公用方法。
    public Method[] getMethods() throws SecurityException
  • getMethod方法:返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
    public Method getMethod(String name, Class<?>... parameterTypes)

5、获取构造器信息

获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例。

6、获取类的成员变量(字段)信息

主要是这几个方法,在此不再赘述:

  • getFiled: 访问公有的成员变量
  • getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量

7、调用方法

当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为:

    public Object invoke(Object obj, Object... args)        throws IllegalAccessException, IllegalArgumentException,           InvocationTargetException

五、反射机制的实例

public static void main(String args[]){    String[] attrs = {"province", "city", "mediaId", "groupId",   "campaignId", "creativeId"};    // 将attrs转化为list    List<String> keyList = Arrays.asList(attrs);    Class<?> clicksClass = ImpressionClick.class;//获取创建ImpressionClick类的class对象    ImpressionClick clicks = (ImpressionClick) clicksClass.newInstance();//创建实例    Method method = null;//获取某个特定的方法    // 封装特征数据            Method method = null;//获取某个特定的方法            for (String key : keySet) {                if (keyList.contains(key)) {                    String methodStr = "set" + key.toUpperCase().substring(0, 1) + key.substring(1);                    Object value = dataJson.get(key);//获取value值                    if(value instanceof Integer) {//如果value是Integer型                        method = clicksClass.getMethod(methodStr, int.class); // 此时获得了set方法                        Object retValue = method.invoke(clicks, (Integer) value); // 自动装箱,//调用clicks对象的method方法,传递"(Integer) value"作为参数,方法的执行结果是retValue                    }else if(value instanceof Double) {//如果value是Double型                        method = clicksClass.getMethod(methodStr, double.class); // 此时获得了set方法                        Object retValue = method.invoke(clicks, (Double) value); // 自动装箱                    }else if(value instanceof Long) {//如果value是Long型                        method = clicksClass.getMethod(methodStr, long.class); // 此时获得了set方法                        Object retValue = method.invoke(clicks, (Long) value); // 自动装箱                    }else if(value instanceof String) {//如果value是String型                        method = clicksClass.getMethod(methodStr, String.class); // 此时获得了set方法                        Object retValue = method.invoke(clicks, (String) value); // 自动装箱                    }else if(value instanceof Float) {//如果value是Float型                        method = clicksClass.getMethod(methodStr, Float.class); // 此时获得了set方法                        Object retValue = method.invoke(clicks, (Float) value); // 自动装箱                    }else if(value instanceof Boolean) {//如果value是Boolean型                        method = clicksClass.getMethod(methodStr, Boolean.class); // 此时获得了set方法                        Object retValue = method.invoke(clicks, (Boolean) value); // 自动装箱                    }else if(value instanceof Date) {//如果value是Date型                        method = clicksClass.getMethod(methodStr, Date.class); // 此时获得了set方法                        Object retValue = method.invoke(clicks, (Date) value); // 自动装箱                    }                }            }            }** * 展示点击数据 */public class ImpressionClick {    private Long timestamp;       //时间戳(竞价时间UTC毫秒数) Long    private String platformUserId;//用户在平台的ID String    private String impId;         //展示Id String    private String ua;            //User-agent String    private String ip;            //IP地址 String    private String country;       //国家编码 String    private String province;      //省编码 String    private String city;          //市编码 String    private String mediaId;       //App的ID String    private String pageUrl;       //当前页的url String    private int groupId;          //组Id Int    private int campaignId;       //活动ID Int    private int creativeId;       //创意Id Int(素材id)    //省略属性的get set方法

由此可见,这段代码如果写成遍历每个属性的set的话,不仅大大增加了代码量,而且代码写的很死,不灵活。但是反射机制也有它的缺点,就是会增加计算复杂度,所以要慎用。