1、反射的概念
1、概念
反射,指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对任意一个对象,都能调用它的任意一个方法。这种动态获取信息,以及动态调用对象方法的功能,叫做java语言的反射机制。反射很强大,有优点也有缺点。
优点:灵活性高。因为反射属于动态编译,即只有到运行时才动态创建 &获取对象实例。
缺点:执行效率低。
2、获取字节码文件对象的方式
2.1 元数据的概念
元数据(metadata):元数据是指用来描述类的数据,就是class的代码数据。所有的class文件加载到虚拟机之后都会被构建成class对象,class对象描述了一个类都有哪些东西,大家都知道的实现的接口,继承的抽象类,成员变量,类变量,成员方法,类方法,静态方法等,这个class对象就是元数据。
Class类:代表一个类。 Field类:代表类的成员变量(成员变量也称为类的属性)。 Method类:代表类的方法。 Constructor类:代表类的构造方法。
2.2 获取class对象的方式
通过对象获得,因为任何对象都必须和class对象关联 通过类对象直接获得 通过类加载器获得,因为类加载器读取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 (); } } }
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反射机制时,主要步骤包括:
获取 目标类型的Class对象
通过 Class 对象分别获取Constructor类对象、Method类对象 或者 Field 类对象
通过 Constructor类对象、Method类对象 & Field类对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作。
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注我们的更多内容!
原文链接:https://gamwatcher.blog.csdn.net/article/details/116244568