目录
1.基本概念
注解是元数据的一种形式,提供有关与程序但不属于程序本身的数据
注解对于他们注解的代码没有直接的影响
java中所有的注解默认实现Annotation接口
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
2.基本使用
(1)申明一个注解
public @interface Lance{ }
(2)传递参数
允许在定义注解时设定参数 -> 若设定了参数,在使用注解时可以传递参数
//注解
@Target({ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Lance {
String value(); //无默认值
int age() default 1; //有默认值
}
//使用
@Lance("帅") //如果只存在value元素需要传值的情况,则可以省略:元素名=
@Lance(value="帅",age = 2)
int i;
3.元注解
定义注解时,注解类也能够被其他注解所注解
可以定义在注解上的注解即为元注解
一般情况下使用到的有
@Target 限制此注解可以注解在什么位置
@Retention 指定注解的存储方式
@Target
@Rentention
@Rentention中
Source < Class < Runtime
实例
4.应用场景
4.1 Resource
可以使用在IDE语法检查、APT技术
APT
Anotation Processor Tools -- 注解处理器
编写好的java文件(源代码)需要经过javac的编译(字节码)
注解处理器是javac自带的一个工具,用来在编译时期扫描注解处理信息
注册的注解处理器由javac自动调用,并将注解处理器传给APT处理
注解处理器是对注解应用最为广泛的场景
实例1使用注解IntDef限制入参
有这样一个元注解
@Retention(SOURCE)
@Target({ANNOTATION_TYPE})//标记这是一个元注解
public @interface IntDef {
int[] value() default {};
boolean flag() default false;
boolean open() default false;
}
IntDef的意义在于可以取代枚举类,实现限制方法入参
因为在Java中枚举Enum是 一个单例类的静态成员变量
但是经过javac编译后 ->
’实质是 所有静态成员变量都会变成单例类全部加载进内存中
比常量多5~10倍内存
IntDef注解可以取代枚举类,优化性能
public class TestIntDef {
//第一种方式 静态成员变量
//缺点 -> 可以随便传参 什么样的int数据都能传,但是我只要0或者1
private static int mCurrentDay_int;
private static final int SUNDAT = 0;
private static final int MONDAY = 1;
public static void setmCurrentDay_int(int currentDay){
mCurrentDay_int = currentDay;
}
//第二种方式 枚举类
//缺点 -> 消耗内存
private static Weekday mCurrentDay_enum;
enum Weekday{
SUNDAY,
MONDAY
}
public static void setmCurrentDay_enum(Weekday currentDay){
mCurrentDay_enum = currentDay;
}
//第三种方式 保留在源码级别的注解
@annotation private static int mCurrentDay_annotation; //不标记会报错
public static void setmCurrentDay_annotation(@annotation int currentDay){
mCurrentDay_annotation = currentDay;
}
public static void main(String[] args) {
setmCurrentDay_int(123);
setmCurrentDay_enum(Weekday.SUNDAY);
// setmCurrentDay_annotation(123);
setmCurrentDay_annotation(1);
}
}
实例2使用注解实现自动获取Intent里获取信息
//注解
@Target(ElementType.FIELD) //使用在字段或者属性上
@Retention(RetentionPolicy.RUNTIME) //保留到JVM级别
public @interface Autowired {
String value() default "";
}
//注解解析
public class AutoWiredUtils {
public static void AutoWriedMethod(Activity activity){ //传进来一个Activity
//通过Activity得到Class
Class<? extends Activity> aClass = activity.getClass();
//判空
Intent intent = activity.getIntent();
Bundle extras = intent.getExtras();
if (extras == null){
return;
}
//获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段
Field[] declaredFields = aClass.getDeclaredFields();
// 获得某个类的所有的公共(public)的字段,包括父类中的字段
// aClass.getField("111");
//遍历所有字段
for (Field field : declaredFields) {
//如果有AutoWried注解
if (field.isAnnotationPresent(Autowired.class)){
//得到注解
Autowired annotation = field.getAnnotation(Autowired.class);
//得到属性值 -> 如果注解的值为空 就使用字段名
// -> 如果注解的值不为空 就使用注解的默认值
String value = TextUtils.isEmpty(annotation.value()) ? field.getName() : annotation.value();
if (extras.containsKey(value)){
//得到intent对象
Object o = extras.get(value);
//获得当前field的类型 -> Parcelable数组类型不能直接设置,其他的都可以
//返回数组中元素的Class对象,如果不是Class对象 或者不是数组 那么返回null
Class<?> componentType = field.getType().getComponentType();
//如果是数组并实现了Parcelable接口 -》 只有实现了Parcelable接口的数组才要特殊处理!
if (field.getType().isArray() && Parcelable.class.isAssignableFrom(componentType)){
//强转为数组类型
Object[] objs = (Object[]) o;
创建对应类型的数组并由objs拷贝 -> 将objs数组拷贝到(Class<? extends Object[]>) field.getType()类型的数组
Object[] objects = Arrays.copyOf(objs, objs.length, (Class<? extends Object[]>) field.getType());
o = objects;
}
//设置权限
field.setAccessible(true);
try {
field.set(activity,o);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
//Activity1 传参
public class OneActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_one);
Intent intent = new Intent(this,TwoActivity.class)
.putExtra("intExtra",111)
.putExtra("stringExtra","sss")
.putExtra("arrIntExtra",new int[]{1,2,3})
.putExtra("arrStringExtra",new String[]{"a","b","c"})
.putExtra("arrSerializable",new UserSerializable[]{new UserSerializable("Jack")})
.putExtra("UserSerializable",new UserSerializable("Ross"))
.putExtra("arrParcelable",new UserParcelable[]{new UserParcelable(18)})
.putExtra("UserParcelable",new UserParcelable(20));
startActivity(intent);
}
}
//Activity2获取参数
public class TwoActivity extends AppCompatActivity {
@Autowired
private int intExtra;
@Autowired("stringExtra")
private String stringExtra;
@Autowired
private int[] arrIntExtra;
@Autowired
private String[] arrStringExtra;
@Autowired
private UserSerializable[] arrSerializable;
@Autowired
private UserSerializable UserSerializable;
@Autowired
private UserParcelable[] arrParcelable;
@Autowired
private UserParcelable UserParcelable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
AutoWiredUtils.AutoWriedMethod(this);
// intExtra = getIntent().getIntExtra("int", 000);
// stringExtra = getIntent().getStringExtra("String");
// Log.d("jiang",String.valueOf(intExtra));
// Log.d("jiang",String.valueOf(stringExtra));
// Log.d("jiang", String.valueOf(arrIntExtra[0]));
// Log.d("jiang", String.valueOf(arrStringExtra[2]));
// Log.d("jiang", String.valueOf(arrSerializable[0].name));
// Log.d("jiang", String.valueOf(UserSerializable.name));
// Log.d("jiang", String.valueOf(arrParcelable[0].age));
// Log.d("jiang", String.valueOf(UserParcelable.age));
}
}
实例3使用注解实现自动FindViewById
@Target(ElementType.FIELD) //注解在字段或者属性上
@Retention(RetentionPolicy.RUNTIME) //保留至JVM级别
public @interface injectView {
@IdRes int value(); //@IdRes元注解 -> 限制只能传入资源id
}
public class injectUtils {
public static void injectView(Activity activity){ //传入一个Activity
//通过Activity得到class文件
Class<? extends Activity> aClass = activity.getClass();
// aClass.getField(); 获得自己+父类的成员(不包括private)
// aClass.getDeclaredField(); 只能获得自己的成员(不包括父类)
// aClass.getSuperclass().getDeclaredField() 得到父类的私有成员
//获得子类所有的成员
Field[] declaredFields = aClass.getDeclaredFields();
//遍历所有成员
for (Field field : declaredFields) {
//判断属性是否有此注解标签
if (field.isAnnotationPresent(injectView.class)){
//获得注解对象
injectView inject = field.getAnnotation(injectView.class);
//获得注解的属性值
int idRes = inject.value();
//找到view findViewById()方法就是activity的
View view = activity.findViewById(idRes);
//反射设置属性的值
field.setAccessible(true); //设置可访问权限,设置允许操作private属性
try {
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
public class MainActivity extends AppCompatActivity {
@injectView(R.id.textView)
TextView textView; //默认是private的
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
injectUtils.injectView(this);
Log.d("jiang", "onCreate: " + textView.getText());
textView.setText("jylnbbbbb");
Log.d("jiang", "onCreate: " + textView.getText());
}
}
4.2 Class
定义为Class的注解会保留在class文件中,但是会被虚拟机忽略
完全适合用与字节码增强技术,如AspectJ、热修复Roubust
字节码增强技术即为,直接修改字节码Class文件以达到修改代码执行逻辑的目的
实例
在程序中有多处需要进行是否登录的判断
如果使用普通的编程方式,需要在多处编写冗余的if-else判断
此时可以借助AOP面向切面编程思想,将代码所有功能点分为:需要登录\不需要
jumpA方法需要具备登陆信息,而@Login注解被设置为class(保留到字节码时期)
因此我们能在该类字节码上获得注解信息
在操作字节码时,就能够根据方法是否具备该注解来修改加入if-else判断
结合参数能实现更为丰富的场景如:运行期权限判定
4.3 Runtime
结合反射技术获取注解中的信息