内容简介:本文主要参考与借鉴frank909 文章,但更为简单,详细。Annotation中文译过来就是注解、标释的意思。Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。 在 Java 中注解是一个很重要的知识点,注解目前非常的流行,很多主流框架都支持注解,而且自己编写代码的时候也会尽量的去用注解,一是方便,二是代码更加简洁。因为平常开发少见,相信有不少的人员会认为注解的地位不高。其实同 classs 和 interfac
本文主要参考与借鉴frank909 文章,但更为简单,详细。
Annotation中文译过来就是注解、标释的意思。Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。 在 Java 中注解是一个很重要的知识点,注解目前非常的流行,很多主流框架都支持注解,而且自己编写代码的时候也会尽量的去用注解,一是方便,二是代码更加简洁。
注解语法
因为平常开发少见,相信有不少的人员会认为注解的地位不高。其实同 classs 和 interface 一样,注解也属于一种类型。它是在 Java SE 5.0 版本中开始引入的概念。
package java.lang;
import java.lang.annotation.*;
/**
* Indicates that a method declaration is intended to override a
* method declaration in a supertype. If a method is annotated with
* this annotation type compilers are required to generate an error
* message unless at least one of the following conditions hold:
*
* <ul><li>
* The method does override or implement a method declared in a
* supertype.
* </li><li>
* The method has a signature that is override-equivalent to that of
* any public method declared in {@linkplain Object}.
* </li></ul>
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
复制代码
它的形式跟接口很类似,不过前面多了一个 @ 符号。上面的代码就创建了一个名字为 Override的注解。
你可以简单理解为创建了一张名字为 Override的标签。
**Annotations仅仅是元数据,和业务逻辑无关。**理解起来有点困难,但就是这样。如果Annotations不包含业务逻辑,那么必须有人来实现这些逻辑。元数据的用户来做这个事情。Annotations仅仅提供它定义的属性(类/方法/包/域)的信息。Annotations的用户(同样是一些代码)来读取这些信息并实现必要的逻辑。
元注解
元注解是什么意思呢?
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
如果难于理解的话,你可以这样理解。元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。
元标签有 @Retention、@Documented、@Target(当一个注解被 @Target 注解时,这个注解就被限定了运用的场景 )、@Inherited、@Repeatable 5 种。
| @target | 表示该注解可以用于什么地方,可能的ElementType参数有: CONSTRUCTOR:构造器的声明 FIELD:域声明(包括enum实例) LOCAL_VARIABLE:局部变量声明 METHOD:方法声明 PACKAGE:包声明 PARAMETER:参数声明 TYPE:类、接口(包括注解类型)或enum声明 |
|---|---|
| @Retention | 表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括: SOURCE:注解将被编译器丢弃; CLASS:注解在class文件中可用,但会被VM丢弃; RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。 |
| @Document | 将注解包含在Javadoc中 |
| @Inherited | 允许子类继承父类中的注解 |
| @Repeatable | 可重复 (@Repeatable 是 Java 1.8) |
注解的属性
注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。
注解中属性可以有默认值,默认值需要用 default 关键值指定。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Person {
int id() default -10998;
String msg() default "no hello";
}
复制代码
上面代码定义了 Person这个注解中拥有 id 和 msg 两个属性。在使用的时候,我们应该给它们进行赋值。赋值的方式是在注解的括号内以 value=”” 形式,多个属性之前用 ,隔开。 一个注解内仅仅只有一个名字为 value 的属性时,这个注解时可以直接接属性值填写到括号内。 还需要注意的一种情况是一个注解没有任何属性 括号可以省略。。
public @interface NoUse {
}
复制代码
public @interface Chou {
String value() default "You";
}
复制代码
@Person(id = 10758, msg = "hello android")//或者直接默认@Person()
public class Liming {
@Chou("She")
String beautiful;
@NoUse
public void say() {
}
}
复制代码
Java 预置的注解
Java 语言本身已经提供了几个现成的注解。
@Deprecated
这个元素是用来标记过时的元素,想必大家在日常开发中经常碰到。编译器在编译阶段遇到这个注解时会发出提醒警告,告诉开发者正在调用一个过时的元素比如过时的方法、过时的类、过时的成员变量。
@Person(id = 10758, msg = "hello android")//或者直接默认@Person()
public class Liming {
@Chou("She")
String beautiful;
@NoUse
public void say() {
System.out.println(" say is using ");
}
@Deprecated
public void speak() {
System.out.println(" speak is out of date ");
}
}
复制代码
Liming类,它有两个方法 say() 和 speak() ,其中 speak() 被 @Deprecated 注解。然后我们在 IDE 中分别调用它们。
可以看到,speak() 方法上面被一条直线划了一条,这其实就是编译器识别后的提醒效果。
@SuppressWarnings
阻止警告。调用被 @Deprecated 注解的方法后,编译器会警告提醒,而有时候开发者会忽略这种警告,他们可以在调用的地方通过 @SuppressWarnings 达到目的。
@SafeVarargs
参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告,在 Java 1.7 的版本中加入。
上面的代码中,编译阶段不会报错,运行时会抛出 ClassCastException 这个异常。
@FunctionalInterface
函数式接口注解,这个是 Java 1.8 版本引入的新特性。
函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口。
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
复制代码
我们进行线程开发中常用的 Runnable 就是一个典型的函数式接口,上面源码可以看到它就被 @FunctionalInterface 注解。
可能有人会疑惑,函数式接口标记,函数式接口可以很容易转换为 Lambda 表达式。
注解与反射
- 注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
- 然后通过 getAnnotation() 方法来获取 Annotation 对象。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
- 或者是 getAnnotations() 方法。
public Annotation[] getAnnotations() {}
- 前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。
如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。
属性、方法上的注解照样是可以的。同样还是要借助于反射。
public static void getAnnotation() {
boolean hasAnnotation = MainActivity.class.isAnnotationPresent(Person.class);
if (hasAnnotation) {
Person testPerson = MainActivity.class.getAnnotation(Person.class);
System.out.println("id is " + testPerson.id() + " msg is " + testPerson.msg());
}
}
public static void getField() {
try {
Field a = Liming.class.getDeclaredField("beautiful");
a.setAccessible(true);
Chou chou = a.getAnnotation(Chou.class);
if (chou != null) {
System.out.println("check value:" + chou.value());
}
Method noUse = Liming.class.getDeclaredMethod("say");
if (noUse != null) { // 获取方法中的注解
Annotation[] ans = noUse.getAnnotations();
for (int i = 0; i < ans.length; i++) {
System.out.println("method noUse annotation:" + ans[i].annotationType().getSimpleName());
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
System.out.println("NoSuchFieldException");
} catch (NoSuchMethodException e) {
e.printStackTrace();
System.out.println("NoSuchMethodException");
}
}
复制代码
say is using speak is out of date id is -10998 msg is this is not default check value:She method noUse annotation:NoUse Process finished with exit code 0 复制代码
当开发者使用了Annotation 修饰了类、方法、Field 等成员之后,这些 Annotation 不会自己生效,必须由开发者提供相应的代码来提取并处理 Annotation 信息。这些处理提取和处理 Annotation 的代码统称为 APT(Annotation Processing Tool)。
注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。 注解有许多用处,主要如下: - 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息 - 编译阶段时的处理: 软件 工具 可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。 - 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取 复制代码
亲手自定义注解完成某个目的
需求:自定义注解与实现,检查MaSaGei类中的错误并反馈
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckOut {
}
复制代码
public class MaSaGei {
@CheckOut
public void testOne() {
System.out.println(" 1 + 0 = " + ((1 + 1) / 2));
}
@CheckOut
public void testTwo() {
System.out.println(" 1 + 1 = " + (8 / 4));
}
@CheckOut
public void testThree() {
System.out.println(" 1 + 2 = " + (6 / 2));
}
@CheckOut
public void testFour() {
System.out.println(" 1 / 3 = " + (6 / 0));
}
}
复制代码
public class CheckOutTool {
public static void checkAll() {
MaSaGei maSaGei = new MaSaGei();
Class clazz = maSaGei.getClass();
Method[] method = clazz.getDeclaredMethods();
StringBuilder log = new StringBuilder();
// 记录异常的次数
int errornum = 0;
for (Method m : method) {
if (m.isAnnotationPresent(CheckOut.class)) {
m.setAccessible(true);
try {
m.invoke(maSaGei, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
errornum++;
log.append(m.getName());
log.append(" ");
log.append("has error ");
log.append("\\n\\r caused by ");
log.append(e.getCause().getClass().getSimpleName());
log.append("\n\r");
log.append(e.getCause().getMessage());
log.append("\n\r");
}
}
}
log.append(clazz.getSimpleName());
log.append(" has ");
log.append(errornum);
log.append(" error."); // 生成测试报告
System.out.println(log.toString());
}
}
复制代码
结果如下:
testFour has error \n\r caused by ArithmeticException at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.heng.subhey.annotation.CheckOutTool.checkAll(CheckOutTool.java:18) at com.heng.subhey.MainActivity.main(MainActivity.java:22) Caused by: java.lang.ArithmeticException: / by zero / by zero at com.heng.subhey.annotation.MaSaGei.testFour(MaSaGei.java:21) ... 6 more MaSaGei has 1 error. Process finished with exit code 0 复制代码
注解应用实例
JUnit
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
复制代码
@Test 标记了要进行测试的方法 addition_isCorrect()。
ButterKnife
ButterKnife 是 Android 开发中大名鼎鼎的 IOC 框架,它减少了大量重复的代码。
public class GoPayActivity extends BaseActivity {
@BindView(R.id.title_tv)
TextView title_tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
title_tv.setText("支付");
}
@Override
protected int getLayoutId() {
return R.layout.activity_go_pay;
}
@OnClick({R.id.title_back, R.id.pay_confirm})
public void onClick(View v) {
switch (v.getId()) {
case R.id.title_back:
finish();
break;
case R.id.pay_confirm:
ToastUtils.showLong("支付环境安全检测中...");
title_tv.postDelayed(new Runnable() {
@Override
public void run() {
ToastUtils.showShort("即将跳转至支付页面");
Intent charge = new Intent(GoPayActivity.this, KeyWriteActivity.class);
startActivity(charge);
}
}, 2 * 1000);
break;
}
}
}
复制代码
Retrofit
Http 网络访问框架
public interface GitHubService { @GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user); }
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build(); GitHubService service = retrofit.create(GitHubService.class);
复制代码
总结
- 注解难于理解,注解为了解释代码。
- 注解的基本语法,多了个 @ 符号。
- 注解的元注解。
- 注解的属性。
- 注解主要给编译器及工具类型的软件用的。
- 注解的提取需要借助于 Java 的反射技术,反射比较慢,所以注解使用时也需要谨慎计较时间成本。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring 注解编程之模式注解
- Java注解之编译时注解
- Java注解之运行时注解
- Java中的注解-自定义注解
- Java注解Annotation与自定义注解详解
- Java 元注解及 Spring 组合注解应用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Automate This
Christopher Steiner / Portfolio / 2013-8-9 / USD 25.95
"The rousing story of the last gasp of human agency and how today's best and brightest minds are endeavoring to put an end to it." It used to be that to diagnose an illness, interpret legal docume......一起来看看 《Automate This》 这本书的介绍吧!