文章目录

  • 什么是注解(Annotation)
  • 注解有什么用
  • 注解怎么用
    • 注解关键字 @interface
    • 注解属性的数据类型
  • 元注解
    • @Retention
    • @Target
    • @Documented
    • @Inherited
    • @Repeatable
  • Java内置的注解
  • 如何获取注解
    • 反射获取运行期注解
      • 结合反射与注解实现类似ButterKnife的功能
      • 反射处理注解的缺点
    • APT处理编译期注解

什么是注解(Annotation)

官方:Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java1.5 开始添加到 Java 的。

通俗:注解就是给代码贴上标签。

注解有什么用

提供信息给编译器: 编译器可以利用注解来探测错误和警告信息

编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。

运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

注解怎么用

要牢记,只要用到注解,必然有三点定义注解使用注解获取注解

注解关键字 @interface

//定义注解
@Retention(RetentionPolicy.RUNTIME)
public @interface A {String a();
}//使用注解
@A(a = "test")
public class Test { }//获取注解
Annotation[] annotation = Test.class.getAnnotations();
System.out.println(Arrays.toString(annotation));

注解属性的数据类型

  • 八种基本数据类型
  • String
  • 枚举
  • Class
  • 注解类型
  • 以上类型的一维数组

元注解

元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。

元标签有 @Retention@Documented@Target@Inherited@Repeatable 5 种。

@Retention

Retention 意为保留期。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。

RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。

RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。

RetentionPolicy.RUNTIME 注解可以保留到运行时,所以在程序运行时可以获取到它们

//RetentionPolicy.SOURCE ==========================================================
@Retention(RetentionPolicy.SOURCE)
public @interface A { String s() default "我是默认值"; }//Test.java
@A()
public class Test { }//Test.class,@A()被磨掉了
public class Test {public Test() { }
}//RetentionPolicy.CLASS ==========================================================
@Retention(RetentionPolicy.CLASS)
public @interface A { String s() default "我是默认值"; }//Test.class,@A()还在
@A
public class Test {public Test() { }
}//但是运行时却获取不到注解A
Annotation[] annotation = Test.class.getAnnotations();
System.out.println(Arrays.toString(annotation)); //输出 -> []//RetentionPolicy.RUNTIME ==========================================================
@Retention(RetentionPolicy.RUNTIME)
public @interface A { String s() default "我是默认值"; }//运行时获取到注解A了
Annotation[] annotation = Test.class.getAnnotations();
System.out.println(Arrays.toString(annotation)); //输出 -> [@com.llk.kt.annotation.A(s=我是默认值)]

@Target

Target 意为目标,@Target 指定了注解的使用的场景

public enum ElementType {/** 用于描述类、接口(包括注解类型) 或enum声明 */TYPE,/** 用于描述属性 */FIELD,/** 用于描述方法 */METHOD,/** 用于描述参数 */PARAMETER,/** 用于描述构造器 */CONSTRUCTOR,/** 用于描述局部变量 */LOCAL_VARIABLE,/** 用于描述注解类型 */ANNOTATION_TYPE,/** 用于描述包 */PACKAGE,/*** 用来标注类型参数* @since 1.8*/TYPE_PARAMETER,/*** 能标注任何类型名称* @since 1.8*/TYPE_USE
}

@Documented

这个元注解与文档有关,它的作用是能够将注解中的元素包含到 Javadoc 中去。(基本用不上)

@Inherited

Inherited 意为继承,如果父注解了@Inherited,那么它的子类没有被任何注解应用的话,那么这个子类就继承了父类的注解。

@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface A {String a() default "赛亚人";
}@A(a = "超级赛亚人")
public class Test { }public class Test2 extends Test{}//输出 -> [@com.llk.kt.annotation.A(a=超级赛亚人)]
//如果没有加@Inherited 输出 -> []
Annotation[] annotation = Test2.class.getAnnotations();
System.out.println(Arrays.toString(annotation));

@Repeatable

Repeatable 意为可重复,@Repeatable 是 Java 1.8 才加进来的。

解决同一个注解不能重复在同一类/方法/属性上使用的问题

@Retention(RetentionPolicy.RUNTIME)
public @interface B {A[] value();
}@Repeatable(B.class)
public @interface A {String s() default "请添加描述";
}@A(s = "超级赛亚人")
@A(s = "奥特曼")
@A(s = "橡胶果实能力者")
public class Test { }//输出 -> [@com.llk.kt.annotation.B(value=[@com.llk.kt.annotation.A(s=超级赛亚人), @com.llk.kt.annotation.A(s=奥特曼), @com.llk.kt.annotation.A(s=橡胶果实能力者)])]
Annotation[] annotation = Test.class.getAnnotations();
System.out.println(Arrays.toString(annotation));

