,好记性不如烂笔头


反射机制

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

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

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

1、JAVA的反射机制主要功能:

    a、在运行时判断任意一个对象所属的类。
    b、在运行时构造任意一个类的对象。
    c、在运行时判断任意一个类所具有的成员变量和方法。
    d、在运行时调用任意一个对象的方法
    前提是在运行时,不是编译时,也就是在运行前并不知道调用哪一个类,通过反射就可以做到这些。
2、在JDK中,主要由以下类来实现JAVA反射机制,这些类位于java.lang.reflect包中:

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

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

    在java.lang.Object 类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类。

    Class类的实例用于表示运行时的java数据类型,包括类、接口、数组、枚举、注解、基本数据类型甚至void等。

获得你想操作的类的 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());        }    }}

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

运行输出该类所具有的方法,包括private      

(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
 clazz = userInfo.getClass();        logger.info("class name is {}", clazz.getName());//        {//            // 先调用Class类的getConstructor()方法获得一个Constructor对象//            Constructor
 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);    }}

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

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

【实例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@Componentpublic @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
 clazz = ReflectTest004.class;        MyBatisDao myBatisDao = clazz.getAnnotation(MyBatisDao.class);        String value = myBatisDao.value();        logger.info("annotation value is {}", value);        Class
 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));        }    }}