Java 进阶巩固:什么是注解以及运行时注解的使用
这篇文章 2016年12月13日星期二 就写完了,当时想着等写完另外一篇关于自定义注解的一起发。结果没想到这一等就是半年多 - -。
有时候的确是这样啊,总想着等条件更好了再干,等准备完全了再开始,结果好多想法、好多事情都不了了之。
读完本文你将了解:
什么是注解
注解是一种元数据(描述数据的数据)
描述作用,不会直接生效,需要在编译前/运行时获取注解信息
代码检查
注解简单的说就是以 @ 开头的一个字符串,在 Android Studio 默认是黄色高亮,比如下面的 @Override:
这里的 @Override 没有值,只是一个修饰作用,告诉编译器这个方法要覆盖父类的方法,编译器会去检查父类有没有这个方法。
我们在使用注解时可以传入更详细的内容,使用 “key1=value1, key2=value2”的格式传入,比如:
@Author(name = "shixinzhang", date = "2016.12.13")
public class AnnotationTestActivity extends BaseActivity {...}
@Author 注解内部有两个字符串,分别为 name, date:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Author {
String name();
String date();
}
当注解的属性只有一个时,可以命名为 value,这样在使用时可以使用快捷方式 – 直接传入值,而不是声明属性名,比如下面的 @ContentView,只有一个名称为 value() 的属性:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContentView {
//属性叫 value ,在使用时可以直接传参数即可,不必显式的指明键值对,是一种快捷方法
int value();
}
使用时直接传参数,不必指明属性名:
@Author(name = "shixinzhang", date = "2016.12.13")
@ContentView(R.layout.activity_annotation)
public class AnnotationTestActivity extends BaseActivity {...}
Java 内置的注解
上面的两个自定义注解用到了 @Retention, @Target,它们其实是 Java 内置的注解,方便我们使用。
Java 内置的注解主要有 9 个,分为位于 java.lang or java.lang.annotation 包下。
5 个用于通知编译器信息的注解:
- @Override :空注解,用于标记那些覆盖父类方法的方法,如果父类没有这个方法,或者复写的方法访问权限比父类的权限小,编译器就会报错
- @Deprecated : 空注解,用于标记那些不应该被使用的代码,如果使用了过时的代码,编译器会发出警告
- @SafeVarargs : 空注解,(varargs 可变参数)用于标记构造函数或者方法,通知编译器,这里的可变参数相关的操作保证安全
- @FunctionInterface : Java SE 8 出现的,用于通知编译器,这个类型是 function 接口
- @SuppressWarning:抑制错误,可以用于标记整个类、某个方法、某个属性或者某个参数,用于告诉编译器这个代码是安全的,不必警告
- 强烈建议最小范围使用这个注解,一旦你在一个比较大的范围抑制错误,可能会把真正的问题掩盖了
@SuppressWarning 支持的参数如下及使用方式见这篇 @SuppressWarning 使用及支持的参数.
4 个用于修饰注解的注解:
修饰其他注解的注解称为“元注解”。
- @Documented:让注解信息出现在 document 中
- @Retention : 指出注解如何存储,支持以下三种参数
- RetentionPolicy.SOURCE : 注解只保留在源码中,编译时会忽略
- RetentionPolicy.CLASS : 更高一级,编译时被编译器保留,但是运行时会被 JVM 忽略
- RetentionPolicy.RUNTIME : 最高级,运行时会被保留,可以被运行时访问
- @Target :指出注解作用于(修饰)什么对象,支持以下几种参数
- ElementType.TYPE : 作用于任何类、接口、枚举
- ElementType.FIELD : 作用于一个域或者属性
- ElementType.METHOD : 作用于一个方法
- ElementType.PARAMTER : 作用于参数
- ElementType.CONSTRUCTOR : 作用于构造函数
- ElementType.LOCAL_VARIABLE : 作用于本地变量
- ElementType. ANNOTATION_TYPE : 作用于注解
- ElementType.PACKAGE : 作用于包
- @Inherited :当前注解是否可以继承
自定义一个注解
创建注解时,需要声明的类型为 @interface,看起来和接口有一些相似哈,其中的 @ 标明这是一个注解类型( “at ~ annotation type”):
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContentView {
//属性叫 value ,在使用时可以直接传参数即可,不必显式的指明键值对,是一种快捷方法
int value();
}
注解除了名字和接口有些相似,内容也很相似,都是声明一个方法,规定返回值,不同的是这里的方法其实是个属性,返回值规定了属性的类型(至于为什么要声明成方法而不是属性,可能是为了后续直接使用这个方法获取值比较直观吧)。
注意:如果你的注解中创建了多个属性,但是使用时只需要使用某几个,这时编译器会提示你有没有指明的属性。
我们可以使用 default … 为注解的某个属性指定默认值,这样即使不指定某个属性,编译器也不会报错。这通常可以节约很多时间,比如这样:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface Author {
String name() default "shixinzhang";
String date();
}
当我们使用 @Author 时没有指定 name = XXX,则会默认为 “shixinzhang”。
注解的作用
注解可以用来修饰类、方法、参数等等,具体的使用场景有以下三种:
- 编译前提示信息:注解可以被编译器用来发现错误,或者清除不必要的警告;
- 编译时生成代码:一些处理器可以在编译时根据注解信息生成代码,比如 Java 代码,xml 代码等;
- 运行时处理:我们可以在运行时根据注解,通过反射获取具体信息,然后做一些操作。
注解的用法
- 自定义注解:规定处理对象类型,保存阶段,以及包含的值
- 使用注解修饰我们想要的处理的类、方法或者属性
- 读取注解,使用注解处理器处理
注解处理器分为两种:
- 运行时处理器
- 编译时处理器
先介绍简单的一种:运行时注解处理器。
运行时注解
运行时注解需要使用 注解 + 反射 ,非常简单。
我们先自定义一个 ContentView 注解,表示当前布局对应的 layout 文件:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContentView {
//属性叫 value ,在使用时可以直接传参数即可,不必显式的指明键值对,是一种快捷方法
int value() ;
}
然后用它修饰一个 Activity:
@ContentView(R.layout.activity_annotation)
public class AnnotationTestActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //调用父类的 onCreate
setContentView(R.layout.activity_annotation);
}
}
在 BaseActivity 中反射获取当前类使用的注解,拿到注解的值,就可以直接设置布局了:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
annotationProcess();
}
//读取注解,进行处理
private void annotationProcess() {
Class c = this.getClass();
//遍历所有子类
for (; c != Context.class; c = c.getSuperclass()) {
//找到使用 ContentView 注解的类
ContentView annotation = (ContentView) c.getAnnotation(ContentView.class);
if (annotation != null) {
try { //有可能出错的地方都要 try-catch
//获取 注解中的属性值,为 Activity 设置布局
this.setContentView(annotation.value());
} catch (RuntimeException e) {
e.printStackTrace();
}
return;
}
}
}
这样就简单实现了运行时根据注解动态设置布局的功能。
总结
黑科技、低性能
使用注解往往可以实现用非常少的代码作出匪夷所思的事情,比如 ButterKnife。
但被人诟病的是,运行时注解需要使用大量 Java 反射而引起较为严重的性能问题。
在使用运行时注解时需要小心,在调用方法时注意对异常的捕获,避免调用失败。
下一篇文章我们了解更为高性能的注解:使用编译时注解简单实现类似 ButterKnife 的效果。
Thanks
https://docs.oracle.com/javase/tutorial/java/annotations/index.html
http://tutorials.jenkov.com/java/annotations.html
http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html
Java 进阶巩固:什么是注解以及运行时注解的使用的更多相关文章
- 自定义注解之运行时注解(RetentionPolicy.RUNTIME)
对注解概念不了解的可以先看这个:Java注解基础概念总结 前面有提到注解按生命周期来划分可分为3类: 1.RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成clas ...
- Android运行时注解
Android的注解有编译时注解和运行时注解,本文就介绍下运行时注解. 其实非常简单,直接上代码:本文主要是替代传统的findViewById()的功能,就是在我们Activity中不需要再使用fin ...
- Android中的自定义注解(反射实现-运行时注解)
预备知识: Java注解基础 Java反射原理 Java动态代理 一.布局文件的注解 我们在Android开发的时候,总是会写到setContentView方法,为了避免每次都写重复的代码,我们需要使 ...
- Java 9 揭秘(7. 创建自定义运行时映像)
Tips 做一个终身学习的人. 在第一章节中,主要介绍以下内容: 什么是自定义运行时映像和JIMAGE格式 如何使用jlink工具创建自定义的运行时映像 如何指定命令名称来运行存储在自定义映像中的应用 ...
- 读书笔记:深入理解java虚拟机(一)虚拟机的运行时的数据区域
最近在看深入了解java虚拟机第一版(周志明著),特此写读书笔记,整理其中重要的东西和自己的理解. ”java与c++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却 ...
- Java内存区域与内存溢出异常--运行时数据区
Java与C之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”. C.C++程序开发在内存管理区域,既拥有每一个对象的“所有权”,又担负着每一个对象声明开始到终结的责任,而Java在虚拟机自动管理 ...
- Java内存区域与内存溢出异常---运行时数据区域
运行时数据区域 Java虚拟机所管理的内存将会包括以下几个运行时数据区域 线程私有区域 1.程序计数器 程序计数器记录的是当前正在执行的虚拟机字节码指令所在的地址.在虚拟机的概念模型中,字节码解释 ...
- 深入理解java虚拟机阅读笔记(1)运行时数据区域
java虚拟机所管理的内存区域主要分为方法区.堆:虚拟机栈.本地方法栈.程序计数器,如图: 1.程序计数器是当前线程所执行的字节码行号指示器,用以记录当前指令执行的位置.程序计数器是线程私有的,每个线 ...
- 你必须了解的java内存管理机制(一)-运行时数据区
前言 本打算花一篇文章来聊聊JVM内存管理机制,结果发现越扯越多,于是分了四遍文章(文章讲解JVM以Hotspot虚拟机为例,jdk版本为1.8),本文为其中第一篇.from 你必须了解的java内存 ...
随机推荐
- 20145331 《Java程序设计》第6周学习总结
20145331 <Java程序设计>第6周学习总结 教材学习内容总结 第十章 输入/输出 10.1.1串流 •Java将输入/输出抽象化为串流,数据有来源及目的地,衔 ...
- C#生成PDF2019
因接口生成Pdf推送, 工作需要进行Pdf生成,但网上生成Pdf的文档好少: 1.生成Pdf需要文件路径/内容 都可以配置 2.使用组件 itextsharp.dll 本人用版本:v2.0.5072 ...
- [CF1042F]Leaf Sets
题意:给定一棵$n$个点的树,将叶子节点分为数个集合使集合里点对最长距离不超过$k$,求最少集合数.($n\le1000000$) 首先我们可以想到,这道题并不是让你构造最优方案,因为只要把所有叶子节 ...
- Spring容器基础ClassPathXmlApplicationContext(一起看源码)
ApplicationContext相比较于BeanFactory,扩展了很多功能.也就是说前者包含了后者的所有功能.使用前者加载XML的方式:ApplicationContext app=new C ...
- Spring mvc异步处理
基于Servlet3.0的异步处理,springmvc的异步处理 控制器返回callable, spring mvc异步处理,将callable提交到TaskExecutor 使用一个隔离线程进行执 ...
- Implement JSON Web Tokens Authentication in ASP.NET Web API and Identity 2.1 Part 3 (by TAISEER)
http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-an ...
- UVA 814 The Letter Carrier's Rounds(JAVA基础map)
题解:就是按照题目模拟就好 但是这个题目让我发现了我Java里面许多问题 具体看代码,但是还是分为这几个方面 属性的作用域问题,缓冲区问题,map与list映射的问题,输出多个空格不一定是/t,反转思 ...
- GridView绑定数据源 绑定DataReader /DataSet /DataTable
有一个GridView1 <asp:GridView ID="GridView1" runat="server"></asp:GridView ...
- 使用IDEA创建SpringBoot自定义注解
创建SpringBoot项目 添加组织名 选择web 输入项目名称 创建后目录结构为 使用Spring的AOP先加入Maven依赖 <dependency> <groupId> ...
- nyoj——5字符串STL复习
Binary String Matching 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 Given two strings A and B, whose alp ...