Java内置的注解

  • @Override - 检查该方法是否是重写方法。如果发现其父类或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
  • @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。

如何获取注解

反射获取运行期注解

AnnotatedElement接口是获取注解信息的接口

public interface AnnotatedElement {//如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。
public default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { throw new RuntimeException("Stub!"); }//获取当前类的指定注解(包括父类的)
public <T extends Annotation> T getAnnotation(Class<T> annotationClass);//获取当前类的所有注解(包括父类的)
public Annotation getAnnotations();//获取当前类的所有指定注解(包括父类的)
public default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) { throw new RuntimeException("Stub!"); }//获取当前类的指定注解
public default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) { throw new RuntimeException("Stub!"); }//获取当前类的所有指定注解
//如果如果使用了元注解@Repeatable(Java1.8)的话,能实现同一个元素上有多个相同的注解
public default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) { throw new RuntimeException("Stub!"); }//获取当前类的所有注解
public Annotation getDeclaredAnnotations();
}

Class、Constructor、Method、Field这些类都实现了AnnotatedElement接口。其中Class是直接实现的,其他都是继承了AccessibleObject类(它实现了AnnotatedElement接口)。

所以基本上反射常用的类都具备获取注解的能。

结合反射与注解实现类似ButterKnife的功能

先康康Activity中的实现,@BindLayout负责setContentLayout,@BindView负责findViewById,@BindClick负责setOnClickListener。BindUtils.bind(this)是触发解析注解的方法。

@BindLayout(id = R.layout.activity_main)
class MainActivity : AppCompatActivity() {@BindView(id = R.id.btn)lateinit var btn: Button@BindView(id = R.id.btn2)lateinit var btn2: Buttonoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)BindUtils.bind(this)btn.text = "我是1"btn2.text = "我是2"}@BindClick(ids = [R.id.btn, R.id.btn2])fun click(v: View){when (v.id){R.id.btn -> Toast.makeText(this, "我按了1", Toast.LENGTH_SHORT).show()R.id.btn2 -> Toast.makeText(this, "我按了2", Toast.LENGTH_SHORT).show()}}
}

在康康注解的实现

@Retention(RetentionPolicy.RUNTIME) //保留期是运行时
@Target(value = ElementType.TYPE) //目标为类
public @interface BindLayout {int id() default -1;
}@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD) //目标为属性
public @interface BindView {int id() default -1;
}@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD) //目标为方法
public @interface BindClick {int[] ids(); //接受id数组
}

最后康康解析注解的BindUtils,都是写反射的基操。

public class BindUtils {public static void bind(Activity activity){if (bindLayout(activity)){bindView(activity);bindOnClick(activity);}}private static boolean bindLayout(Activity activity){Class clazz = activity.getClass();if (clazz.isAnnotationPresent(BindLayout.class)){BindLayout bindLayout = (BindLayout) clazz.getAnnotation(BindLayout.class);if (bindLayout.id() == -1) return false;activity.setContentView(bindLayout.id());}return true;}private static void bindView(Activity activity){Class clazz = activity.getClass();Field[] fields = clazz.getDeclaredFields();for (Field f : fields){if (f.isAnnotationPresent(BindView.class)){try {BindView bindView = f.getAnnotation(BindView.class);if (bindView.id() == -1) continue;View view = activity.findViewById(bindView.id());if (view == null) continue;f.setAccessible(true);f.set(activity, view);} catch (Exception e) {e.printStackTrace();}}}}private static void bindOnClick(Activity activity){Class clazz = activity.getClass();Method[] methods = clazz.getDeclaredMethods();for (Method m : methods){if (m.isAnnotationPresent(BindClick.class)){BindClick mOnclick = m.getAnnotation(BindClick.class);int[] ids = mOnclick.ids();for (int i = 0; i < ids.length; i++){final View view = activity.findViewById(ids[i]);if(view == null) continue;view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {try {m.setAccessible(true);m.invoke(activity, view);} catch (Exception e) {e.printStackTrace();}}});}}}}
}
反射处理注解的缺点
  1. 只能处理运行期注解;

  2. 效率不高,影响程序性能。

    (通过反射注解方式绑定view,在view数量少的情况还好。但是在实际项目中一个Activity可能需要绑定十几二十个view,而且还可能有数十个这样的Activity。这样情况下使用反射是非常不明智的选择)

ButterKnife的核心不是用反射,而是用APT。

APT处理编译期注解

注解处理器(Annotation Processing Tool)简称APT。它是javac的一个工具。
作用:APT可以用来在编译时扫描和处理注解。
应用场景:在框架中广泛运用,比如:ButterKnife、EventBus、Dagger2、ARouter…

