[JAVA]-反射机制
[JAVA]-反射机制
1.基础概念
Java反射机制在日常的业务开发可能较少应用,但针对一些基础框架的搭建上应用较为广泛
【1】反射机制概念
反射的引入
Object obj = new User();
若程序运行时接收到外部传入的一个对象,该对象的编译类型是Object,但程序又需要调用该对象运行类型的方法:
1.若编译和运行类型都知道,使用 instanceof判断后进行强制类型转化
2.编译时根本无法预知该对象属于什么类,程序只能依靠运行时信息来发现对象的真实信息,需要用到反射
3.需要得到对象真正的类型,需要用到反射
反射机制的定义
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的以及动态调用对象的方法的功能称为Java的反射机制
简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息
反射机制的优缺点
从“动态”、“静态”的方向分析反射机制的优缺点
静态编译:在编译时确定类型,绑定对象,即通过
动态编译:在运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,用以降低类之间的藕合性
反射机制的优点就是可以实现动态创建对象和编译,具备灵活性(在J2EE开发中可体现),运行期类型的判断、动态类加载、动态代理使用反射;缺点在于对程序的性能有影响(使用反射基本上是一种解释操作,告知JVM要做的事情,这类操作总是慢于只直接执行相同的操作)
反射机制能够获取的信息
在运行时判定任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判定任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理;
主要的反射机制类:
java.lang.Class; //类
java.lang.reflect.Constructor;//构造方法
java.lang.reflect.Field; //类的成员变量
java.lang.reflect.Method;//类的方法
java.lang.reflect.Modifier;//访问权限
【2】Class类和Class类实例
Class相关概念
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类就是Class类
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的(一个类在虚拟机中只有一份字节码);
反射就是得到元数据的行为
用类来描述对象(类:描述数据的结构);用元数据来描述Class(MetaData(元数据):描述数据结构的结构)
class对象的获取
// 方式1:通过对象getClass方法
Person person = new Person();
Class<?> class1 = person.getClass();
// 方式2:通过类的class属性
class1 = Person.class;
try {
// 方式3:通过Class类的静态方法forName()来实现
class1 = Class.forName("club.sscai.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
获取class对象的属性、方法、构造函数等
// 获取class对象的所有属性
Field[] allFields = class1.getDeclaredFields();
// 获取class对象的public属性
Field[] publicFields = class1.getFields();
try {
// 获取class指定属性
Field ageField = class1.getDeclaredField("age");
// 获取class指定的public属性
Field desField = class1.getField("des");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 获取class对象的所有声明方法
Method[] methods = class1.getDeclaredMethods();
// 获取class对象的所有方法 包括父类的方法
Method[] allMethods = class1.getMethods();
// 获取class对象的父类
Class parentClass = class1.getSuperclass();
// 获取class对象的所有接口
Class<?>[] interfaceClasses = class1.getInterfaces();
// 获取class对象的所有声明构造函数
Constructor<?>[] allConstructors = class1.getDeclaredConstructors();
// 获取class对象public构造函数
Constructor<?>[] publicConstructors = class1.getConstructors();
try {
// 获取指定声明构造函数
Constructor<?> constructor = class1.getDeclaredConstructor(new Class[]{String.class});
// 获取指定声明的public构造函数
Constructor publicConstructor = class1.getConstructor(new Class[]{});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
// 获取class对象的所有注解
Annotation[] annotations = class1.getAnnotations();
// 获取class对象指定注解
Annotation annotation = class1.getAnnotation(Deprecated.class);
// 获取class对象的直接超类的 Type
Type genericSuperclass = class1.getGenericSuperclass();
// 获取class对象的所有接口的type集合
Type[] interfaceTypes = class1.getGenericInterfaces();
2.功能应用
【1】通过反射机制获取泛型类型
示例
// People类
public class People<T> {}
//Person类继承People类
public class Person<T> extends People<String> implements PersonInterface<Integer> {}
//PersonInterface接口
public interface PersonInterface<T> {}
获取泛型类型
private Class<?> getComponentType(Type type) {
Class<?> componentType = null;
if (type instanceof ParameterizedType) {
// getActualTypeArguments()返回表示此类型实际类型参数的 Type 对象的数组
Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
if (actualTypeArguments != null && actualTypeArguments.length > 0) {
componentType = (Class<?>) actualTypeArguments[0];
}
} else if (type instanceof GenericArrayType) {
// 表示一种元素类型是参数化类型或者类型变量的数组类型
componentType = (Class<?>) ((GenericArrayType) type).getGenericComponentType();
} else {
componentType = (Class<?>) type;
}
return componentType;
}
Person<String> person = new Person<>();
// 方式1:通过对象getClass方法
Class<?> class1 = person.getClass();
Type genericSuperclass = class1.getGenericSuperclass();// 获取class对象的直接超类的 Type
Type[] interfaceTypes = class1.getGenericInterfaces();// 获取class对象的所有接口的Type集合
getComponentType(genericSuperclass);
getComponentType(interfaceTypes[0]);
【2】通过反射机制获取注解信息
通过反射机制获取注解信息再AOP中经常使用
try {
// 首先需要获得与该方法对应的Method对象
Method method = class1.getDeclaredMethod("methodName", new Class[]{String.class, String.class});
// 获取所有的方法注解信息
Annotation[] annotations1 = method.getAnnotations();
// 获取指定的注解信息
Annotation annotation1 = method.getAnnotation(RouterUri.class);
TypeVariable[] typeVariables1 = method.getTypeParameters();
// 拿到所有参数注解信息
Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();
// 获取所有参数class类型
Class<?>[] parameterTypes = method.getParameterTypes();
// 获取所有参数的type类型
Type[] genericParameterTypes = method.getGenericParameterTypes();
// 获取方法的返回类型
Class<?> returnType = method.getReturnType();
// 获取方法的访问权限
int modifiers = method.getModifiers();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
【3】
3.应用实例
【1】比较两个实体对象的信息
分析说明:针对两个对象,Member 和 MemberView,需要进行相应的转化,通常使用的方式是把通过手动get、set操作实现,当对象属性较多的时候,代码或显得臃肿
public class TestClass{
public double eachOrtherToAdd(Integer one,Double two,Integer three){
return one + two + three;
}
}
public class ReflectionDemo{
public static void main(String args[]){
String className = "initLoadDemo.TestClass";
String methodName = "eachOrtherToAdd";
String[] paramTypes = new String[]{"Integer","Double","int"};
String[] paramValues = new String[]{"1","4.3321","5"};
// 动态加载对象并执行方法
initLoadClass(className, methodName, paramTypes, paramValues);
}
@SuppressWarnings("rawtypes")
private static void initLoadClass(String className,String methodName,String[] paramTypes,String[] paramValues){
try{
// 根据calssName得到class对象
Class cls = Class.forName(className);
// 实例化对象
Object obj = cls.newInstance();
// 根据参数类型数组得到参数类型的Class数组
Class[] parameterTypes = constructTypes(paramTypes);
// 得到方法
Method method = cls.getMethod(methodName, parameterTypes);
// 根据参数类型数组和参数值数组得到参数值的obj数组
Object[] parameterValues = constructValues(paramTypes,paramValues);
// 执行这个方法并返回obj值
Object returnValue = method.invoke(obj, parameterValues);
System.out.println("结果:"+returnValue);
}catch (Exception e){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static Object[] constructValues(String[] paramTypes,String[] paramValues){
Object[] obj = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++){
if(paramTypes[i] != null && !paramTypes[i].trim().equals("")){
if ("Integer".equals(paramTypes[i]) || "int".equals(paramTypes[i])){
obj[i] = Integer.parseInt(paramValues[i]);
}else if ("Double".equals(paramTypes[i]) || "double".equals(paramTypes[i])){
obj[i] = Double.parseDouble(paramValues[i]);
}else if ("Float".equals(paramTypes[i]) || "float".equals(paramTypes[i])){
obj[i] = Float.parseFloat(paramValues[i]);
}else{
obj[i] = paramTypes[i];
}
}
}
return obj;
}
@SuppressWarnings("rawtypes")
private static Class[] constructTypes(String[] paramTypes){
Class[] cls = new Class[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++){
if(paramTypes[i] != null && !paramTypes[i].trim().equals("")){
if ("Integer".equals(paramTypes[i]) || "int".equals(paramTypes[i])){
cls[i] = Integer.class;
}else if ("Double".equals(paramTypes[i]) || "double".equals(paramTypes[i])){
cls[i] = Double.class;
}else if ("Float".equals(paramTypes[i]) || "float".equals(paramTypes[i])){
cls[i] = Float.class;
}else{
cls[i] = String.class;
}
}
}
return cls;
}
}
【2】
【3】
4.扩展说明
如何提高反射效率?
- 在系统启动阶段使用反射。
- 将反射得到元数据保存起来,使用时,只需从内存中调用即可。
- hotspot虚拟机会对执行次数较多的方法进行优化(例如使用jit技术)。
- 使用高性能的反射库,应该会比自己写缓存效果好。
反射框架:joor?apache的commons工具类
Class.forName这个方法比较耗时,它实际上调用了一个本地方法,通过这个方法来要求JVM查找并加载指定的类。所以我们在项目中使用的时候,可以把Class.forName返回的Class对象缓存起来,下一次使用的时候直接从缓存里面获取,这样就极大的提高了获取Class的效率。同理,在我们获取Constructor、Method等对象的时候也可以缓存起来使用,避免每次使用时再来耗费时间创建
?? 注解和反射概念?
注解不止可以通过反射来获取,也可以通过注解在编译时生成字节码,效率要好于反射。 优化反射的通常做法就是通过生成字节码,动态生成比如cglib
,静态生成比如Javassist
參考学习链接:
https://segmentfault.com/q/1010000003004720
5.反射引用场景
Java反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java的反射机制。
应用场景:
【1】开发各种通用的工具,比如对任意类进行序列化或者反序列化
【2】动态获取类的信息,进行运行时的类型判断
【3】动态调用类的方法,比如动态代理
【4】对于某些安全级别要求不高的环境,可以用于动态加载和执行代码
简单DEMO
操作步骤
【1】创建一个实体类,提供属性、方法
【2】创建反射测试类,根据包名、方法名动态执行方法
public class User {
private String name;
public void callMyName(String name){
System.out.println("hello:" + name);
}
}
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 使用Class.forName()方法加载类(加载指定的类)
Class<?> clazz = Class.forName("reflection.User");
// 使用clazz.newInstance()方法创建类的实例
Object myClassInstance = clazz.newInstance();
// 获取特定的方法
Method myMethod = clazz.getMethod("callMyName", String.class);
// 调用方法
myMethod.invoke(myClassInstance, "noob");
} catch (Exception e) {
e.printStackTrace();
}
}
}