一篇文章带你搞定JAVA反射

时间:2022-09-05 07:26:59

1、反射的概念

 

1、概念

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

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

缺点:执行效率低。

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

 

2.1 元数据的概念

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

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

一篇文章带你搞定JAVA反射

2.2 获取class对象的方式

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

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

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

获取反射class的三种方式

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

 

1、访问权限

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

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

2、获取方法

一篇文章带你搞定JAVA反射

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、获取字段,读取字段的值

一篇文章带你搞定JAVA反射

  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、获取实现的接口

一篇文章带你搞定JAVA反射

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

一篇文章带你搞定JAVA反射

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

5、总结

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

获取 目标类型的Class对象

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

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

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

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