美文网首页
Java反射(一)

Java反射(一)

作者: kdong | 来源:发表于2018-10-08 17:58 被阅读0次

    一、概述

    (1)Java反射机制定义

    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    (2)Java反射机制的功能

         1)在运行时判断任意一个对象所属的类。
    
          2)在运行时构造任意一个类的对象。
    
          3)在运行时判断任意一个类所具有的成员变量和方法。
    
          4)在运行时调用任意一个对象的方法。
    
          5)生成动态代理。
    

    (3)Java反射机制的应用场景

           1)逆向代码,例如反编译。
    
           2)与注解相结合的框架,例如Retrofit
    
           3)单纯的反射机制应用框架,例如EventBus
    
           4)动态生成类框架,例如Gson。
    

    二、通过Java反射查看类信息

    (1)Class对象

    每个类被加载之后,系统就会为该类生成一个对应的Class对象。通过该Class对象就可以访问到JVM中的这个类。

    在Java程序中获得Class对象通常有如下三种方式:

    1)使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定名(必须添加完整包名)。

    2)调用某个类的class属性来获取该类对应的Class对象。

    3)调用某个对象的getClass()方法。该方法是java.lang.Object类中的一个方法。

     //第一种方式,使用Class.forName静态方法,传入完整类路径获取
            try{
                Class<?> class1=Class.forName("com.qd.threadcontact.Student");
            }catch (ClassNotFoundException e)
            {
                e.printStackTrace();
            }
    
            //第二种方式,通过类的class属性
            Class<Student> class2=Student.class;
    
            //第三种方式,通过对象的getClass方法获取
            Student stu=new Student();
            Class<?> class3=stu.getClass();
    

    (2)获取class对象的属性、方法、构造函数等
    1)获取class对象的成员变量

     //获取class对象的所有属性
            Field[] allFields = class2.getDeclaredFields();
            //获取class对象的public属性
            Field[] publicFields = class2.getFields();
            try{
                //获取class指定属性
                Field ageField = class2.getDeclaredField("name");
                //获取class指定的public属性  由于age属性是private,所以会抛出异常
                Field desField = class2.getField("age");
            }catch (NoSuchFieldException e)
            {
                e.printStackTrace();
            }
    
            for(Field f:allFields)
            {
                System.out.println(f.getName());
            }
    
            //Student类中并没有public属性
            if(publicFields.length==0)
            {
                System.out.println("没有public属性");
            }else{
                System.out.println("有public属性");
            }
    

    2)获取class对象的方法

     //获取class对象的所有声明方法
            Method[] methods = class2.getDeclaredMethods();
            //获取class对象的所有public方法 包括父类的方法
            Method[] allMethods = class2.getMethods();
            try{
                //返回次Class对象对应类的、带指定形参列表的public方法
                Method method = class2.getMethod("test3", Student.class);
                //返回次Class对象对应类的、带指定形参列表的方法
                Method declaredMethod = class2.getDeclaredMethod("test1",Student.class);
    
    
            }catch(NoSuchMethodException e)
            {
                e.printStackTrace();
            }
    
    
            //打印出本类的所有方法,不包括父类的方法
            for(Method m:methods)
            {
                System.out.println(m.getName());
            }
    
            System.out.println("<<-------------------->>");
    
            //返回Class对象所有的public方法,包括父类的方法
            for(Method m:allMethods)
            {
                System.out.println(m.getName());
            }
    
    

    3)获取class对象的构造函数

     //获取class对象的所有声明构造函数
            Constructor<?>[] allConstructors = class2.getDeclaredConstructors();
            //获取class对象public构造函数
            Constructor<?>[] publicConstructors = class2.getConstructors();
            try{
                //获取指定声明构造函数
                Constructor<?> constructor = class2.getDeclaredConstructor(String.class);
                //获取指定声明的public构造函数
                Constructor publicConstructor = class2.getConstructor(String.class,Integer.class);//获取指定声明的public构造函数
            }catch (NoSuchMethodException e)
            {
                e.printStackTrace();
            }
    
    
            for(Constructor<?> c:allConstructors)
            {
                System.out.println(c.toGenericString());
            }
    
            for(Constructor<?> c:publicConstructors)
            {
                System.out.println(c.toGenericString());
            }
    

    4)其他方法

     Annotation[] annotations = class2.getAnnotations();//获取class对象的所有注解
            Annotation annotation =class2.getAnnotation(TestAnno1.class);//获取class对象指定注解
            Type genericSuperclass = class2.getGenericSuperclass();//获取class对象的直接超类的 Type
            Type[] interfaceTypes = class2.getGenericInterfaces();//获取class对象的所有接口的type集合
    
            for(Annotation a:annotations)
            {
                System.out.println(a.annotationType().getName());
            }
    
            System.out.println("type:"+genericSuperclass.getTypeName());
    
            for(Type t:interfaceTypes)
            {
                System.out.println(t.getTypeName());
            }
    

    (3)获取class对象的信息

    boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型
    boolean isArray = class1.isArray();//判断是否是集合类
    boolean isAnnotation = class1.isAnnotation();//判断是否是注解类
    boolean isInterface = class1.isInterface();//判断是否是接口类
    boolean isEnum = class1.isEnum();//判断是否是枚举类
    boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类
    boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰
    String className = class1.getName();//获取class名字 包含包名路径
    Package aPackage = class1.getPackage();//获取class的包信息
    String simpleName = class1.getSimpleName();//获取class类名
    int modifiers = class1.getModifiers();//获取class访问权限
    Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类
    Class<?> declaringClass = class1.getDeclaringClass();//外部类
    

    三、通过反射生成并操作对象
    (1)生成类的实例信息
    1)使用Class对象的newInstance()方法来创建该Class对象对应类的实例。这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。
    2)先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。

     //第一种方式 newInstance()
            Class<Student> classStudent=Student.class;
            try {
                //必须得确认是否有无参数构造函数
                Student student=classStudent.newInstance();
                if(student!=null)
                {
                    System.out.println("Student Name:"+student.getName());
    
                }else{
                    System.out.println("Student null");
                }
    
            }catch (InstantiationException e)
            {
                e.printStackTrace();
            }catch (IllegalAccessException e){
                e.printStackTrace();
            }
    
            //第二种方式 对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成
            try{
                Class<Student> studentClass=Student.class;
                Constructor<?> constructor = studentClass.getDeclaredConstructor(String.class,int.class);//获取指定声明构造函数
                Student student=(Student)constructor.newInstance("qd",20);
                System.out.println("Student Name:"+student.getName());
            }catch (NoSuchMethodException e)
            {
                e.printStackTrace();
            }catch (InstantiationException e)
            {
                e.printStackTrace();
            }catch (InvocationTargetException e)
            {
                e.printStackTrace();
            }catch(IllegalAccessException e)
            {
                e.printStackTrace();
            }
    

    (2)调用类的方法
    1)通过Class对象的getMethods()方法或者getMethod()方法获得指定方法,返回Method数组或对象。
    2)调用Method对象中的Object invoke(Object obj, Object... args)方法。第一个参数对应调用该方法的实例对象,第二个参数对应该方法的参数。

     try{
                // 生成新的对象:用newInstance()方法
                Class<Student> classs=Student.class;
                Student obj = classs.newInstance();
                //首先需要获得与该方法对应的Method对象
                Method method = classs.getDeclaredMethod("tests", String.class);
                method.setAccessible(true); // 抑制Java的访问控制检查
                //调用指定的函数并传递参数
                method.invoke(obj,"YaYaYa");
            }catch (NoSuchMethodException e)
            {
                e.printStackTrace();
            }catch (InstantiationException e)
            {
                e.printStackTrace();
            }catch (InvocationTargetException e)
            {
                e.printStackTrace();
            }catch(IllegalAccessException e)
            {
                e.printStackTrace();
            }
    

    当通过Method的invoke()方法来调用对应的方法时,Java会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的private方法,则可以先调用Method对象的如下方法。
    setAccessible(boolean flag):将Method对象的acessible设置为指定的布尔值。值为true,指示该Method在使用时应该取消Java语言的访问权限检查;值为false,则知识该Method在使用时要实施Java语言的访问权限检查。

    (3)访问成员变量
    1)通过Class对象的getFields()方法或者getField()方法获得指定方法,返回Field数组或对象。
    2)Field提供了两组方法来读取或设置成员变量的值:
    getXXX(Object obj):获取obj对象的该成员变量的值。此处的XXX对应8种基本类型。如果该成员变量的类型是引用类型,则取消get后面的XXX。setXXX(Object obj,XXX val):将obj对象的该成员变量设置成val值。

     try{
                //通过反射获取属性
                Class<Student> c1=Student.class;
                //获取age成员变量
                Field field = c1.getDeclaredField("age");
                field.setAccessible(true);// 抑制Java的访问控制检查
                Student s=c1.newInstance();
                //将obj对象的age的值设置为10
                field.setInt(s, 10);
                //获取obj对象的age的值
                field.getInt(s);
            }catch (InstantiationException e)
            {
                e.printStackTrace();
            }catch(IllegalAccessException e)
            {
                e.printStackTrace();
            }catch(NoSuchFieldException e)
            {
                e.printStackTrace();
            }
    

    相关文章

      网友评论

          本文标题:Java反射(一)

          本文链接:https://www.haomeiwen.com/subject/qmsjaftx.html