注解基础

注解基础

  • 一、注解是什么
  • 二、注解的语法
  • 三、注解如何使用

一、注解是什么?

我们先来看看这个解析:

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

mark

What? 是不是一脸懵逼?是不是在想这元数据又是啥东东?哈哈,我第一次看完这段话也是一脸懵逼的表情。下面我们对上面那段话解析一下。

Annotation 翻译过来就是注解、注释、标注的意思,是在JDK1.5引入的新特性,作者是Josh Bloch。等等,你怎么知道是在JDK1.5引入的?你又怎么知道作者是这个谁谁谁的?

mark

??? Talk is cheap,show you the code:

mark

mark

这位Josh Bloch不仅是Java界的顶级高手,他还写了一本书叫《Effective Java》。

mark

好了,扯回正题。

为了便于理解,在这里把注解类比成标签。标签在我们的生活中很常见,例如我们买一包辣条,辣条身上会有一个QS的标签,说明这包辣条是安全的,可以放心食用;但如果没有这个QS的标记,那我们就要小心了,我们可能买了一包山寨辣条。生产厂家给辣条加上QS的标签,当我们消费者看到这个标签,我们就认为这是安全食品。

然后我们回到代码中,@Override是一个系统的注解,在一些重写的方法上都能看到,例如在Activity中常见的onCreate方法:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

onCreate方法上有一个@Override注解,我们可以把这个@Override注解看成一个标签,一个重写的标签,onCreate方法加了这个标签,表明这个方法是重写父类的onCreate方法,告诉编译器在编译的时候要检查这里,看看这里的重写是不是合法的。

下面我们再回过头来看看Java注解的官方文档:

注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

注解有许多用处,主要如下:
- 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
- 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
- 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

二、注解的语法

注解的基本使用

注解的定义

注解通过@interface关键字进行定义。

例如定义一个叫TestAnnotation的注解:

public @interface TestAnnotation {

}
注解的使用

给test方法加上我们刚刚定义的注解TestAnnotation

@TestAnnotation
public void test(){

}

元注解

元注解?妈呀这又是啥?

mark

哈哈,别慌。

元注解其实就是注解的注解。它是用来解释一个注解是一个怎样的注解的。这在我们自定义注解的时候会用到。

Java定义了5个标准的元注解,分别是:
- @Target
- @Retention
- @Documente
- @Inherited
- @Repeatabl

@Target

Target是目标的意思,@Target指定注解可以用在哪些地方,也就是限定注解的使用范围。

@Target的取值有:

  • ElementType.TYPE 可以对类、接口、注解、enum进行注解
  • ElementType.FIELD 可以对变量进行注解
  • ElementType.METHOD 可以对方法进行注解
  • ElementType.PARAMETER 可以对参数进行注解
  • ElementType.CONSTRUCTOR 可以对构造器进行注解
  • ElementType.LOCAL_VARIABLE 可以对局部变量进行注解
  • ElementType.ANNOTATION_TYPE 可以对注解进行注解
  • ElementType.PACKAGE 可以对包进行注解
  • ElementType.TYPE_PARAMETER JDK1.8新增,可以对类型参数进行注解,如泛型。
  • ElementType.TYPE_USE JDK1.8新增,可以对类型进行注解,如泛型。

我们用@Override这个注解来举例说明,我们先看看这个注解的源码定义:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

可以看到@Override注解的@Target的取值是ElementType.METHOD,我们也知道Java中只有方法可以重写,也就是说@Override这个注解只能用于对方法进行注解,如果我们把这个注解用于其他地方(如对变量进行注解),编译就会报错。

@Retention

Retention是保留的意思,表示需要在什么级别保留该注解信息,即用来定义注解的生命周期。

@Retention的取值有三种:

  • RetentionPolicy.SOURC 只保留在源码中,编译后会被丢弃,运行的时候是获取不到的。
  • RetentionPolicy.CLASS 会被保留在class文件中,但运行的时候不会加载到JVM中。
  • RetentionPolicy.RUNTIE 会被保留在class文件中,运行的时候会加载到JVM中,运行的时候可以获取到它。
@Documented

这个注解是跟javadoc相关的,表明将此注解包含在 javadoc 中。

@Inherited

Inherited是继承的意思,但不是注解可以继承注解的意思。

先来看看官方文档的解释(这里是用来装B的)