APT内容我单独整理了一个笔记
Java注解处理器学习记录(实现乞丐版ButterKnife)

Java注解学习记录(反射也能实现ButterKnife)相关推荐

  1. Java设计模式学习记录-解释器模式

    前言 这次介绍另一个行为模式,解释器模式,都说解释器模式用的少,其实只是我们在日常的开发中用的少,但是一些开源框架中还是能见到它的影子,例如:spring的spEL表达式在解析时就用到了解释器模式,以 ...

  2. java注释的理解,java注解原理——记录一下自己的理解

    最近因为系统可能要更换成java语言,于是每天都在拼命的研究java的相关知识和框架.之前学习注解的时候,没有太深入的去理解它,只是觉得标注一下挺好用,但是现在在学到spring aop的时候,突然发 ...

  3. Java设计模式学习记录-单例模式

    前言 已经介绍和学习了两个创建型模式了,今天来学习一下另一个非常常见的创建型模式,单例模式. 单例模式也被称为单件模式(或单体模式),主要作用是控制某个类型的实例数量是一个,而且只有一个. 单例模式 ...

  4. java 注解学习_JAVA注解学习

    什么是注解 ? [这个解释起来比较....不知道怎么解释,还是直接百度吧,不会咱就抄一抄嘛~~~] 定义一:用一个词就可以描述注解,那就是元数据,即一种描述数据的数据.所以,可以说注解就是源代码的元数 ...

  5. Java 注解学习笔记

    转自:http://wanqiufeng.blog.51cto.com/409430/458883 一.什么是java注解 注解,顾名思义,注解,就是对某一事物进行添加注释说明,会存放一些信息,这些信 ...

  6. Java注解学习一:注解术语

    2019独角兽企业重金招聘Python工程师标准>>> 一.元注解 @Target:表示该注解可以用于什么地方.可选的ElementType参数包括: TYPE:类,接口(包括注解类 ...

  7. Java SE 学习记录06

    @学习记录 开始学习Java 遵从同学的指导,从Java se开始学习 黑马的JavaSE零基础入门 day06-01 面向对象 package day06;import java.util.Arra ...

  8. Java SE 学习记录——进阶版11

    @学习记录 开始学习Java 遵从同学的指导,从Java se开始学习 黑马的JavaSE零基础入门[网络编程] 第一章 网络编程入门 1.1 软件结构 22-02 软件结构 1.2 网络通信协议 2 ...

  9. java之学习记录 5 - 1 - 模拟拉勾项目介绍与后台系统搭建

    项目架构(此文章只供个人学习的记录) 1 项目介绍 拉勾教育后台管理系统,是提供给拉勾教育的相关业务人员使用的一个后台管理系统, 业务人员可以在这个后台管理系统中,对课程信息.讲师信息. 学员信息等数 ...

最新文章

  1. 【转载】“error LNK1169: 找到一个或多个多重定义的符号”的解决方法
  2. python用中文怎么说-震惊!!!python可以用中文写代码
  3. 微信小程序,前端大梦想(七)
  4. IE9:为什么 Acid3 无足轻重
  5. 协方差矩阵, 相关系数矩阵
  6. WKViewManager iOS 弹窗架构
  7. 畅游“私有化”完成,搜狐股价暴涨逾25%
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的高校科研管理系统
  9. linux开发板通过网线连接电脑
  10. cesium添加高德路网中文注记图及高德在线地图介绍
  11. java zip解压抛出异常,java – ZipFile抛出错误,但ZipInputStream能够解压缩归档
  12. 特网云 DirectAdmin 安装SSL
  13. list(map(tokenizer.tokenize, text))
  14. Parallel ScavengeGC收集器
  15. 齐岳2-苯基吡啶(C-N)|2-phenylpyridine|cas1008-89-5
  16. Unity2D游戏开发之保卫萝卜
  17. io密集服务器cpu性能,线程池中CPU密集型和IO密集型选择
  18. IDA 和 IDA-Python 学习笔记
  19. 微信小程序订单页面格式
  20. 海思Hi3516DV300软硬件全套开发资料

热门文章

  1. 人“贱”人爱——香港电影“贱”星X档案
  2. Pregel模型原理
  3. oracle如何使用子查询,Oracle-子查询
  4. 聊聊程序员如何学习英语单词:写了一个记单词的小程序
  5. PHP代码猜数字游戏,基于JavaScript实现猜数字游戏代码实例
  6. LibFuzzer学习
  7. 传奇所有地图参数的意思
  8. 刚入职程序员竟是培训出身!网友:百度3年和培训班一个水平吗?
  9. Manjaro升级后无法打开AppImage
  10. TLINK时报错Fatal:No Program entry point的解决办法