一、Java反射机制
1、定义
JAVA反射机制是在“运行状态”中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。Java反射机制主要提供了几个功能:在运行时判断任意一个对象所属的类、在运行时构造任意一个类的对象、在运行时判断任意一个类所具有的成员变量和方法、在运行时调用任意一个对象的方法。
2、获取Class对象
当我们编译一个 Java 项目,所有的 Java 文件都会被编译成一个.class 文件,这些 class 文件在程序运行时会被 ClassLoader 加载到虚拟机中。当一个类被加载以后,Java 虚拟机就会在内存中自动产生一个 Class 对象。Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象,这个Class对象是由JVM生成的,通过它能够获悉整个类的结构。我们通过 new 构造函数的形式创建对象实际上也是通过这些 Class 来创建。那么,我们在代码中如何获得Class对象呢?通常有三个方法,如下所示:
/**
* 获取Class对象的三种方式
*/
public static Class<?> getClassObj() {
// 根据类名获取Class对象
Class<?> clazz1 = People.class;
// 根据对象获取Class对象
People people = new People();
Class<?> clazz2 = people.getClass();
// 根据完整类名获取Class对象
try {
Class<?> clazz3 = Class.forName("com.yuyh.reflection.java.People");
} catch (ClassNotFoundException e) {
Log.e(TAG, e.toString());
}
Log.i(TAG, "clazz1 = " + clazz1);
return clazz1; // clazz2 clazz3
}
3、通过Class对象获取目标类的对象
平时所熟悉的创建对象的方式就是去new一个类,执行他们的构造函数,那么当我们拿到Class对象想去创建目标类对象,说是通过反射,实际上还是去执行类的构造函数。如下所示:
/**
* 反射获取类的对象
*
* @return
*/
public static Object getObject() {
try {
// 获取类的Class对象
Class<?> clz = getClassObj();
// 获取类对象的Constructor
Constructor<?> constructor = clz.getConstructor(String.class, int.class, String.class);
// 在使用时取消 Java语言访问检查,提升反射性能
constructor.setAccessible(true);
// 通过 Constructor 来创建对象
Object obj = constructor.newInstance("yuyh", 25, "xxx@gmail.com");
Log.i(TAG, obj.toString());
return obj;
} catch (Exception e) {
Log.e(TAG, e.toString());
}
return null;
}
4、通过Class对象获取类的所有方法
同样,拿到Class对象之后我们可以通过调用getDeclaredMethods或getMethods(包括从父类继承下来的方法) 去获取类的所有方法,也可调用getDeclaredMethod (String name, Class...<?> parameterTypes) 来根据方法名获取某个方法。如下所示:
/**
* 反射获取类的方法
*/
public static void getDeclaredMethods() {
People people = (People) getObject();
// 获取到类中的所有方法(不包含从父类继承的方法)
Method[] methods = people.getClass().getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Log.i(TAG, "method[" + i + "] = " + methods[i].getName());
}
try {
// 获取类中的某个方法
Method method = people.getClass().getDeclaredMethod("setEMail", String.class);
// 判断是否是public方法
Log.i(TAG, "method is public = " + Modifier.isProtected(method.getModifiers()));
// 获取该方法的参数类型列表
Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < paramTypes.length; i++) {
Log.i(TAG, "paramTypes[" + i + "] = " + paramTypes[i].getName());
}
Log.i(TAG, "people.email befor= " + people.getEMail());
// 执行该方法
method.invoke(people, "xxx@163.com");
Log.i(TAG, "people.email after= " + people.getEMail());
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
5、通过Class对象获取类的所有属性
同理,可通过getDeclaredFields、 getFields、 getDeclaredField (String name)、 getField (String name) 来获取类的所有属性或单个属性。如下所示:
/**
* 反射获取类的属性
*/
public static void getDeclaredFields() {
People people = (People) getObject();
// 获取当前类所有属性
Field[] fields = people.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Log.i(TAG, "fields[" + i + "] = " + fields[i].getName());
}
try {
// 获取当前类的某个属性
Field field = people.getClass().getDeclaredField("name");
// 获取属性值
Log.i(TAG, "people.name before = " + field.get(people));
// 设置属性值
field.set(people, "yuyh1");
Log.i(TAG, "people.name after = " + field.get(people));
} catch (Exception e) {
e.printStackTrace();
}
}
6、根据Class对象获取父类或实现的接口
如下所示:
/**
* 获取对象的父类
*/
public static void getSuperClass() {
Student student = new Student("142315079");
Class<?> superClass = student.getClass().getSuperclass();
while (superClass != null) {
Log.i(TAG, "superClass = " + superClass.getName());
superClass = superClass.getSuperclass(); // 循环获取上一层父类(如果存在),至少存在一层java.lang.Object
}
}
/**
* 获取对象实现的接口
*/
public static void getInterface() {
Student student = new Student("142315079");
// 获取该类实现的所有接口
Class<?>[] interfaces = student.getClass().getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
Log.i(TAG, "interfaces[" + i + "] = " + interfaces[i].getName());
}
}
二、从Java反射到Android注解
俗话说,不会偷懒的程序员不是好程序员。相信初学Android的时候,大家都会被一堆findViewById()并且还要进行强转这种简单没营养,又不得不写的代码气死,显然,Android注解也在一定程度上帮助了你成为一名偷懒的程序猿。
什么是注解?通常我们可以把它理解为一个标记,最常见的注解有:@Override,@Deprecated,@SuppressWarnings等。注解本质上也是一个类,他不是通过class和interface来定义,而是通过@interface来定义一个注解。如下所示:
@Documented // 是否保存到JavaDoc文档
@Retention(RetentionPolicy.RUNTIME) // SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS
@Target(ElementType.METHOD) // 用于修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等,未标注则表示可修饰所有
@Inherited // 是否可以被继承,默认为 false
public @interface Test {
int value() default 0;
}
那么,我们如何通过注解来免去写findViewById(), setOnClickListener() ... ... 的麻烦呢?接下来我们就自定义一个简单的Android注解框架。
首先,定义一个注解InjectView,如下所示:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectView {
int value() default 0;
}
那么,我们要如何来解析这个注解呢?如下所示:
package com.yuyh.reflection.annotation;
import android.app.Activity;
import android.util.Log;
import java.lang.reflect.Field;
/**
* @author yuyh.
* @date 2016/6/13.
*/
public class Inject {
public static final String TAG = "Reflection";
public static void inject(Activity activity) {
getAnnotationInfos(activity);
}
private static void getAnnotationInfos(Activity activity) {
Class clazz = activity.getClass();
Log.i(TAG, clazz.getName());
Field[] fields = clazz.getFields();
for (Field field : fields) {
InjectView injectView = field.getAnnotation(InjectView.class);
if (injectView != null) {
int id = injectView.value();
try {
field.setAccessible(true);
field.set(activity, activity.findViewById(id));
} catch (IllegalAccessException e) {
Log.e(TAG, "IllegalAccessException = " + e.toString());
}
}
}
}
}
那么,在Activity中就可以进行注解View了。如下所示:
public class MainActivity extends AppCompatActivity {
@InjectView(R.id.hello)
TextView tvHello;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Inject.inject(this); // 初始化注解
tvHello.setText("hahaha");
}
}
tvHello.setText()方法没有报空指针,并且成功设置值,说明我们注解View实现成功。
同理,我们还可对实现对setOnClickListener等等的注解。
/**
* 解析OnClick以及OnLongClick注解
*
* @param activity
*/
private static void injectClick(final Activity activity) {
Class clazz = activity.getClass();
Log.i(TAG, clazz.getName());
Method[] methods = clazz.getDeclaredMethods();
for (final Method method : methods) {
OnClick click = method.getAnnotation(OnClick.class);
OnLongClick longClick = method.getAnnotation(OnLongClick.class);
if (click != null && click.value() != 0) {
View view = activity.findViewById(click.value());//通过注解的值获取View控件
if (view == null)
return;
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
method.invoke(activity, v);//通过反射来调用被注解修饰的方法,把View传回去
} catch (InvocationTargetException e) {
Log.e(TAG, "InvocationTargetException = " + e.toString());
} catch (IllegalAccessException e) {
Log.e(TAG, "IllegalAccessException = " + e.toString());
}
}
});
}
if (longClick != null && longClick.value() != 0) {
View view = activity.findViewById(click.value());
if (view == null)
return;
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
try {
method.invoke(activity, v);
} catch (InvocationTargetException e) {
Log.e(TAG, "InvocationTargetException = " + e.toString());
} catch (IllegalAccessException e) {
Log.e(TAG, "IllegalAccessException = " + e.toString());
}
return true;
}
});
}
}
}
源码地址:https://github.com/smuyyh/ReflectionDemo
Thank you for reading~~