Indicates that an annotation type is automatically inherited.  If
an Inherited meta-annotation is present on an annotation type
declaration, and the user queries the annotation type on a class
declaration, and the class declaration has no annotation for this type,
then the class's superclass will automatically be queried for the
annotation type.  This process will be repeated until an annotation for this
type is found, or the top of the class hierarchy (Object)
is reached.  If no superclass has an annotation for this type, then
the query will indicate that the class in question has no such annotation.

<p>Note that this meta-annotation type has no effect if the annotated
type is used to annotate anything other than a class.  Note also
that this meta-annotation only causes annotations to be inherited
from superclasses; annotations on implemented interfaces have no
effect.

意思就是说,如果一个类A被用@Inherited注解的注解Z注解了,而且这个类的子类B没有被其他注解注解,那么这个子类B就会自动继承父类A的那个注解Z。

@Repeatable

Repeatable是可重复的意思。这个是JDK1.8新增的,表明一个注解可以多次应用。

例如,一个程序员既会写C,也会写JAVA和Python。

public @interface Languages {
    Language[] value();
}

@Repeatable(value = Languages.class)
public @interface Language {
    LanguageEnum value();
}

public enum LanguageEnum {
    C,
    CPP,
    JAVA,
    PYTHON,
}

@Language(value = LanguageEnum.C)
@Language(value = LanguageEnum.JAVA)
@Language(value = LanguageEnum.PYTHON)
public class Coder {

}

好了,到这里5个元注解就说完了。

mark

下面会介绍注解的属性。


注解的属性

注解的属性也叫成员变量,注解只有属性,没有方法。注解的属性以“无参方法”的形式定义,方法名为属性名,返回类型为属性的类型。属性有默认值,用关键字default 定义。

public @interface TestAnnotation {
    int i();

    String s();

    String ss() default "";
}

@TestAnnotation(i = 1, s = "aaa")
public void test() {

}

有默认值的属性,在使用的时候可以不用设置(如属性ss) 。

三、注解的使用

注解的获取

注解是通过反射去获取的。

  • isAnnotationPresent(Class<? extends Annotation> annotationClass) : 可以通过该方法判断是否应用了某个注解。
  • getAnnotation(Class<T> annotationClass): 获取指定类型的注解。
  • getAnnotations() 获取所有注解。

下面我们通过一个例子说明:

我们先定义一个注解,这个注解可用于类型、成员变量及方法上。

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface TestAnnotation {
    int intValue() default 0;

    String stringValue() default "";
}

然后我们分别在MainActivity类、变量i、方法testMethodAnnotation()上使用我们定义的注解,在getAnnotion()方法里我们分别获取上面使用的三个注解:

@TestAnnotation(intValue = 1, stringValue = "s1")
public class MainActivity extends AppCompatActivity {
    private String TAG = getClass().getSimpleName();

    @TestAnnotation(stringValue = "s2")
    private int i;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getAnnotion();
    }

    public void getAnnotion() {

        //TODO 获取类上注解
        if (getClass().isAnnotationPresent(TestAnnotation.class)) {
            TestAnnotation annotation = getClass().getAnnotation(TestAnnotation.class);
            if (annotation != null) {
                Log.d(TAG, "getAnnotion:class " + annotation.intValue() + "--" + annotation.stringValue());
            }
        }

        //TODO 获取成员变量上的注解
        try {
            Field field = getClass().getDeclaredField("i");
            TestAnnotation annotation = field.getAnnotation(TestAnnotation.class);
            if (annotation != null) {
                Log.d(TAG, "getAnnotion:field " + annotation.intValue() + "--" + annotation.stringValue());
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        //TODO 获取方法上的注解
        try {
            Method method = getClass().getDeclaredMethod("testMethodAnnotation");
            TestAnnotation annotation = method.getAnnotation(TestAnnotation.class);
            if (annotation != null) {
                Log.d(TAG, "getAnnotion:method " + annotation.intValue() + "--" + annotation.stringValue());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }


    }

    @TestAnnotation(intValue = 3, stringValue = "s3")
    public void testMethodAnnotation() {
    }

}

输出的结果:

getAnnotion:class 1--s1
getAnnotion:field -1--s2
getAnnotion:method 3--s3

注解使用的一些注意事项

  • Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
  • 参数成员只能用public或默认(default)这两个访问权修饰。
  • 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
  • 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取Annotation对象,因为你除此之外没有别的获取注解对象的方法(分为编译期 和运行期)

参考资料

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值