跳至主要內容

[JAVA]-反射机制

holic-x...大约 9 分钟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.扩展说明

如何提高反射效率?

  1. 在系统启动阶段使用反射。
  2. 将反射得到元数据保存起来,使用时,只需从内存中调用即可。
  3. hotspot虚拟机会对执行次数较多的方法进行优化(例如使用jit技术)。
  4. 使用高性能的反射库,应该会比自己写缓存效果好。

反射框架: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();
        }
    }
}
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3