Java反射机制

鲁春利的工作笔记,好记性不如烂笔头



反射机制

为了更好的理解java的反射机制,最好先对java的泛型有所了解。java泛型就是参数化类型,即为所操作的数据类型指定一个参数。如果只指定了<?>,而没有extends,则默认是允许Object及其下的任何Java类。

List<? extends Map<String, String>> list = null;
{
    // 具体实现时限定了List所能add的数据类型
    list = new ArrayList<Map<String, String>>();
}
{
    // 具体实现时限定了List所能add的数据类型
    list = new ArrayList<HashMap<String, String>>();
}
logger.info("list is {}", list);

Java运行时,对任意一个类,想知道它有哪些属性和方法,对于任意一个对象,想调用它的任意一个方法,都是可以实现的,这来自JAVA的反射机制

1、JAVA的反射机制主要功能:
    a、在运行时判断任意一个对象所属的类。
    b、在运行时构造任意一个类的对象。
    c、在运行时判断任意一个类所具有的成员变量和方法。
    d、在运行时调用任意一个对象的方法
    前提是在运行时,不是编译时,也就是在运行前并不知道调用哪一个类,通过反射就可以做到这些。

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

wKiom1iBZPHzzIkEAAd1TSmrJaU921.jpg


3、Class类是Reflection API 中的核心类   

    Class类是Reflection API 中的核心类。在类加载时,java虚拟机会自动创建相应的Class对象。

    在java.lang.Object 类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类。
    Class类的实例用于表示运行时的java数据类型,包括类、接口、数组、枚举、注解、基本数据类型甚至void等。

wKiom1iBZS2CZItVAAQgDgMzd_M397.jpg

获得你想操作的类的 java.lang.Class 对象

  • 针对引用数据类型:

调用静态方法:Class.forName();

Class.forName("p1.Person");
Class.forName("com.mysql.jdbc.Driver");


调用Object类中定义的getClass()方法

Person person = new Person();
Class  cs = p.getClass();
Class  strClass = "Hello World".getClass();


使用.class表达式

Class  strClass = String.class;
Class  personClass = p1.Person.class;
Class  jdbcClass = com.mysql.jdbc.Driver.class;



  • 针对基本数据类型及void

使用.class表达式

Class  intClass = int.class;
Class  dobClass = double.class;
Class  voidClass = void.class;


调用相应封装类的Type属性

Class IntegerClass = Integer.TYPE;
Class voicClass = Void.TYPE;


【实例1】

读取命令行参数指定的类名,然后打印这个类所具有的方法信息。即JAVA的反射机制功能中的“在运行时判断任意一个类所具有的方法



package com.invicme.tools.reflect;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.invicme.tools.utils.StringUtils;

/**
 * 
 * @author lucl
 * @version 2017-01-19
 * 
 * 测试运行时获取类的实例及参数
 */
public class ReflectTest001 {
    // 
    private static Logger logger = LoggerFactory.getLogger(ReflectTest001.class);

    public static void main(String[] args) throws Exception {
        if (null == args || args.length <= 0 || StringUtils.isBlank(args[0])) {
            return;
        }
        // 加载并初始化命令行参数指定的类
        Class<?> clazz = Class.forName(args[0]);
        String className = clazz.getName();
        logger.info("class name is {}", className);
        // 获得类的所有方法
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            logger.info("\tmethod is {}", method.toString());
        }
    }

}

在命令行里输入参数,此处参数必须为类的全限定名

wKiom1iAWzeRIipbAAAyN0lL5RY394.jpg

运行输出该类所具有的方法,包括private      
wKioL1iAW7GgDmI3AAC-vUfVaGM663.jpg

(1). Class.forName(args[0])  传入的是类的全称,返回的是与这个类所对应的一个Class类的实例

(2). Method methods[] = classType.getDeclaredMethods()  获得该类所有的方法,包括private的。


【实例2】

这个例子只能复制简单的JavaBean,假定JavaBean 的每个属性都有public 类型的getXXX()和setXXX()方法。体现了JAVA的反射机制中的“在运行时判断任意一个类所具有的属性”、“在运行时调用任意一个对象的方法”和“在运行时构造任意一个类的对象

package com.invicme.tools.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.invicme.common.model.test.TestUserInfo;
import com.invicme.tools.utils.GUIDUtils;

/**
 * 
 * @author lucl
 * @version 2017-01-19
 * 
 * 测试运行时获取对象的拷贝
 */
public class ReflectTest002 {
    
