,好记性不如烂笔头
反射机制
为了更好的理解java的反射机制,最好先对java的泛型有所了解。java泛型就是参数化类型,即为所操作的数据类型指定一个参数。如果只指定了<?>,而没有extends,则默认是允许Object及其下的任何Java类。
List > list = null;{ // 具体实现时限定了List所能add的数据类型 list = new ArrayList
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)); } }}