当前位置 : 主页 > 编程语言 > java >

一篇文章带你搞定JAVA反射

来源:互联网 收集:自由互联 发布时间:2021-08-21
目录 1、反射的概念 1、概念 2、获取字节码文件对象的方式 2.1 元数据的概念 2.2 获取class对象的方式 1、访问权限 2、获取方法 2.1 访问静态方法 2.2 访问类方法 3、获取字段,读取字段的
目录
  • 1、反射的概念
    • 1、概念
  • 2、获取字节码文件对象的方式
    • 2.1 元数据的概念
    • 2.2 获取class对象的方式
    • 1、访问权限
    • 2、获取方法
      • 2.1 访问静态方法
      • 2.2 访问类方法
      •  3、获取字段,读取字段的值
    • 4、获取实现的接口
      • 5、获取构造函数,创建实例
        • 6、获取继承的父类
          • 7、获取注解
          • 4、反射实例
            • 5、总结

              1、反射的概念

              1、概念

              反射,指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对任意一个对象,都能调用它的任意一个方法。这种动态获取信息,以及动态调用对象方法的功能,叫做java语言的反射机制。反射很强大,有优点也有缺点。

              优点:灵活性高。因为反射属于动态编译,即只有到运行时才动态创建 &获取对象实例。

              缺点:执行效率低。

              2、获取字节码文件对象的方式

              2.1 元数据的概念

              元数据(metadata):元数据是指用来描述类的数据,就是class的代码数据。所有的class文件加载到虚拟机之后都会被构建成class对象,class对象描述了一个类都有哪些东西,大家都知道的实现的接口,继承的抽象类,成员变量,类变量,成员方法,类方法,静态方法等,这个class对象就是元数据。

              • Class类:代表一个类。
              • Field类:代表类的成员变量(成员变量也称为类的属性)。
              • Method类:代表类的方法。
              • Constructor类:代表类的构造方法。

              图片

              2.2 获取class对象的方式

              • 通过对象获得,因为任何对象都必须和class对象关联
              • 通过类对象直接获得
              • 通过类加载器获得,因为类加载器读取class文件会返回class对象

              即将用来反射的对象(随便定义的一个对象,只是为了演示)

              package org.pdool.reflect;
               
              /**
              * @author 香菜
              */
              public class Npc {
                // 静态field
                public static int NPC_TYPE_1 = 1;
                // 私有成员变量
                private int npcType;
                // 共有成员变量
                public String name;
                // 无参构造函数
                public Npc() {
                }
                // 有参构造函数
                public Npc(int npcType, String name) {
                    this.npcType = npcType;
                    this.name = name;
                }
               
                public int getNpcType() {
                    return npcType;
                }
               
                public void setNpcType(int npcType) {
                    this.npcType = npcType;
                }
               
                public String getName() {
                    return name;
                }
               
                public void setName(String name) {
                    this.name = name;
                }
                // 静态方法
                public static void sayHello(String word){
                    System.out.println("hello " + word);
                }
              }

              获取反射class的三种方式

              package org.pdool.reflect;
               
              /**
              * @author 香菜
              */
              public class ClazzTest {
                 public static void main(String[] args) {
                     //第一种方式获取Class对象
                     Npc npc1 = new Npc();//这一new 产生一个Npc对象,一个Class对象。
                     Class npcClazz1 = npc1.getClass();//获取Class对象
                     System.out.println(npcClazz1.getName());
               
                     //第二种方式获取Class对象
                     Class npcClazz2 = Npc.class;
                     System.out.println(npcClazz1 == npcClazz2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
               
                     //第三种方式获取Class对象
                     try {
                         Class npcClazz3 = Class.forName("org.pdool.reflect.Npc");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
                         System.out.println(npcClazz3 == npcClazz2);//判断三种方式是否获取的是同一个Class对象
                    } catch (ClassNotFoundException e) {
                         e.printStackTrace();
                    }
               
                }
              }

              1、访问权限

              反射机制的默认行为受限于Java的访问控制,可通过 setAccessible 绕过控制。

              // 设置对象数组可访问标志
              static void setAccessible(AccessibleObject[] array, boolean flag)  

              2、获取方法

              图片

              2.1 访问静态方法

              public static void main(String[] args) throws NoSuchMethodException,InvocationTargetException, IllegalAccessException {
                     Npc npc = new Npc(1, "妖神·凰女");
                     Class npcClazz = Npc.class;
                     // 第一个参数是方法名,第二个参数是函数的参数class对象,因为存在重载的可能性,用参数类型区分
                     Method sayHello = npcClazz.getMethod("sayHello", String.class);
                     sayHello.invoke(npc, "world");
                }

              2.2 访问类方法

                    Npc npc = new Npc(1, "妖神·凰女");
                    System.out.println(npc.getName());
                    Class npcClazz = Npc.class;
                    // 第一个参数是方法名,第二个参数是函数的参数class对象,因为存在重载的可能性,用参数类型区分
                    Method sayHello = npcClazz.getMethod("setName", String.class);
                    sayHello.invoke(npc, "world");
                    System.out.println(npc.getName());

               3、获取字段,读取字段的值

              图片

                     Npc npc = new Npc(1, "妖神·凰女");
                     Class npcClazz = Npc.class;
                     // 获取字段,并设置可访问
                     Field field = npcClazz.getField("name");
                     field.setAccessible(true);
                     System.out.println( field.get(npc));

              4、获取实现的接口

              图片

              5、获取构造函数,创建实例

              图片

                  Class npcClazz = Npc.class;
                             Constructor declaredConstructor = npcClazz.getDeclaredConstructor(int.class,String.class);
                     Npc npc = (Npc) declaredConstructor.newInstance(1, "妖神");
                     System.out.println(npc.getName());

              6、获取继承的父类

              Class npcClazz = Npc.class;
              Class superclass = npcClazz.getSuperclass();
              System.out.println(superclass.getName());

              7、获取注解

              Class npcClazz = Npc.class;
                     Annotation[] annotations = npcClazz.getAnnotations();
              // 运行时注解
                     for (Annotation annotation : annotations) {
                         System.out.println(annotation.getClass().getName());
                    }

              4、反射实例

              获取到元数据不是最终的目的,我们最终的目的是想在运行时去调用,访问类。说了太多,还是举个例子,大家都知道Spring的IOC,怎么实现的呐?

              过程:

              1、Spring 在项目启动的时间通过读取xml中配置的bean的路径,

              2、然后通过Class.forName 读取class 到类加载器,

              3、然后通过反射创建所有的bean实例并保存到容器中,启动容器之后,

              4、在项目中可以直接获取bean对象。

              我们来大概实现这一过程,因为xml的读取比较麻烦,直接用property来代替了。大家体会一下思想就可以了。

              package org.pdool.reflect;
               
              import java.io.IOException;
              import java.io.InputStream;
              import java.lang.annotation.Annotation;
              import java.lang.reflect.Constructor;
              import java.lang.reflect.Field;
              import java.lang.reflect.InvocationTargetException;
              import java.lang.reflect.Method;
              import java.util.HashMap;
              import java.util.Map;
              import java.util.Properties;
              import java.util.Set;
               
              /**
              * @author 香菜
              */
              public class ClazzTest {
                public static void main(String[] args){
                    try {
                        Map<String,Object> container = new HashMap<>();
                        //1.读取配置
                        InputStream in = ClazzTest.class.getResourceAsStream("/beans.properties");
                        Properties property = new Properties();
                        property.load(in);
                        //2.反射创建对象
                        Set<Object> keySet = property.keySet();
                        for (Object key : keySet) {
                            // 2.1 获取类的全路径
                            String classStr = (String) property.get(key);
                            // 2.2 加载class 到虚拟机
                            Class<?> beanClazz = Class.forName(classStr);
                            // 2.3 获取缺省的构造函数
                            Constructor<?> declaredConstructor = beanClazz.getDeclaredConstructor();
                            // 2.4 创建实例
                            Object o = declaredConstructor.newInstance();
                            container.put((String) key,o);
                        }
                        // 3.获取实例
                        Npc npc = (Npc) container.get("npc");
                        System.out.println(npc == null);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
              }

              5、总结

              在使用Java反射机制时,主要步骤包括:

              获取 目标类型的Class对象

              通过 Class 对象分别获取Constructor类对象、Method类对象 或者 Field 类对象

              通过 Constructor类对象、Method类对象 & Field类对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作。

              本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注自由互联的更多内容!

              网友评论