    // 
    private static Logger logger = LoggerFactory.getLogger(ReflectTest002.class);
    
    public Object copy (Object userInfo) throws Exception {
        // 获得对象的类的类型
        Class<? extends Object> clazz = userInfo.getClass();
        logger.info("class name is {}", clazz.getName());
//        {
//            // 先调用Class类的getConstructor()方法获得一个Constructor对象
//            Constructor<? extends Object> constructor = clazz.getConstructor(new Class[]{});
//            // 它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。
//            Object newInstance = constructor.newInstance(new Object[]{});
//        }
        // 或者直接通过Class类的newInstance方法
        Object newInstance = clazz.newInstance();
        
        // 获得对象的所有属性
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();
            if ("serialVersionUID".equals(fieldName)) {
                continue;
            }
            if (fieldName.startsWith("is")) {
                continue;
            }
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            // 获得和属性对应的getXXX()方法的名字
            String getMethodName = "get" + firstLetter + fieldName.substring(1);
            // 获得和属性对应的setXXX()方法的名字
            String setMethodName = "set" + firstLetter + fieldName.substring(1);

            // 获取源实例的数据
            Object invoke = null;
            {
                Method method = clazz.getMethod(getMethodName, new Class[]{});
                // 调用原对象的getXXX()方法
                invoke = method.invoke(userInfo, new Object[]{});
                logger.info("field name is {}, field value is {}, get method name is {}, set method name is {}", fieldName, invoke, getMethodName, setMethodName);
            }
            // 设置为新复制出来的实例
            {
                Method method = clazz.getMethod(setMethodName, new Class[]{field.getType()});
                // 调用拷贝对象的setXXX()方法
                method.invoke(newInstance, new Object[]{invoke});
            }
        }
        
        return newInstance ;
    }

    public static void main(String[] args) throws Exception {
        // 
        ReflectTest002 reflect = new ReflectTest002();
        // 
        TestUserInfo userInfo = new TestUserInfo();
        userInfo.setPkid(GUIDUtils.getGUID());
        userInfo.setUserName("maneo.lu");
        userInfo.setUserPwd("123456");
        userInfo.setRealName("鲁春利");
        logger.info("source user info is {}", userInfo);
        // 
        Object objectCopy = reflect.copy(userInfo); 
        logger.info("copy user info is {}", objectCopy);
    }

}

打印该对象所属的类的名字 后打印出原对象中所有属性及对应值  最后打印出新建对象复制原对象后的属性及对应值

wKiom1iAbNiRIvjPAAFHYe9KcDY052.jpg

(1). 获得对象的类的类型 Class<?> classType = object.getClass();与Class.forName()是一样的结果,返回一个对象运行时的Class,这个Class描述了当前这个对象所具有的一些属性和方法,也就是内部的构造。getClass方法定义在Object类里面,也就是JAVA中任何一个类都有这个方法
(2). Object objectCopy = classType.getConstructor(new Class[] {}).newInstance(new Object[] {});这句话主要的目的是通过默认的构造方法创建一个该Class类的实例。通过Class实例调用getConstructor方法,可以获得当前对象的构造方法。参数是用来辨别返回哪个构造方法的,所以是Class类型数组,无参数表示返回默认构造方法。
newInstance方法,通过当前构造方法生成当前类的一个实例。


【实例3】

该类的main()方法中,运用反射机制调用一个ReflectTest003对象的add()和echo()方法。add()方法的两个参数为int 类型,获得表示add()方法的Method对象的代码如下:

Method method = clazz.getMethod("add", new Class[]{int.class, int.class});

Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。

Method类的invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回。

体现了JAVA的反射机制功能中的“在运行时获得任意一个类的方法”、“在运行时调用任意一个对象的方法

package com.invicme.tools.reflect;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author lucl
 * @version 2017-01-19
 * 
 * 通过反射获取类的方法并调用
 *
 */
public class ReflectTest003 {
    // 
    private static Logger logger = LoggerFactory.getLogger(ReflectTest003.class);
    
    public int add (int param1, int param2) {
        return param1 + param2;
    }
    
    public String echo (String msg) {
        return "echo : " + msg;
    } 

    public static void main(String[] args) throws Exception {
        // 获得类的类型
        Class<? extends ReflectTest003> clazz = ReflectTest003.class;
        // 生成实例
        ReflectTest003 newInstance = clazz.newInstance();
        // 调用ReflectTest003对象的add()方法
        {
            Method method = clazz.getMethod("add", new Class[]{int.class, int.class});
            Object invoke = method.invoke(newInstance, new Object[]{1, 2});
            logger.info("the add method result is {}", invoke);
        }
        // 调用ReflectTest003对象的echo()方法
        {
            Method method = clazz.getMethod("echo", new Class[]{String.class});
            Object invoke = method.invoke(newInstance, new Object[]{"Hello"});
            logger.info("the echo method result is \"{}\"", invoke);
        }
    }

}

