当前位置 博文首页 > 香菜:一文把Java反射说的明明白白,清清楚楚,记得点赞关注,距

    香菜:一文把Java反射说的明明白白,清清楚楚,记得点赞关注,距

    作者:[db:作者] 时间:2021-07-17 09:41

    目录

    1、反射的概念

    1、概念

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

    2.1 元数据的概念

    2.2 获取class对象的方式

    3、反射如何获取元数据并访问

    1、访问权限

    2、获取方法

    2.1 访问静态方法

    2.2 访问类方法

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

    4、获取实现的接口

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

    6、获取继承的父类

    7、获取注解

    4、反射实例

    5、总结


    图片

    今天有时间没加班回家来好好写一篇文章,反射是Java里比较高级的概念了,一般在书的后半部分。反射也是写框架的必备技能,反射很重要,现在仍然记得刚毕业的一两年一直没有搞懂反射是什么。今天就讲讲反射,希望这篇文章能帮有同样疑惑的你解开疑团,废话不多说,让我们开始吧。

    1、反射的概念

    1、概念

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

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

    缺点:执行效率低。

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

    2.1 元数据的概念

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

    • Class类:代表一个类。

    • Field类:代表类的成员变量(成员变量也称为类的属性)。

    • Method类:代表类的方法。

    • Constructor类:代表类的构造方法。

    ?

    图片

    ?

    2.2 获取class对象的方式

    • 2.2.1 通过对象获得,因为任何对象都必须和class对象关联

    • 2.2.2 通过类对象直接获得

    • 2.2.3 通过类加载器获得,因为类加载器读取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();
      ? ? ? }
      
      ? }
      }

      ?

    3、反射如何获取元数据并访问

    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反射机制时,主要步骤包括:

    1. 获取 目标类型的Class对象

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

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

      有疑问的可以留言,我们一起讨论,没有问题的也可以留言,我们交个朋友

    字不容易,点赞,转发,关注三连,谢谢大家,对了,关注我公众号:【香菜聊游戏】有更多福利哦

    cs