了解反射我们要先了解class。class是java通过javac编译出来的二进制文件,他有严格的顺序规定。一个class的数据结构如下如下
---------------------------------------------------------------------------
| 头部信息,包含jdk版本,jdk向下兼容 |
---------------------------------------------------------------------------
.......
---------------------------------------------------------------------------
| 常量池计数器 |
---------------------------------------------------------------------------
| 方法名称的表 |
---------------------------------------------------------------------------
| 成员变量名称的表 |
---------------------------------------------------------------------------
当他需要调用时,首先会被装载器classload装载。这时他在内存中呈现的是一个对象,万物皆对象。这个对象里面有我们写的java类的方法名。属性等等数据
反射就是程序运行过程中获取到这个类的数据,通过改变这个类,或者使用这个类。达到运行中动态运行程序的结果。
我们主要了解几点就够了
一、获取Class对象
String pkgName = "com.example.function.study.A_了解JAVA.G_java之反射.ReflectActivity";
//1、通过类名,在编译期间直接获取Class对象
Class<?> clazz = ReflectActivity.this.getClass();
//2、通过包名,在运行期间获取装载过初始化过的Class对象
Class claxx = Class.forName(pkgName);
//3、通过包名,在运行期间装载class二进制文件,生成Class对象
Class clacc = getClassLoader().loadClass(pkgName);
2、获取Class对象的属性和值
Class的一些特定属性和值就是java文件中的成员变量和方法
//1.1、获取当前类和其父类的所有公共属性的成员变量
Field[] fields = clacc.getFields();
//1.2、获取当前类的所有成员变量
Field[] declaredFields = claxx.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Log.d(TAG, "onResume_fields: " + fields[i].getName());
}
for (int i = 0; i < declaredFields.length; i++) {
Log.d(TAG, "onResume_declaredFields: " + declaredFields[i].getName());
}
//2.1、获取当前类和其父类的所有公共方法
Method[] methods = clazz.getMethods();
//2.1、获取当前类的所有方法
Method[] declaredMethods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Log.d(TAG, "onResume_methods: " + methods[i].getName());
}
for (int i = 0; i < declaredMethods.length; i++) {
Log.d(TAG, "onResume_declaredMethods: " + declaredMethods[i].getName());
}
//3.1、获取当前类和其父类的所有注解
Annotation[] annotations= claxx.getAnnotations();
//3.1、获取当前类的所有注解
Annotation[] declaredAnnotations= claxx.getDeclaredAnnotations();
for (int i = 0; i < annotations.length; i++) {
Log.d(TAG, "onResume_annotations: " + annotations[i].toString());
}
for (int i = 0; i < declaredAnnotations.length; i++) {
Log.d(TAG, "onResume_declaredAnnotations: " + declaredAnnotations[i].toString());
}
三、实际类的使用
//实际使用类里面的方法
//获取到isTrue该方法
Method method= claxx.getDeclaredMethod("isTrue", String.class);
//使用isTrue该方法
Object obj=method.invoke(this,"返回值为真");
//获取到reflect成员变量
Field field=clacc.getDeclaredField("reflect");
//读写reflect成员变量
Log.e(TAG,"赋值前"+field.get(this));
field.set(this,obj);
Log.e(TAG,"赋值后"+field.get(this));
反射的优势主要在于两点:
-
在一些场景中,这种“未知类型”实际上大大增强了程序运行时的灵活性,但是其性能会有一些损耗;
-
对于对象的类型可以在运行时判断,这样的特性实质上是对多态极大地增强,进一步地将上层的抽象与下层的具体实现进行解耦。
这两点在JDBC Driver中体现的非常明显,例如上图中的实例中,JDBC的驱动加载方式是通过反射机制实现的,从而保证运行时可以动态选择要加载的驱动程序,程序灵活性大大增强。另外,JDBC只是设计了驱动需要实现的接口,并不关心驱动厂商的个数和实现方式,只要安装统一的规范即可,至于类型的判断和具体方法的触发,交给运行期动态判断即可,这种反射机制的使用淋漓尽致的体现了多态,并且降低了类与类之间的耦合度。