一文把Java反射说的明明白白,清清楚楚,记得点赞关注,距离架构师的小目标又进一步

举报
香菜聊游戏 发表于 2021/07/15 00:48:28 2021/07/15
【摘要】 目录 1、反射的概念 1、概念 2、获取字节码文件对象的方式 2.1 元数据的概念 2.2 获取class对象的方式 3、反射如何获取元数据并访问 1、访问权限 2、获取方法 2.1 访问静态方法 2.2 访问类方法 3、获取字段,读取字段的值 4、获取实现的接口 5、获取构造函数,创建实例 6、获取继承的父类 7、获取注解 4、反射实例 5...

目录

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对象

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

    
        
    1. package org.pdool.reflect;
    2. /**
    3. * @author 香菜
    4. */
    5. public class Npc {
    6.   // 静态field
    7.   public static int NPC_TYPE_1 = 1;
    8.   // 私有成员变量
    9.   private int npcType;
    10.   // 共有成员变量
    11.   public String name;
    12.   // 无参构造函数
    13.   public Npc() {
    14.   }
    15.   // 有参构造函数
    16.   public Npc(int npcType, String name) {
    17.       this.npcType = npcType;
    18.       this.name = name;
    19.   }
    20.   public int getNpcType() {
    21.       return npcType;
    22.   }
    23.   public void setNpcType(int npcType) {
    24.       this.npcType = npcType;
    25.   }
    26.   public String getName() {
    27.       return name;
    28.   }
    29.   public void setName(String name) {
    30.       this.name = name;
    31.   }
    32.   // 静态方法
    33.   public static void sayHello(String word){
    34.       System.out.println("hello " + word);
    35.   }
    36. }

    获取反射class的三种方式

    
        
    1. package org.pdool.reflect;
    2. /**
    3. * @author 香菜
    4. */
    5. public class ClazzTest {
    6.    public static void main(String[] args) {
    7.        //第一种方式获取Class对象
    8.        Npc npc1 = new Npc();//这一new 产生一个Npc对象,一个Class对象。
    9.        Class npcClazz1 = npc1.getClass();//获取Class对象
    10.        System.out.println(npcClazz1.getName());
    11.        //第二种方式获取Class对象
    12.        Class npcClazz2 = Npc.class;
    13.        System.out.println(npcClazz1 == npcClazz2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
    14.        //第三种方式获取Class对象
    15.        try {
    16.            Class npcClazz3 = Class.forName("org.pdool.reflect.Npc");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
    17.            System.out.println(npcClazz3 == npcClazz2);//判断三种方式是否获取的是同一个Class对象
    18.       } catch (ClassNotFoundException e) {
    19.            e.printStackTrace();
    20.       }
    21.   }
    22. }

     

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

1、访问权限

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


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

2、获取方法

图片

2.1 访问静态方法


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

2.2 访问类方法


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

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

图片


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

4、获取实现的接口

图片

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

 

图片


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

6、获取继承的父类


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

 

7、获取注解


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

4、反射实例

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

过程:

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

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

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

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

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


  
  1. package org.pdool.reflect;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.lang.annotation.Annotation;
  5. import java.lang.reflect.Constructor;
  6. import java.lang.reflect.Field;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.lang.reflect.Method;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. import java.util.Properties;
  12. import java.util.Set;
  13. /**
  14. * @author 香菜
  15. */
  16. public class ClazzTest {
  17.   public static void main(String[] args){
  18.       try {
  19.           Map<String,Object> container = new HashMap<>();
  20.           //1.读取配置
  21.           InputStream in = ClazzTest.class.getResourceAsStream("/beans.properties");
  22.           Properties property = new Properties();
  23.           property.load(in);
  24.           //2.反射创建对象
  25.           Set<Object> keySet = property.keySet();
  26.           for (Object key : keySet) {
  27.               // 2.1 获取类的全路径
  28.               String classStr = (String) property.get(key);
  29.               // 2.2 加载class 到虚拟机
  30.               Class<?> beanClazz = Class.forName(classStr);
  31.               // 2.3 获取缺省的构造函数
  32.               Constructor<?> declaredConstructor = beanClazz.getDeclaredConstructor();
  33.               // 2.4 创建实例
  34.               Object o = declaredConstructor.newInstance();
  35.               container.put((String) key,o);
  36.           }
  37.           // 3.获取实例
  38.           Npc npc = (Npc) container.get("npc");
  39.           System.out.println(npc == null);
  40.       } catch (Exception e) {
  41.           e.printStackTrace();
  42.       }
  43.   }
  44. }

5、总结

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

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

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

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

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

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

文章来源: gamwatcher.blog.csdn.net,作者:香菜聊游戏,版权归原作者所有,如需转载,请联系作者。

原文链接:gamwatcher.blog.csdn.net/article/details/116244568

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。