概述
Java 反射浅析
目录
Java 反射浅析
- 反射的意义
- 反射的基本运用
- 获取class对象
- 创建获取到的Class类对象的实例
- 获取类的方法
- 获取类的成员变量
- 调用类的方法
- Simple Demo
- 反射原理浅析
1. 反射的意义
Java反射机制是指在运行状态,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意方法和属性,这种动态获取信息以及动态调用对象方法的功能。
Java提供两种形式在运行时间查找与对象和类有关的信息。第一种是“传统”RTTI(运行时间类型鉴定),它假定我们已经在编译和运行时间拥有了所有类型;另一种是“反射”机制,利用它可在运行时间独立查找类信息。所以,“传统”RTTI有一个缺陷:类型必须是在编译时间已知的。而反射克服了这一缺陷。
2. 反射的基本运用
Java中有一个专门的库来提供反射相关的类,名为java.lang.reflect;
2.1 获取class对象
2.1.1 使用Class类的forName()
静态方法
//forName()的参数是全限定类名;
Class<?> clazz1 = Class.forName("className");
2.1.2 直接获取一个对象的class
Class<?> clazz2 = int.class;
Class<?> clazz3 = Integer.TYPE;
2.1.3 调用某个对象的getClass()
方法
StringBuilder str = new StringBuilder("hello");
Class<?> clazz4 = str.getClass();
2.2 创建获取到的Class类对象的实例
通过反射生成对象有两种方式:
/* 第一种:使用Class对象的newInstance()方法来创建Class对象对应类的实例
* 通过Class对象的newInstance()方法来创建Class对象对应类的实例 */
Class<?> c = String.class;
Object str = c.newInstance();
/*第二种:先通过Class对象获取指定的Constructor构造器实例,在调用
* Constructor实例的newInstance()方法来创建实例,这种方法可以用
* 指定的构造器构造类的实例 */
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);
2.3 获取类的方法
主要有以下几个方法:
//getDeclaredMethods()方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问
//和私有方法,但不包括继承的方法;
public Method[] getDeclaredMethods() throws SecurityException
//getMethods()方法返回某个类的所有公用(public)方法,包括其继承的公用方法;
public Method[] getMethods() throws SecurityException
//getMethod()方法返回一个特定的方法(包括其继承的公用方法,但不包括私有方法),其中第一个参数为方法名称,
//后面的参数为方法的参数对应Class的对象
public Method[] getMethod(String name, Class<?>... parameterTypes) throws SecurityException
2.4 获取类的成员变量
主要有以下几个方法:
getFiled("变量名")
:访问特定的公有的成员变量;
getDeclaredField()
所有已声明的成员变量,但不能得到其父类的成员变量;
getFields()
访问所有公有的成员变量(包括其继承的公有变量,但不包括私有变量);
这三个方法同获取Method的方法相似;
2.5 调用类的方法
当我们从类中获取一个方法后,我们就可以用invoke()
方法来调用这个方法。
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
2.6 Simple Demo
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/* *******************************************
* 创建一个父类easy_algorithm
* 类包含私有变量、公有变量、私有方法、公有方法
*/
class easy_algorithm {
private int a =1;
public int b =0;
private int add( int x, int y) {
return x + y ;
}
public int minus(int x, int y) {
return x -y ;
}
}
/*创建一个子类diff_algorithm继承easy_algorithm
*类包含私有变量、公有变量、私有方法、公有方法
* */
class diff_algorithm extends easy_algorithm {
private int clazz_a = 1;
public int clazz_b = 0;
private int plus(int x, int y) {
return x * y ;
}
public int div(int x, int y) {
if(y == 0){
System.out.println("输入参数有误");
return x;
}else {
return x/y;
}
}
}
public class demo {
public static void main(String[] args) throws IllegalAccessException, InstantiationException
, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获取diff_algorithm类对象,并实例化;
Class<?> c = diff_algorithm.class;
Object obj = c.newInstance();
//通过三种方式获取类的成员函数,并打印
Method[] methods = c.getMethods();
Method[] declaredMethods = c.getDeclaredMethods();
System.out.println("getMethods获取的方法");
for(Method m:methods) {
System.out.println(m);
}
System.out.println("declaredMethods获取的方法");
for(Method m:declaredMethods) {
System.out.println(m);
}
Method method = c.getMethod("minus", int.class, int.class);
//调用类的方法并输出结果
Object result = method.invoke(obj,2, 5);
System.out.println("add方法的输出结果为:"+result);
//通过三种方式获取类的成员变量,并打印
Field[] fields = c.getFields();
Field[] declaredFields = c.getDeclaredFields();
System.out.println("getFields获取的方法");
for(Field f:fields) {
System.out.println(f);
}
System.out.println("getDeclaredFields获取的方法");
for(Field f:declaredFields) {
System.out.println(f);
}
Field field = c.getField("clazz_b");
System.out.println("getField获取的方法"+field);
}
}
程序的输出结果:
getMethods获取的方法
public int diff_algorithm.div(int,int)
public int easy_algorithm.minus(int,int)
//以下这部分属于Object基类的方法
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
declaredMethods获取的方法
private int diff_algorithm.plus(int,int)
public int diff_algorithm.div(int,int)
add方法的输出结果为:-3
getFields获取的方法
public int diff_algorithm.clazz_b
public int easy_algorithm.b
getDeclaredFields获取的方法
private int diff_algorithm.clazz_a
public int diff_algorithm.clazz_b
getField获取的方法public int diff_algorithm.clazz_b
3. 反射原理浅析
反射最终调用的就是Method类的invoke()
方法;因此我们通过查看invoke()
源码来分析原理;
进入 Method 的 invoke 方法我们可以看到,一开始是进行了一些权限的检查,最后是调用了 MethodAccessor 类的 invoke 方法进行进一步处理
@CallerSensitive
@ForceInline
@HotSpotIntrinsicCandidate
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (!this.override) {
Class<?> caller = Reflection.getCallerClass();
this.checkAccess(caller, this.clazz, Modifier.isStatic(this.modifiers) ? null : obj.getClass(), this.modifiers);
}
MethodAccessor ma = this.methodAccessor;
if (ma == null) {
ma = this.acquireMethodAccessor();
}
//最终调用MethodAccessor的invoke()方法
return ma.invoke(obj, args);
}
那么 MethodAccessor 又是什么呢?
其实 MethodAccessor 是一个接口,定义了方法调用的具体操作,而它有三个具体的实现类:
- sun.reflect.DelegatingMethodAccessorImpl
- sun.reflect.MethodAccessorImpl
- sun.reflect.NativeMethodAccessorImpl
而要看 ma.invoke()
到底调用的是哪个类的 invoke
方法,则需要看看 MethodAccessor
对象返回的到底是哪个类对象,所以我们需要进入 acquireMethodAccessor()
方法中看看。
private MethodAccessor acquireMethodAccessor() {
MethodAccessor tmp = null;
//判断是否存在对应的MethodAccessor对象,如果存在那么就复用之前的MethodAccessor对象
if (this.root != null) {
tmp = this.root.getMethodAccessor();
}
if (tmp != null) {
this.methodAccessor = tmp;
} else {
//若不存在,则调用newMethodAccessor()方法生成一个MethodAccessor对象
//接下来看一下newMethodAccessor方法的源代码
tmp = reflectionFactory.newMethodAccessor(this);
this.setMethodAccessor(tmp);
}
return tmp;
}
public MethodAccessor newMethodAccessor(Method method) {
//省略部分源代码
......
if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
return (new MethodAccessorGenerator()).generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTypes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers());
} else {
//首先生成一个NativeMethodAccessorImpl对象,再以它为参数调用 DelegatingMethodAccessorImpl 类的构造方法。
//接下来看一下DelegatingMethodAccessorImpl 类的构造方法的源代码
NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
acc.setParent(res);
return res;
}
}
//将 NativeMethodAccessorImpl 对象赋值给 DelegatingMethodAccessorImpl 类的 delegate 属性。
DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
this.setDelegate(delegate);
}
所以说ReflectionFactory
类的 newMethodAccessor
方法最终返回 DelegatingMethodAccessorImpl
类对象。所以我们在前面的 ma.invoke() 里,其将会进入DelegatingMethodAccessorImpl
类的 invoke
方法中。
//发现DelegatingMethodAccessorImpl类的invoke方法最终调用NativeMethodAccessorImpl 对象的invoke方法
//(代码中的delegate就是NativeMethodAccessorImpl 对象)所以进入NativeMethodAccessorImpl 的 invoke 方法。
public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException {
return this.delegate.invoke(obj, args);
}
public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException {
//判断调用次数是否超过阈值(numInvocations)、类名称是否还有‘/’符号
if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
//满足条件调用
MethodAccessorImpl acc = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
this.parent.setDelegate(acc);
}
//否则调用invoke0方法,它在Native层,到达这里便不再深入了;
return invoke0(this.method, obj, args);
}
通过查看源码上的注释,可以了解到MethodAccessor 对象的一些设计信息。
"Inflation" mechanism. Loading bytecodes to implement Method.invoke() and Constructor.newInstance() currently costs 3-4x more than an invocation via native code for the first invocation (though subsequent invocations have been benchmarked to be over 20x faster).Unfortunately this cost increases startup time for certain applications that use reflection intensively (but only once per class) to bootstrap themselves.
Inflation 机制。初次加载字节码实现反射,使用 Method.invoke() 和 Constructor.newInstance() 加载花费的时间是使用原生代码加载花费时间的 3 - 4 倍。这使得那些频繁使用反射的应用需要花费更长的启动时间。
To avoid this penalty we reuse the existing JVM entry points for the first few invocations of Methods and Constructors and then switch to the bytecode-based implementations. Package-private to be accessible to NativeMethodAccessorImpl and NativeConstructorAccessorImpl.
为了避免这种痛苦的加载时间,我们在第一次加载的时候重用了 JVM 的入口,之后切换到字节码实现的实现。
就像注释里说的,实际的 MethodAccessor 实现有两个版本,一个是 Native 版本,一个是 Java 版本。
Native 版本一开始启动快,但是随着运行时间边长,速度变慢。Java 版本一开始加载慢,但是随着运行时间边长,速度变快。正是因为两种存在这些问题,所以第一次加载的时候我们会发现使用的是 NativeMethodAccessorImpl 的实现,而当反射调用次数超过 15 次之后,则使用 MethodAccessorGenerator 生成的 MethodAccessorImpl 对象去实现反射。
Method类的invoke方法整个流程表示为如下时序图:
(时序图中的DelegatingMethod
代表DelegatingMethodAccessorImpl
NativeMethod
代表NativeMethodAccessorImpl
)
参考以下文档:
深入解析Java反射(1)
大白话说Java反射:入门、使用、原理
最后
以上就是激情小天鹅为你收集整理的Java 反射浅析Java 反射浅析的全部内容,希望文章能够帮你解决Java 反射浅析Java 反射浅析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复