Java反射机制与实例
程序员文章站
2022-05-28 19:16:17
...
java的反射机制其实更多用于一些框架中,实际的项目中比较少的能用到。
最近要实现一个小的功能就是将前端返回的json对象转化成相应的实体类从而存储到数据库中。(如果对每个成员变量进行判断,会很麻烦,就使用反射来实现)
代码质量写的不忍直视哈,主要看一下反射的应用:
上面代码很好理解,就是每次循环json对象,取得相应的key值到实体类中找到相应的Fiels对象,找到后存储相应的value值到成员变量的set方法中。
一、Class类
1.在面向对象的世界中:万事万物皆为对象
那么每次我们新建的类是不是对象?
答案是:新建的.java类也是对象-->是java.lang.Class类的实例对象。
2.这个对象是如何表示的呢?官方的说法是该类的类类型
比如我们在工程中新建的AssetInfo.java类如何表示成java.lang.Class类的实例对象。
①任何一个类都有一个隐含的静态成员变量class
②已知该类的实例化对象(以上面的代码为例)
c1,c2表示的是AssetInfo.java的类类型(class type)
同时AssetInfo.java类也有自己的实例对象就是assetInfo
那么此时if(c1==c2)? 答案是true:一个类只能是Class类的一个实例对象
③动态加载类类型的方法
通过以上的方式能够得到该类的类类型,并且根据类类型可以创建该类的实例对象:
c1=c2=c3
3.类的动态加载
类有两种加载方式:动态加载和静态加载
①动态加载:编译时刻不去加载类,运行时刻去加载使用的类
②静态加载:编译时刻就去加载所有可能使用的类的方式
关键字new来实例化对象就是静态加载
如果使用Eclipse并不太好区分编译和运行,如果想看动态加载和静态加载的区别最好自己写使用doc编译和运行
如果使用静态方法实例化对象,并且没有提供相应的类,那么在编译的过程中就是会报错的。
而使用动态加载类的方式,即使不提供相应的类,编译也是可以通过,在运行时候才会报错。
4.基本数据类型
5.Class基本API
要取得类的相应的信息,必须先获得该类的类类型,通过类类型再去获取类的成员变量成员函数等信息。
①类的成员方法:java.lang.reflect.Method;封装了关于成员函数的操作信息
②类的成员变量:java.lang.reflect.Fiels;封装了关于成员变量的操作信息
③类的构造函数:java.lang.reflect.Constructor;封装了构造函数的操作信息
Class中有很多的方法,可以去参考一下API
二、方法的反射
1.获取某个方法
如果我们想通过反射的方式来获取到相应的方法需要什么必须条件呢?
方法的名称和方法的参数列表能够唯一决定某个方法
2.方法调用
通过反射方式取得该方法如何调用呢?
method.invoke(对象,参数列表);
3.认识泛型的本质
因为是动态加载的方式,所以反射的操作都是编译之后的操作
if(c1==c2)-->true : 说明编译之后的集合都是去泛型的
java中的泛型只是为了防止错误的输入,只在编译有效,绕过编译就无效了
三、应用
最后奉上我们组大神的反射代码,原谅我有点没看懂 差距不是一点半点呵呵
欢迎大家吐槽。。。。
最近要实现一个小的功能就是将前端返回的json对象转化成相应的实体类从而存储到数据库中。(如果对每个成员变量进行判断,会很麻烦,就使用反射来实现)
public static void main(String[] args) { JSONObject json = new JSONObject(); AssetInfo assetInfo = new AssetInfo(); json.put("name", "sun"); json.put("num", "23"); reflectEntity(assetInfo,json); System.out.println(assetInfo.getName()+"*********"+assetInfo.getNum()); } public static void reflectEntity(AssetInfo assetInfo,JSONObject json){ Class ca = assetInfo.getClass(); for(Iterator<String> iter = json.keys();iter.hasNext();){ String key = iter.next(); Object value = json.get(key); if(value != null){ try { //在assetInfo实体类的类类型中获取到json相应key的Field对象 Field field = null; field = ca.getDeclaredField(key); field.setAccessible(true); String type = field.getType().getName(); //根据类型进行set操作 if(type.endsWith("String")){ field.set(assetInfo, json.getString(key)); }else{ //省略。。。 } } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
代码质量写的不忍直视哈,主要看一下反射的应用:
上面代码很好理解,就是每次循环json对象,取得相应的key值到实体类中找到相应的Fiels对象,找到后存储相应的value值到成员变量的set方法中。
一、Class类
1.在面向对象的世界中:万事万物皆为对象
那么每次我们新建的类是不是对象?
答案是:新建的.java类也是对象-->是java.lang.Class类的实例对象。
2.这个对象是如何表示的呢?官方的说法是该类的类类型
比如我们在工程中新建的AssetInfo.java类如何表示成java.lang.Class类的实例对象。
①任何一个类都有一个隐含的静态成员变量class
Class c1 = AssetInfo.class;
②已知该类的实例化对象(以上面的代码为例)
Class c2 = assetInfo.getClass();
c1,c2表示的是AssetInfo.java的类类型(class type)
同时AssetInfo.java类也有自己的实例对象就是assetInfo
那么此时if(c1==c2)? 答案是true:一个类只能是Class类的一个实例对象
③动态加载类类型的方法
Class c3 = Class.forName("com.reflect.testDemo");//是包含包名的完整路径
通过以上的方式能够得到该类的类类型,并且根据类类型可以创建该类的实例对象:
AssetInfo ai = (AssetInfo)c1.newInstance();//做一个强制转换
c1=c2=c3
3.类的动态加载
类有两种加载方式:动态加载和静态加载
①动态加载:编译时刻不去加载类,运行时刻去加载使用的类
Class c = Class.forName("类的全称");//这种方式是动态加载的,不仅仅表示了该类的类类型 AssetInfo ai = (AssetInfo)c.newInstance();//实例化对象
②静态加载:编译时刻就去加载所有可能使用的类的方式
关键字new来实例化对象就是静态加载
AssetInfo ai = new AssetInfo();
如果使用Eclipse并不太好区分编译和运行,如果想看动态加载和静态加载的区别最好自己写使用doc编译和运行
如果使用静态方法实例化对象,并且没有提供相应的类,那么在编译的过程中就是会报错的。
而使用动态加载类的方式,即使不提供相应的类,编译也是可以通过,在运行时候才会报错。
4.基本数据类型
Class c1 = int.class;//int的类类型 Class c2 = String.class;//String的类类型 //一个类中的所有关键字都存在类类型 c1.getName();//类的全称,带包名 c2.getSimpleName();//不包含包名
5.Class基本API
要取得类的相应的信息,必须先获得该类的类类型,通过类类型再去获取类的成员变量成员函数等信息。
①类的成员方法:java.lang.reflect.Method;封装了关于成员函数的操作信息
public static void classMethodMessage(Object obj){ Class c = obj.getClass(); c.getName();//类的名称 Method[] ms = c.getMethods();//所有的public的函数,包括父类继承而来的 Method[] ms = c.getDeclaredMethods();//获取所有该类自己声明的方法 ms[0].getName();//获取该方法的名称 Class returnType = ms[0].getReturnType();//获取返回值的类类型 returnType.getName();//获取返回值的名称 Class[] paramTypes = ms[0].getParameterTyprs();//获取方法的参数列表的类类型 paramType[0].getName()//参数类型的名称 }
②类的成员变量:java.lang.reflect.Fiels;封装了关于成员变量的操作信息
public static void classFieldMessage(Object obj){ Class c = obj.getClass(); Field[] fs = c.getFields();//获取所有public的成员变量 Field[] fs = c.getDeclaredFields();//获取该类自己声明的所有成员变量 fs[0].getName();//获取成员变量名 Class fieldType = fs[0].getType();//获取成员变量类型的类类型 --> int.class fieldType.getName();//获取成员变量类型的名称 }
③类的构造函数:java.lang.reflect.Constructor;封装了构造函数的操作信息
public static void classConstructorMessage(Object obj){ Class c = obj.getClass(); Constructor[] cs = c.getConstructors();//获取所有public的构造函数 Constructor[] cs = c.getDeclaredConstructors();//获取所有的构造函数 cs[0].getName();//获取构造函数名称 Class[] paramTypes = cs[0].getParameterTypes();//获取构造函数的参数列表的类类型 paramTypes[0].getName();//获取参数列表的名称 }
Class中有很多的方法,可以去参考一下API
二、方法的反射
1.获取某个方法
如果我们想通过反射的方式来获取到相应的方法需要什么必须条件呢?
方法的名称和方法的参数列表能够唯一决定某个方法
2.方法调用
通过反射方式取得该方法如何调用呢?
method.invoke(对象,参数列表);
public void userMethod(Object obj){ Class c = obj.getClass(); Method m = c.getDeclaredMethod("printMsg",int.class,int.class);//获取相应的方法,两种写法 Method m = c.getDeclaredMethod("printMsg",Class[]{int.class,int.class}); Object o = m.invoke(obj,10,15);//调用方法,如果有返回值返回具体的返回值,没有返回值返回null,有以下两种写法 Object o = m.invoke(obj,Object[]{10,15}); } public void printMsg(int a,int b){ System.out.println(a+b); }
3.认识泛型的本质
因为是动态加载的方式,所以反射的操作都是编译之后的操作
ArrayList list = new ArrayList(); ArrayList<String> list1 = new ArrayList<String>(); Class c1 = list.getClass(); Class c2 = list1.getClass();
if(c1==c2)-->true : 说明编译之后的集合都是去泛型的
java中的泛型只是为了防止错误的输入,只在编译有效,绕过编译就无效了
Method m = c2.getMethod("add",Object.class);//获取ArrayList类的add方法 m.invoke(list1,1);//调用该方法向list1中添加一个int类型,查看是否能成功,list1有泛型规范只能添加String list1.size();//list 的大小是1,表示插入成功了。说明反射绕过了泛型成功添加了数据。
三、应用
最后奉上我们组大神的反射代码,原谅我有点没看懂 差距不是一点半点呵呵
/** * * [将一个bean中字段值 copy至另一个bean相应字段中,] * * @comment [注释说明] * * @param <T> * @param originalBean * @param targetClazz 必须包含默认构造方法 * @return */ public static <T> T copyBean2Another(Object originalBean ,Class<T> targetClazz) { if(targetClazz == null || originalBean == null) { throw new UniEAPBusinessException("空指针"); } Class<?> originalClass = originalBean.getClass(); Method[] targetMethods = targetClazz.getMethods(); Constructor<T>[] targetConstructors = (Constructor<T>[]) targetClazz.getConstructors(); TypeVariable<Constructor<T>>[] typeVariableArray = null; Constructor<T> targetConstructor = null; for(Constructor<T> tempConstructor : targetConstructors) { typeVariableArray = tempConstructor.getTypeParameters(); if(typeVariableArray == null || typeVariableArray.length < 1) { targetConstructor = tempConstructor; break; } } if(targetConstructor == null) { throw new UniEAPBusinessException("目标类中没有默认构造方法"); } T a = null; try { a = targetConstructor.newInstance(); String fieldName = null; Method orginalBeanGetMethod = null; for(Method tempTargetMethod : targetMethods) { if(tempTargetMethod.getName().startsWith("set")) { fieldName = tempTargetMethod.getName().substring(3); try { orginalBeanGetMethod = originalClass.getMethod("get" + fieldName); tempTargetMethod.invoke(a, orginalBeanGetMethod.invoke(originalBean)); } catch (Throwable e) { if(logger.isInfoEnabled()) { logger.info(originalClass.getName() + "中没有get" + fieldName + "方法"); } } } } } catch (IllegalArgumentException e) { logger.error(e.getMessage(), e); throw new UniEAPBusinessException("目标类中没有默认构造方法"); } catch (InstantiationException e) { logger.error(e.getMessage(), e); throw new UniEAPBusinessException("目标类中没有默认构造方法"); } catch (IllegalAccessException e) { logger.error(e.getMessage(), e); throw new UniEAPBusinessException("目标类中没有默认构造方法"); } catch (InvocationTargetException e) { logger.error(e.getMessage(), e); throw new UniEAPBusinessException("目标类中没有默认构造方法"); } return a; }
欢迎大家吐槽。。。。