wKiom1iAdLHh35XJAAAy62_Uz7E630.jpg


【实例4】

通过反射机制获取类的注解。

com.invicme.common.persistence.annotation.MyBatisDao

package com.invicme.common.persistence.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.stereotype.Component;

/**
 * @author lucl
 * @version 2013-8-28
 * 
 * 标识MyBatis的DAO,方便{@link org.mybatis.spring.mapper.MapperScannerConfigurer}的扫描。 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Component
public @interface MyBatisDao {
    
    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any
     */
    String value() default "";

}


com.invicme.tools.reflect.ReflectTest004

package com.invicme.tools.reflect;

import java.lang.annotation.Annotation;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.invicme.common.persistence.annotation.MyBatisDao;

/**
 * 
 * @author lucl
 * @version 2017-01-19
 * 
 * 获取类的注解
 *
 */
@MyBatisDao(value="@MyBatisDao")
public class ReflectTest004 {
    // 
    private static Logger logger = LoggerFactory.getLogger(ReflectTest004.class);
    
    public static void main(String[] args) {
        // 
        Class<? extends ReflectTest004> clazz = ReflectTest004.class;
        MyBatisDao myBatisDao = clazz.getAnnotation(MyBatisDao.class);
        String value = myBatisDao.value();
        logger.info("annotation value is {}", value);
        Class<? extends Annotation> annotationType = myBatisDao.annotationType();
        logger.info("annotation type is {}", annotationType.getName());
        Annotation[] annotations = annotationType.getAnnotations();
        for (Annotation annotation : annotations) {
            logger.info("sub annotation is {}", annotation.annotationType());
        }
        //
        Class<?>[] interfaces = clazz.getInterfaces();
        for (Class<?> interfacz : interfaces) {
            logger.info("interface name is {}", interfacz.getName());
        }
        //
        if (null != clazz.getComponentType()) {
            logger.info("component type is {}", clazz.getComponentType().getName());
        }
    }

}

【实例4】

通过Array操作数组

package com.invicme.tools.reflect;

import java.lang.reflect.Array;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.invicme.common.model.test.TestUserInfo;

/**
 * 
 * @author lucl
 * @version 2017-01-20
 * 
 * 动态数组
 *
 */
public class ReflectTest005 {
    // 
    private static Logger logger = LoggerFactory.getLogger(ReflectTest005.class);

    public static void main(String[] args) {
        // 初始化数组
        int[] records = { 1, 2, 3 };
        TestUserInfo[] ps = { 
            new TestUserInfo("张三", "123456"), 
            new TestUserInfo("李四", "123456"),
            new TestUserInfo("王五", "123456") 
        };
        // 使用Array类来操作数组对象
        records = (int[]) incrementArray(records);
        ps = (TestUserInfo[]) incrementArray(ps);
        // 打印扩容之后的数组内容 
        list(records);
        list(ps);

    }
    
    /**
     * 
     * @param array
     * @return
     */
    public static Object incrementArray(Object array) {
        // 返回表示数组组件类型的 Class
        Class<?> componentType = array.getClass().getComponentType();
        // component type is int
        // component type is class com.invicme.common.model.test.TestUserInfo
        logger.info("component type is {}", componentType);
        // 返回指定数组对象的长度
        int size = Array.getLength(array);
        // 新数组
        Object newArray = Array.newInstance(componentType, size * 2);
        // 创建一个具有指定的组件类型和长度的新数组。
        for (int i = 0; i < size; i++) {
            Object o = Array.get(array, i);// 返回指定数组对象中索引组件的值
            Array.set(newArray, i, o);
            // 将指定数组对象中索引组件的值设置为指定的新值。newArray中i位置的值设为o指定的值
        }
        // System.arraycopy(array, 0, newArray, 0, size);
        return newArray;
    }
    
    /**
     * 
     * @param array
     */
    public static void list(Object array) {
        int size = Array.getLength(array);
        for (int i = 0; i < size; i++) {
            logger.info("{} [ {} ]的 值是 {}", array.getClass().getName(), i, Array.get(array, i));
        }
    }
}

wKioL1iBc7vxaHp_AAC5SQMZ42o757.jpg





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章