前言
首先,了解下java类的初始化过程:
编程:将java文件编译为.class字节码文件
加载:类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例
连接:细分三步
验证:格式(class文件规范) 语义(final类是否有子类) 操作
准备:静态变量赋初值和内存空间,final修饰的内存空间直接赋原值,此处不是用户指定的初值。
解析:符号引用转化为直接引用,分配地址
初始化:有父类先初始化父类,然后初始化自己;将static修饰代码执行一遍,如果是静态变量,则用用户指定值覆盖原有初值;如果是代码块,则执行一遍操作。
原理
Java的反射就是利用上面第二步加载到jvm中的.class文件来进行操作的。.class文件中包含java类的所有信息,当你不知道某个类具体信息时,可以使用反射获取class,然后进行各种操作。
我们从源码开始分析:
首先定一个Student学生类,如下:
package com.hpsyche.wangyi;
/**
* @author Hpsyche
*/
public class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
再有一个反射类,如下:
package com.hpsyche.wangyi;
import java.lang.reflect.InvocationTargetException;
/**
* @author Hpsyche
*/
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class c= Class.forName("com.hpsyche.wangyi.Student");
Student s= (Student) c.newInstance();
System.out.println(s.getName());
}
}
编码成字节码文件后,javap反解析:
并未发现什么端倪,主要在#4 #5处,通过Class.newInstance()产生Object,并通过checkcast转换为Student类;
我们深入看下newInstance()方法的源码:
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
// NOTE: the following code may not be strictly correct under
// the current Java memory model.
// Constructor lookup
if (cachedConstructor ==