一. 反射
1.反射的简介
JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性并且能进行修改;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
2.反射的作用
反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。
(1)进行灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;
(2)还有动态代理、工厂模式的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
(3)获取系统隐藏类的信息,并进行修改;
3.获取Class 的三种方式
(1)通过对象调用 getClass() 方法来获取,通常应用在:传入一个类型的对象,但又不知道传入的具体是什么类,用这种方法,比如传过来一个 Object 类型的对象;
(2)直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高 ;
(3)通过 Class 对象的 forName() 静态方法来获取,是最常用的方式, 但是有时候会抛出 ClassNotFoundException的 异常;
Class c3 = Class.forName(“com.aj.Person”);
(4)通过ClassLoader获取:
ClassLoader cl = context.getClassLoader();
@SuppressWarnings(“rawtypes”)
Class SystemProperties = cl.loadClass(“android.os.SystemProperties”);
通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等查阅 API 可以看到 Class 有很多方法:
getName():获得类的完整名字。
getFields():获得类的public类型的属性。
getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
getMethods():获得类的public类型的方法。
getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
getConstructors():获得类的public类型的构造方法。
getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
二. 注解
- @Retention代表注解的保留策略:
(1)RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
(2)RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,无法通过反射读取,这是默认的生命周期;
(3)RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,可以通过反射读取;
这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码,生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和@SuppressWarnings,则可选用 SOURCE 注解。
- @Target代表注解可能出现的语法位置,即可以在哪里使用定义的注解,可选的位置如下:
ElementType.TYPE类、接口(包括注解类型)或枚举声明
ElementType.FIELD字段声明
ElementType.METHOD方法声明
ElementType.PARAMETER方法的参数声明
ElementType.CONSTRUCTOR类的构造法声明
ElementType.LOCAL_VARIABLE局部变量声明
ElementType.ANNOTATION_TYPE注解声明
ElementType.PACKAGE包声明
ElementType.TYPE_PARAMETERJDK1.8新加的,类型参数声明
ElementType.TYPE_USEJDK1.8新加的,类型使用声明
三. IOC注解框架
本次学习和笔记的内容就是通过使用上述的反射和注解,写一套FindViewById的IOC框架,以减少在项目中重复写相同的代码,并加深对反射和注解的理解;
- 注解:
FindViewById的注解
package com.bombking.bkchat.baselib.ioc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FindViewById {
//表示可以传值
int value();
}
点击事件的注解
package com.bombking.bkchat.baselib.ioc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) //运行时
public @interface OnClick {
int[] value();
}
- IOC工具类
package com.bombking.bkchat.baselib.ioc;
import android.app.Activity;
import android.view.View;
import com.bombking.bkchat.baselib.net.CheckNet;
import com.bombking.bkchat.baselib.net.NetManagerUtil;
import com.bombking.bkchat.utils.ToastUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ViewIoc {
// 绑定注解
public static void bindInit(Activity activity){
findViewById(activity);
onClickListener(activity);
}
/**
* find view by id
* @param activity
*/
private static void findViewById(Activity activity){
Class<?> clazz = activity.getClass();
//1.获取activity中的所有属性; clazz.getFields() -> 获取public的属性
Field[] fields = clazz.getDeclaredFields();
//2.遍历获取注解的属性;
for (Field field : fields){
FindViewById findViewById = field.getAnnotation(FindViewById.class);
//3.遍历所有的属性,如果注解不是空,获取注解的viewId
if (findViewById != null){
int viewId = findViewById.value();
//4.findViewById,获取view;
View view = activity.findViewById(viewId);
//5.把view动态的注入activity属性;相当于把注解获取到的属性赋值给activity中的成员变量;
try {
field.setAccessible(true);//确定可以操作私有的属性
field.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/**
* 点击事件
* @param activity
*/
private static void onClickListener(final Activity activity) {
Class<?> clazz = activity.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (final Method method : methods){
OnClick onClick = method.getAnnotation(OnClick.class);
final boolean isNet = method.getAnnotation(CheckNet.class) != null;//加入一个网络检查注解
if (onClick != null){
//获取所有方法上面注解的View的id值;
int[] values = onClick.value();
//遍历找出所有的view;
for (int viewId : values){
View view = activity.findViewById(viewId);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//检查网络是否可用
if (isNet){
if (!NetManagerUtil.cureentNetIsAvailable(activity)){
ToastUtils.getInstance().toastShort("当前无网络");//无网络时的操作
return;
}
}
//使用反射
method.setAccessible(true);//私有方法权限
try {
method.invoke(activity);
} catch (Exception e) {
try {
method.invoke(activity, view);//兼容有view的情况
} catch (Exception e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
});
}
}
}
}
}
- 使用
@FindViewById(R.id.btn_register)
Button button;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewIoc.bindInit(this);
}
@OnClick({R.id.bt1,R.id.bt2})
@CheckNet
private void getData(){
Toast.makeText(this,"请求后台数据",Toast.LENGTH_LONG).show();
}
结束语:我是一个入行不久的小菜鸟,如有不正确的地方,欢迎指正,感谢你的阅读;