Java
中,注解(Annotation)很是重要,但对于不少开发者来讲,这并不容易理解,甚至以为有点神秘Java
注解的介绍 & 实战攻略,但愿大家会喜欢。
- 注解属于
Java
中的一种类型,同 类class
和 接口interface
同样- 在
Java SE 5.0
开始引入- 基础做用:标识 / 解释
Java
代码
面向 编译器 / APT
使用php
APT(Annotation Processing Tool)
:提取 & 处理Annotation
的代码- 由于当开发者使用
Annotation
修饰类、方法、方法 等成员后,这些Annotation
不会本身生效,必须由开发者提供相应的代码来提取并处理Annotation
信息。这些处理提取和处理Annotation
的代码统称为APT
因此说,注解除了最基本的解释 & 说明代码,注解的应用场景还取决于你想如何利用它java
如出名的测试框架JUnit
= 采用注解进行代码测试git
public class ExampleUnitTest {
@Test
public void Method() throws Exception {
...
}
}
// @Test 标记了要进行测试的方法Method() 复制代码
如 Http
网络请求库 Retrofit
& IOC
框架ButterKnife
github
<-- Http网络请求库 Retrofit -->
// 采用 注解 描述 网络请求参数
public interface GetRequest_Interface {
@GET("ajax.php?a=fy&f=auto&t=auto&w=hello%20world")
Call<Translation> getCall();
}
<-- IOC 框架ButterKnife -->
public class MainActivity extends AppCompatActivity {
// 经过注解设置资源
@BindView(R.id.test)
TextView mTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
}复制代码
注解的类型包括:元注解 、 Java
内置注解 & 自定义注解。ajax
是一种基本注解 =
Android
系统内置的注解
// 元注解 做用于注解 & 解释注解
// 元注解在注解定义时进行定义
@元注解
public @interface Carson_Annotation {
}复制代码
Java
代码 & 解释Java
代码// 注解做用于Java代码 & 解释Java代码
@Carson_Annotation
public class Carson {
}
复制代码
// 元注解@Retention(RetentionPolicy.RUNTIME)的做用:说明 注解Carson_Annotation的生命周期 = 保留到程序运行时 & 被加载到 JVM 中
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation {
}
<-- 元注解@Retention参数说明 -->
// RetentionPolicy.RUNTIME:注解保留到程序运行时 & 会被加载进入到 JVM 中,因此在程序运行时能够获取到它们
// RetentionPolicy.CLASS:注解只被保留到编译进行时 & 不会被加载到 JVM
// RetentionPolicy.SOURCE:注解只在源码阶段保留 & 在编译器进行编译时将被丢弃忽视。
复制代码
Java
文档注解Javadoc
文档中// 元注解@Documented做用:说明 注解Carson_Annotation的元素包含到 Javadoc 文档中
@Documented
public @interface Carson_Annotation {
}复制代码
// 元注解@Target做用:限定了注解Carson_Annotation做用的目标范围 = 方法
// 即注解Carson_Annotation只能用于解释说明 某个方法
@Target(ElementType.METHOD)
public @interface Carson_Annotation {
}
<-- @Target取值参数说明 -->
// ElementType.PACKAGE:能够给一个包进行注解
// ElementType.ANNOTATION_TYPE:能够给一个注解进行注解
// ElementType.TYPE:能够给一个类型进行注解,如类、接口、枚举
// ElementType.CONSTRUCTOR:能够给构造方法进行注解
// ElementType.METHOD:能够给方法进行注解
// ElementType.PARAMETER 能够给一个方法内的参数进行注解
// ElementType.FIELD:能够给属性进行注解
// ElementType.LOCAL_VARIABLE:能够给局部变量进行注解
复制代码
- 前提:子类没有被任何注解应用
- 如,注解
@Carson_Annotation
(被元注解@Inherited
注解)做用于A类,那么A类的子类则继承了A类的注解- 具体使用
// 元注解@Inherited 做用于 注解Carson_Annotation
@Inherited
public @interface Carson_Annotation {
}
// 注解Carson_Annotation 做用于A类
@Carson_Annotation
public class A {
}
// B类继承了A类,即B类 = A类的子类,且B类没被任何注解应用
// 那么B类继承了A类的注解 Carson_Annotation
public class B extends A {}复制代码
Java
1.8后引进
// 1. 定义 容器注解 @ 职业
public @interface Job {
Person[] value();
}
<-- 容器注解介绍 -->
// 定义:自己也是一个注解
// 做用:存放其它注解
// 具体使用:必须有一个 value 属性;类型 = 被 @Repeatable 注解的注解数组
// 如本例中,被 @Repeatable 做用 = @Person ,因此value属性 = Person []数组
// 2. 定义@Person
// 3. 使用@Repeatable 注解 @Person
// 注:@Repeatable 括号中的类 = 容器注解
@Repeatable(Job.class)
public @interface Person{
String role default "";
}
// 在使用@Person(被@Repeatable 注解 )时,能够取多个值来解释Java代码
// 下面注解表示:Carson类便是产品经理,又是程序猿
@Person(role="coder")
@Person(role="PM")
public class Carson{
}
复制代码
注:一个注解能够被多个元注解做用
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation {
}复制代码
类型:Java
中 内置的注解有5类,具体包括:
数组
下面我将对这5类内置注解进行讲解安全
// 用 注解@Deprecated 标记类中已过期的 方法Hello()
public class Buyer2 {
@Deprecated
public void Hello(){
System.out.println("Hello 2015!");
}
}复制代码
使用该类中被 @Deprecated
做用的方法时,IDE
会提示该方法已过期 / 抛弃markdown
主要应用于开发者须要忽略警告时
// 在括号内传入须要忽略警告的属性
@SuppressWarnings("deprecation")
public void test(){
Buyer2 mBuyer2 = new Buyer2();
mBuyer2.hello();
// IDE将不会发出警告(即不会在hello()出现划线)
}复制代码
Java
1.7 后引入
unchecked
警告// 如下是官方例子
// 虽然编译阶段不报错,但运行时会抛出 ClassCastException 异常
// 因此该注解只是做提示做用,可是实际上仍是要开发者本身处理问题
@SafeVarargs // Not actually safe!
static void m(List<String>... stringLists) {
Object[] array = stringLists;
List<Integer> tmpList = Arrays.asList(42);
array[0] = tmpList; // Semantically invalid, but compiles without warnings
String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
}复制代码
定义:函数式接口注解网络
Java
1.8 后引入的新特性多线程
做用:表示该接口 = 函数式接口
函数式接口 (Functional Interface) = 1个具备1个方法的普通接口
具体使用
// 多线程开发中经常使用的 Runnable 就是一个典型的函数式接口(被 @FunctionalInterface 注解)
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
<--额外:为何要用函数式接口标记 -->
// 缘由:函数式接口很容易转换为 Lambda 表达式
// 这是另一个很大话题,此处不做过多讲解,感兴趣的同窗可自行了解复制代码
注解的基础使用包括定义、属性 & 具体使用
// 经过 @interface 关键字进行定义
// 形式相似于接口,区别在于多了一个 @ 符号
public @interface Carson_Annotation {
}
// 上面的代码建立了一个名为 Carson_Annotaion 的注解复制代码
<-- 1. 定义 注解的属性 -->
// 注解的属性 在定义该注解自己时 进行定义
public @interface Carson_Annotation {
// 注解的属性 = 成员变量
// 注解只有成员变量,没有方法
// 注解@Carson_Annotation中有2个属性:id 和 msg
int id();
String msg() default "Hi" ;
// 说明:
// 注解的属性以 “无形参的方法” 形式来声明
// 方法名 = 属性名
// 方法返回值 = 属性类型 = 8 种基本数据类型 + 类、接口、注解及对应数组类型
// 用 default 关键值指定 属性的默认值,如上面的msg的默认值 = ”Hi“
}
<-- 2. 赋值 注解的属性 -->
// 注解的属性在使用时进行赋值
// 注解属性的赋值方式 = 注解括号内以 “value=”xx” “ 形式;用 ”,“隔开多个属性
// 注解Carson_Annotation 做用于A类
// 在做用 / 使用时对注解属性进行赋值
@Carson_Annotation(id=1,msg="hello,i am Carson")
public class A {
}
// 特别说明:若注解只有一个属性,则赋值时”value“能够省略
// 假设注解@Carson_Annotation只有1个msg属性
// 赋值时直接赋值便可
@Carson_Annotation("hello,i am Carson")
public class A {
}
复制代码
// 在类 / 成员变量 / 方法 定义前 加上 “@注解名” 就可使用该注解
@Carson_Annotation
public class Carson {
@Carson_Annotation
int a;
@Carson_Annotation
public void bMethod(){}
}
// 即注解@Carson_Annotation 用于标识解释 Carson类 / a变量 / b方法复制代码
Java
反射技术 因为反射须要较高时间成本,因此注解使用时要谨慎考虑时间成本
<-- 步骤1:判断该类是否应用了某个注解 -->
// 手段:采用 Class.isAnnotationPresent()
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
<-- 步骤2:获取 注解对象(Annotation)-->
// 手段1:采用getAnnotation() ;返回指定类型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
// 手段2:采用getAnnotations() ;返回该元素上的全部注解
public Annotation[] getAnnotations() {}
复制代码
步骤1:自定义2个注解
Carson_Annotation.java
// 由于注解@Carson_Annotation须要在程序运行时使用
// 因此必须采用元注解Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation {
// 注解@Carson_Annotation中有2个属性:id 和 msg
int id();
String msg() default "Hi";
}复制代码
Carson_Annotation2.java
// 由于注解@Carson_Annotation2须要在程序运行时使用
// 因此必须采用元注解Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation2 {
// 注解@Carson_Annotation2中有2个属性:id 和 msg
int id();
String msg() default "Hi";
}复制代码
步骤2:定义1个被注解的类
Test.java
// 1个注解做用于Test类
@Carson_Annotation(id = 1,msg="我是类Test")
public class Test {
// 1个注解 做用于Test类成员变量a
@Carson_Annotation(id = 2,msg="我是变量a")
int a;
// 2个注解 做用于Test类方法
@Carson_Annotation(id = 3,msg="我是方法b")
@Carson_Annotation2(id = 4,msg="我是方法bb(来自第2个注解)")
public void bMethod(){}
}复制代码
步骤3:分别获取一个类、方法 & 成员变量上的注解
private static final String TAG = "Annotation";
/**
* 讲解1:获取类上的注解
*/
// 1. 判断Test类是否应用了注解@Carson_Annotation
boolean hasAnnotation = Test.class.isAnnotationPresent(Carson_Annotation.class);
// 2. 若是应用了注解 = hasAnnotation = true
// 则获取类上的注解对象
if ( hasAnnotation ) {
Carson_Annotation classAnnotation = Test.class.getAnnotation(Carson_Annotation.class);
// 3. 获取注解对象的值
Log.d(TAG, "我是Test类上的注解");
Log.d(TAG, "id:" + classAnnotation.id());
Log.d(TAG, "msg:" + classAnnotation.msg());
}
/**
* 讲解2:获取类成员变量a上的注解
*/
try {
// 1. 获取类上的成员变量a
Field a = Test.class.getDeclaredField("a");
a.setAccessible(true);
// 2. 获取成员变量a上的注解@Carson_Annotation
Carson_Annotation variableAnnotation = a.getAnnotation(Carson_Annotation.class);
// 3. 若成员变量应用了注解 = hasAnnotation = true
// 则获取注解对象上的值 = id & msg
if ( variableAnnotation != null ) {
Log.d(TAG, "我是类成员变量a上的注解");
Log.d(TAG, "id:" + variableAnnotation.id());
Log.d(TAG, "msg:" + variableAnnotation.msg());
}
/**
* 讲解3:获取类方法bMethod上的注解
*/
// 1. 获取类方法bMethod
Method testMethod = Test.class.getDeclaredMethod("bMethod");
// 2. 获取方法上的注解
if ( testMethod != null ) {
// 由于方法上有2个注解,因此采用getAnnotations()得到全部类型的注解
Annotation[] ans = testMethod.getAnnotations();
Log.d(TAG, "我是类方法bMethod的注解");
// 3. 获取注解的值(经过循环)
for( int i = 0;i < ans.length ;i++) {
Log.d(TAG, "类方法bMethod的" + "注解"+ i+ ans[i].annotationType().getSimpleName());
}
}
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
}
}复制代码
Carson的Github地址:Java_Annotation
下面,我将经过注解实现一个最多见的应用场景:测试代码
经过注解,检查一个类中的方法是否存在异常
Carson_Test.java
// 由于注解@Carson_Test须要在程序运行时使用
// 因此必须采用元注解Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Test {
}复制代码
Test.java
public class Test {
@Carson_Test
public void method1(){
System.out.println("method1正常运行 = " + (1+1));
}
@Carson_Test
public void method2(){
System.out.println("method2正常运行 = " + (2*3));
}
@Carson_Test
public void method3(){
System.out.println("method3正常运行 = " + (2/0));
}
}复制代码
Bug
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1. 获取测试类Test对象
Test mTest = new Test();
Class mTest_Class = mTest.getClass();
// 2. 获取测试类Test的全部方法(经过数组存放)
Method[] method = mTest_Class.getDeclaredMethods();
// 3. 经过注解@Carson_Test 测试类中的方法
// a. 遍历类中全部方法
for ( Method m: method ) {
// b. 只有被 @Carson_Test 标注过的方法才容许进行测试
if ( m.isAnnotationPresent( Carson_Test.class )) {
try {
m.setAccessible(true);
// c. 经过反射调用测试类中的方法
m.invoke(mTest);
// d. 捕捉方法出现的异常 & 输出异常信息
} catch (Exception e) {
System.out.println( "Test类出现Bug了!!!");
System.out.println( "出现Bug的方法:" + m.getName());
System.out.println( "Bug类型:" + e.getCause().getClass().getSimpleName());
System.out.println( "Bug信息:" + e.getCause().getMessage());
}
}
}
}复制代码
Carson_Ho的Github地址:Annation_Debug
本文全面讲解了Java
注解(Annotation
)的相关知识,相信您对Java
注解已经了解深入。
下面我将继续对 Android
中的知识进行深刻讲解 ,有兴趣能够继续关注Carson_Ho的安卓开发笔记