小言_互联网的博客

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

385人阅读  评论(0)

目录

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类对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作。

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

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


转载:https://blog.csdn.net/perfect2011/article/details/116244568
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场