写点什么

这份清华学霸的 Java 反射完整版学习笔记,2 小时带你从入门到入土

用户头像
飞飞JAva
关注
发布于: 2021 年 04 月 29 日

Hello,今天给各位童鞋们分享一篇文章,反射是 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 类对象分别获取类的构造函数、方法 &属性的具体信息,并进行后续操作。

今天分享的内容到这里就结束了,希望能给屏幕前的你们带来小小的帮助。

用户头像

飞飞JAva

关注

还未添加个人签名 2021.04.28 加入

分享、普及java相关知识

评论

发布
暂无评论
这份清华学霸的Java反射完整版学习笔记,2小时带你从入门到入土