前言
在我们日常的android开发中注解的使用可谓是不少,无论是Acitivyt中onCreate的@Override,还是出名的注解框架butterknife 我们都或多或少的见到过或者使用过。
为什么要使用注解,大家都知道语言区分为动态语言和静态语言,如perl,python,Ruby都是动态语言,而C++,Java,C#是静态语言。但是java却可以通过注解反射的方式,探知 使用,编译期间完全位置的Classes,这几篇也是写给自己做个总结和分享。
一、注解
除了系统自带@Override,@NonNull,@Nullable之外,若我们要自己的去写注解就要使用到 系统提供的元注解。
元注解:
1.@Retention
表示需要在上面级别保存该注解信息。如果注解类型声明中不存在Retention注解,则默认为RetentionPolicy.CLASS
使用场景
@Retention(RetentionPolicy.CLASS)编译时注解,注解被保留到class文件,但jvm加载class文件时被遗弃(这是默认的生命周期)也就是说在class文件有效可能会被虚拟机jvm忽略。
@Retention(RetentionPolicy.RUNTIME)运行时注解,注解不仅被保存到class文件中,虚拟机jvm加载class文件后,仍然存在。
@Retention(RetentionPolicy.SOURCE)源码注解,生命周期只存在java源文件这一阶段,是这三种中生命周期最短的注解,注解只保留在源文件,当java文件编辑成class文件的时候,注解被遗弃。
2.@Target
这个注解用来说明该注解所修饰的对象范围:Annotation可以被用于packages,types(类,接口,枚举,Annotaiton类型),类型成员(方法,构造方法,成员变量,枚举值),方法参数和本地变量(比如循环变量,catch参数)。在Annotation类型的声明中使用到了target可更加明析其修饰的目标。ELementType.ANNOTATION_TYPE 注释类型声明
ELementType.CONSTRUCTOR构造函数声明
ELementType.FIELD 这里的Field 翻译是字段,但实际是指的对象(包括枚举常量)
ELementType.LOCAL_VARIABLE 局部变量声明
ELementType.METHOD 方法声明
ELementType.PACKAGE 包声明
ELementType.PARAMETER 正式参数声明
ELementType.TYPE 类,接口(包括注释类型)或枚举声明
ELementType.TYPE_PARAMETER 输入参数声明
ELementType.TYPE_USE 使用一种类型
3.@Documented
表示默认情况下,javaDoc和类似的工具将记录这个类型的注解。此类型应用于注解类型的声明,其会影响其客户注释的元素的使用。如果使用Documented注释类型声明,则其注释将成为带注释元素的公共API的一部分。
4.@Inherited
表示自动继承注释类型。如果注释类型声明中有Inherited元注解存在,以及用户在类上查询注释类型,并且类没有此类型的注解,则将自动查询该类的父类以获取注释的类型。重复此过程直至找到此类型的注解,或者达到类层次结构的顶部。如果没有父类具有此类型的注解,则查询将指示相关类没有此类注释。
注意:如果使用带注释的类型来注释除类之外的任何内容,则此元注释类型不起作用。另请注意,此元注释仅导致注释从超类继承; 已实现接口上的注释无效
二、反射
反射和注解是分不开的,平时使用注解基本都要使用到反射。那么我可以使用反射做些什么?利用反射机制在java中,我们可以动态的去调用一些protected甚至是private方法或类,不过这样比起普通的方法反射还是有一些限制这里我们后面在说,但也可以很大程度上满足一些特殊的业务需求。Reflection (反射)它允许运行中的java程序对自己身进行筛查,并能直接操作内部信息,包括:package,type parameters,superclass,implemented interfaces,inner classes,outer classes,fields,constructors,methods,modifiers等,在执行过程中,动态生成instance,变更(填充)fields 内容唤起methods。
简单来说反射机制就是允许开发人员在程序运行时改变程序的结构或变量的类型。通过这个特性,我们可以在运行时得知某个类的所有成员,包括其属性和方法,同时也能调用这些方法。这里要着重说明的是 反射机制的特殊之处在于可以使用编译期间完全位置的类,也就是说通过反射机制可以加载一个运行时才得知名字的类,从而取得其内部的成员函数调用,这里我们举个例子。
在平时开中要使用页面切换一遍会用到 TabLayout 那么就一定会遇到修改底部下划线的问题,我们一般会怎么处理? 使用drawable?修改TabLayout做个自定义View?都不用,我们可以使用反射的机制去动态修改TabLayout内部下划线的长度,话不多说上代码
/**
* 根据反射调整tab
* @param tabLayout
*/
private void test(final TabLayout tabLayout){
tabLayout.post(new Runnable() {
@Override
public void run() {
try {
LinearLayout mTabStrip = (LinearLayout) tabLayout.getChildAt(0);
int dp10 = (int) UiUtils.dip2px(40f);
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
View tabView = mTabStrip.getChildAt(i);
//反射或textView的对象,设置View的间距达成控制下划线长度的需求
Field mTextViewField = tabView.getClass().getDeclaredField("mTextView");
mTextViewField.setAccessible(true);
TextView mTextView = (TextView) mTextViewField.get(tabView);
tabView.setPadding(0, 0, 0, 0);
int width = 0;
width = mTextView.getWidth();
if (width == 0) {
mTextView.measure(0, 0);
width = mTextView.getMeasuredWidth();
}
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tabView.getLayoutParams();
params.width = width ;
params.leftMargin = dp10;
params.rightMargin = dp10;
tabView.setLayoutParams(params);
tabView.invalidate();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
});
}
是不是很新颖,其实我们平时开发碰到的许多问题都可以使用反射的方法去解决,起初我再做块时也再想是不是要去自定义扩展,网上也百度了很多方法,但是转念一想既然只是调整下划线间距为什么不能使用反射区获取对象进行动态修改呢。至少我个人觉得这是个很新颖的解决问题的方式,省去了很多不必要的时间和步骤。
有点看不懂?没关系等看完接下来的东西你会觉得这个原来无比简单。
反射的几个大板块
- Class
Class表示某个具体的类或接口,Class类提供了四个public方法,用于获取某个类的构造方法。
//这里用MainActivity作为例子,获取 Class对象
Class<? extends T> clazz = MainActivity.class;
Class<? extends T> clazz2 = mainActivity.getClass();
//讲解下Constructor 构造函数
//获取构造函数,以上面获取到的 clazz为例
//这是获取Class对象的类不存在不存在构造函数或者构造函数没有参数的情况下
Constructor<? extends C> constructor = clazz.getConstructor();
//假设存在构造函数 且参数是一个int 一个boolean
Constructor<? extends C> construcetor = clazz.getConstructor(int.class,boolean.class);
Constructor<?>[] construcetor = clazz.getConstructors();
//如果这个类的构造函数继续使用上面的方法是无法获取的
//这时候我们就要使用getDeclaredConstructor()方法
Constructor<? extend C> construcetor = clazz.getDeclaredConstructor(int.class,boolean.class);
Constructor<?>[] construcetor = clazz.getDeclaredConstructors();
- Method
Method提供类或者接口上的方法信息,Method类与获取构造方法的方式相同,存在四种获取成员的方式。
//获取public的方法假设存在一个类型为String 名称是value的参数
Method method = clazz.getMethod("value",String.class);
//获取该类所有public方法
Method[] methods = clazz.getMethods();
//获取不确定是public的方法与不确定是public构造方法类似
Method method = clazz.getDeclaredMethod("value",String.class);
//获取类所有方法包括不是public的方法
Method methods = clazz.getDeclaredMethods();
//获取Method 方法调用
//获取方法名
String name = method.getName();
//获取方法修饰符,这里获取的是int类型需要转换一下
int modifier = method.getModifiers();
String modifiers = Modifier.toString(modifier);
//获取方法的返回值类型 getReturnType()获取的是Class类型
String type = method.getReturnType().getName();
//获取方法所需参数列表
Class<?>[] clazz = method.getParameterTypes();//clazz储存所有参数对应的类型
clazz[0].getName(); //getName();转成String类型
- Field
同样有四种获取成员属性的方法
//假设在class中存在一个成员变量为 value
Field field = clazz.getField("value");
//获取所有public成员变量
clazz.getFields;
//获取是public或者非public的成员变量,和Method,Constructor类似 括号中是参数名
clazz.getDeclaredField("value");
//获取所有public或者非public的成员变量
clazz.getDeclaredFields();
//获取变量类型,获取的是class类型
field.getType();
//获取变量修饰符 获取的是int类型像转换成string类型和上面method一样这里就不多做处理了
field.getModifiers();
//获取变量名
field.getName();
//Field设置私有属性是否可以访问,这里提一下Method也有这个方法,Class是不具有的
field.setAccessible(true);
//获取变量 括号中填的是获取变量的类的对象
field.get(mainActivity)
反射和注解到目前就差不多可以告一段落了,第一次发博客有不足的地方也希望各位能在评论中提出来,下一章节我会介绍注解反射 开发中的使用以及动态代理
1322

被折叠的 条评论
为什么被折叠?



