基本原理理解
我们编写完的class文件,会被编译为字节码,然后在程序运行时被类加载器加载到vm中,加载完成后,每个class都会有一个对应的Class对象,我们所有的这个类的对象都是由这个Class实例来创建的。 由于我们的Class实例对象是通过类加载class文件获得的,他能够获取到几乎有关于该class的所有信息,包括
- 版本号:主版本号和次版本号
- 常量池
- 访问标志:是类还是接口,public还是private
- 字段表集合
- 方法集合
等等;
这些信息都会保存在Class对象实例中,所以我们可以利用Class来获取到有关该类的所有基本信息;
类型信息的获取
通过上面的介绍,因为类的所有信息都已经被加载到Class实例上了,自然可以通过该Class实例来获取;
方法的调用
假设有如下一个类A,利用反射调用foo方法。
public class A {
public void foo(String name) {
System.out.println("Hello, " + name);
}
}
public class TestClassLoad {
public static void main(String[] args) throws Exception {
Class<?> clz = Class.forName("A");
Object o = clz.newInstance();
Method m = clz.getMethod("foo", String.class);
for (int i = 0; i < 16; i++) {
m.invoke(o, Integer.toString(i));
}
}
}
Method类中invoke的源码
public final class Method extends Executable {
....
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
....
}
可以看到是通过MothodAccessor来实现invoke的。 MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的,以下是MethodAccessorGenerator生成的java版MethodAccessor;
public class GeneratedMethodAccessor1 extends MethodAccessorImpl {
public GeneratedMethodAccessor1() {
super();
}
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException {
// prepare the target and parameters
if (obj == null) throw new NullPointerException();
try {
A target = (A) obj;
if (args.length != 1) throw new IllegalArgumentException();
String arg0 = (String) args[0];
} catch (ClassCastException e) {
throw new IllegalArgumentException(e.toString());
} catch (NullPointerException e) {
throw new IllegalArgumentException(e.toString());
}
// make the invocation
try {
target.foo(arg0);
} catch (Throwable t) {
throw new InvocationTargetException(t);
}
}
}
字段设置
File字段的代码
class Field extends AccessibleObject implements Member {
...
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
getFieldAccessor(obj).set(obj, value);
}
...
}
FieldAccessor是一个接口,提供set和get基本类型和object接口;有很多Unsafe**FieldAccessorImpl实现,如UnsafeObjectFieldAccessorImpl;
class UnsafeObjectFieldAccessorImpl extends UnsafeFieldAccessorImpl {
...
public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException {
this.ensureObj(var1);
if (this.isFinal) {
this.throwFinalFieldIllegalAccessException(var2);
}
if (var2 != null && !this.field.getType().isAssignableFrom(var2.getClass())) {
this.throwSetIllegalArgumentException(var2);
}
unsafe.putObject(var1, this.fieldOffset, var2);
}
...
}
他们最终都会调用到Unsafe.put**的接口;这些接口都是native的;通过调用putLong,putInt,putDouble,putChar,putObject等方法,可以直接修改内存数据,达到修改变量值的目的;
参考
Java反射原理简析
关于反射调用方法的一个log
java反射怎么实现的?
sun.misc.Unsafe的理解