对象创建的一般流程
在讲反射之前,先描述一下在一般情况下,一个类的对象是如何创建的。如运行:
Mobile m = new Mobile();
根据类的加载流程我们知道:
- 当JVM之前未加载过Mobile类,则进入类加载过程
- 类加载器从磁盘上找到Mobile.class字节码文件
- 类加载器将字节码文件加载进虚拟机内存,同时在堆中产生一个Class类的对象
- 类加载完成后,该类的对象所需的内存空间已经完全确定了(编译完成后则确定了),因此此时为新生对象在堆中开辟内存空间。
- 对分配到内存空间的数据类型初始化为零值。
- 对对象头进行设置,如所属类(这个对象是哪个类的实例)、对象哈希码等
- 调用对象的init()方法,根据传入值为对象的属性赋值
- 在线程的虚拟机栈中新建对象引用,指向刚刚在堆中建立的对象。
下图展示从类加载到为新生对象分配内存的流程:反射原理
在new对象的一般流程的基础上,反射就是:通过某个类的全类名(字符串),加载该类的字节码到虚拟机内存中,获得该类的Class对象,在获得了运行时类对象后,可以根据这个运行时对象直接取得类的所有属性和方法。流程如下所示:使用反射
获取Class对象
要调用一个类的方法或属性,以及关于该类其他的信息,就是通过反射产生的Class对象提供的接口实现的。要通过反射获得某类的Class对象,可以:使用全类名和Class.forName方法,反射大多使用这种方式
Class clz = Class.forName("java.lang.String");
使用.class方法,当编译前就知道要操作的类的时候才可以使用
Class clz = String.class;
使用一个类的对象的getClass()方法
String str = new String("Hello"); Class clz = str.getClass();
通过反射创建类对象
通过Class对象的newInstance()方法
Class clz = Mobile.class; Mobile m = (Mobile)clz.newInstance();
这种方式只能使用Mobile类的默认的无参构造方法。获得了类的构造器对象后,用构造器对象获得类的对象
Class clz = Mobile.class; Constructor constructor = clz.getConstructor(); Mobile m = (Mobile)constructor.newInstance();
这种方式就可以根据需要选择不同的构造器进行构造对象了,获得不同的构造器的方式是在getConstructor()方法中写入希望获得的构造器的传入参数类型,如:Constructor constructor = clz.getConstructor(String.class, int.class);
获得类的属性
getFields()方法
获得类的私有属性外的所有属性的数组:Class clz = Mobile.class; Field[] fields = clz.getFields(); for (Field field : fields) { System.out.println(“属性名是:”+field.getName()); }
getDeclaredFields()方法
和getFields()方法类似,而此方法可以获得类的所有属性getField(String fieldName)方法
根据变量名获得单个的公有的属性getDeclaredField(String fieldName)方法
根据变量名获得单个的公有或私有的属性获得类的方法
getMethods()方法
获得类所有的公有方法的对象数组getDeclaredMethods()方法:
获得类的所有方法的对象数组getMethod(String name, Class<?> ... parameterTypes)方法
根据方法名和方法形参的Class对象获得类的一个公有方法getDelcaredMethod(String name, Class<?> ... parameterTypes)方法
根据方法名和方法形参的Class对象获得类的一个公有或私有方法调用获得的方法对象:invoke(Object obj,Object... args)
obj:要调用本方法的对象
args:调用方法时传入的实参
例:Method method = clz.getMethod("show", String.class); //实例化一个Student对象 Object obj = clz.getConstructor().newInstance(); m.invoke(obj, "iPhone");