Java 注解详解 (annotation)

什么是java注解

注解是java5的新特性。注解可以看做一种注释或者元数据(MetaData),可以把它插入到我们的java代码中,用来描述我们的java类,从而影响java类的行为。

Java注解的目的

使用Java注解一般来说主要有三种目的

  • 构建时指示: RetentionPolicy.SOURCE
  • 编译期指示: RetentionPolicy.CLASS
  • 运行时指示: RetentionPolicy.RUNTIME

Java注解可以用在构建期。当构建我们的工程时,构建进程会编译源码、生成xml文件,打包编译后的代码和文件到jar包。构建过程一般由构建工具自动完成,常用的构建工具有ant、maven。构建工具在构建时会自动扫描我们的代码,当遇到构建期注解时,会根据注解的内容生成源码或者其它文件。

注解基本概念

注解的构成

一个java注解由一个@符后面跟一个字符串构成,类似于这样:

@Entity

java注解中一般包含一些元素,这些元素类似于属性或者参数,可以用来设置值,比如我们有一个包含两个元素的@Entity注解:

@Entity(tableName = "vehicles", primaryKey = "id")

上面注解中有两个元素,tableNameprimaryKey,它们各自都被赋予了自己的元素值。

注解的位置

注解可以用于描述一个类、接口、方法、方法参数、字段、局部变量等。在下边这个例子中,注解分别用在了类、字段、方法、参数和局部变量中:

//注解一个类
@Entity
public class Vehicle {
    //注解一个字段
    @Persistent
    protected String vehicleName = null;
    //注解一个方法
    @Getter
    public String getVehicleName() {
        return this.vehicleName;
    }
    //注解一个参数
    public void setVehicleName(@Optional vehicleName) {
        this.vehicleName = vehicleName;
    }

    public List addVehicleNameToList(List names) {
        //注解一个局部变量
        @Optional
        List localNames = names;
        if(localNames == null) {
        localNames = new ArrayList();
        }
        localNames.add(getVehicleName());
        return localNames;
    }
}

一些常用的内置注解

Java本身提供了三个内置注解,他们分别是:

1. @Deprecated
2. @Override
3. @SuppressWarnings

@Deprecated注解

@Deprecated可以用来描述一个类、方法或者字段,表示java不赞成使用这些被描述的对象,如果我们使用了这些类、方法或者字段,编译器会给我们警告。@Deprecated注解使用方法如下:

@Deprecated
public class MyComponent {
}

在我们实际应用中,在使用@Deprecated注解时,最好同时使用Java Doc的@deprecated符号,用来描述当前类、方法或者字段是不赞成使用的,并且告诉开发者应该用哪个对象替换,如下面例子:

@Deprecated
/**
  @deprecated Use MyNewComponent instead.
*/
public class MyComponent {

}

@Override注解

@Override注解是一个编译时注解,它主要用在一个子类的方法中,当被注解的子类的方法在父类中找不到与之匹配的方法时,编译器会报错。 当我们在子类中覆盖父类的方法时,就要用到@Override注解,这样,如果父类中的方法名称或参数发生改变时,如果子类没有做相应的调整编译器便会报错,这就是@Override注解所起到的作用。当然@Override注解不是强制使用的,但我还是推荐大家尽量使用它。下面是一个@Override注解的例子:

public class MySuperClass {

    public void doTheThing() {
        System.out.println("Do the thing");
    }
}

public class MySubClass extends MySuperClass{

    @Override
    public void doTheThing() {
        System.out.println("Do it differently");
    }
}

@SuppressWarnings

@SuppressWarnings注解的作用是使编译器忽略掉编译器警告。比如,如果我们的一个方法调用了一个@Deprecated方法,或者做了一个不安全的类型转换,此时编译器会生成一个警告。如果我们不想看到这些警告,我们就可以使用@SuppressWarnings注解忽略掉这些警告:

@SuppressWarnings
public void methodWithWarning() {

}

创建自定义注解

定义一个自定义注解

从上面内置注解可以看到,注解很方便也很有用,很多时候我们也需要创建我们自己的注解。创建注解其实和创建类或接口一样简单:

@interface MyAnnotation {
    String   value();
    String   name();
    int      age();
    String[] newNames();
}

上面代码便创建了一个@MyApplication注解,它一共有四个元素。@interface关键字就代表这是一个注解类型,所以使用@interface关键字就可以创建注解了。

需要注意的是,注解中的每个元素定义类似于接口中的方法定义。每个元素定义包含一个数据类型名称,注解元素的数据类型可以是java基本数据类型、String、数组,但不能是复杂对象类型。

下面这段代码演示了如何使用注解:

@MyAnnotation(
    value="123",
    name="Jakob",
    age=37,
    newNames={"Jenkov", "Peterson"}
)
public class MyClass {

}

给注解元素设置默认值

我们可以通过default关键字为某个元素设置默认值,当一个元素被设置默认值之后,这个元素便成了注解的可选元素。
下面我们为@MyAnnotation注解的value元素设置一个默认值:

@interface MyAnnotation {

    String   value() default "";
    String   name();
    int      age();
    String[] newNames();
}

value元素设置默认值之后,再使用时我们就可以省略掉value元素,此时的value值采用的是默认值:

@MyAnnotation(
    name="Jakob",
    age=37,
    newNames={"Jenkov", "Peterson"}
)
public class MyClass {

}

两个元注解:@Retention和@Target

元注解就是注解的注解。我们可以通过元注解来控制描述我们自定义注解的行为。

@Retention

@Retention用来定义当前注解的作用范围,如果我们要把我们的自定义注解限制为运行时有效,那么我们可以使用@Retention注解进行指定:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {

    String   value() default "";

}

注意@MyAnnotation注解上面的@Retention的值:

@Retention(RetentionPolicy.RUNTIME)

上面这个注解会告诉编译器和JVM,这个注解需要在运行时有效,JVM会在运行时通过反射机制获取注解信息,关于如何在运行时利用反射获取注解信息,最后面会进行介绍。@Retention注解的值一共有三种:

  • RetentionPolicy.SOURCE : 注解只存在于源码中,不会存在于.class文件中,在编译时会被忽略掉
  • RetentionPolicy.CLASS:注解只存在于.class文件中,在编译期有效,但是在运行期会被忽略掉,这也是默认范围
  • RetentionPolicy.RUNTIME:在运行期有效,JVM在运行期通过反射获得注解信息

@Target

@Target注解用来约束自定义注解可以注解Java的哪些元素。比如下面这个例子:

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
public @interface MyAnnotation {
    String   value();
}

这个例子中,@Target的值是ElementType.METHOD,通过它的名称可以看出,这个自定义注解只能注解类的方法。

ElementType的值一共有以下几种:

  • ElementType.ANNOTATION_TYPE
  • ElementType.CONSTRUCTOR
  • ElementType.FIELD
  • ElementType.LOCAL_VARIABLE
  • ElementType.METHOD
  • ElementType.PACKAGE
  • ElementType.PARAMETER
  • ElementType.TYPE

其中大部分通过名字就能看出它的作用,不过有两个需要单独介绍一下:

  • ElementType.ANNOTATION_TYPE:元注解类型,只能用来注解其它的注解,例如@Target和@Retention。
  • ElementType.TYPE:可以用来注解任何类型的java类,如类、接口、枚举、或者注解类。

@Inherited

@Inherited注解表示当前注解会被注解类的子类继承。比如有一个自定义注解:

java.lang.annotation.Inherited

@Inherited
public @interface MyAnnotation {

}

如果有一个类使用了上面这个注解:

@MyAnnotation
public class MySuperClass { ... }

那么这个类的子类也会继承这个注解:

public class MySubClass extends MySuperClass { ... }

因为MySubClass 继承了MyClass,而MyClass的注解@MyAnnotation是可继承的,最终MySubClass也会有@MyAnnotation注解。

@Documented

@Documented的作用是告诉JavaDoc工具,当前注解本身也要显示在Java Doc中。比如我们用@Document注解了我们的自定义注解:

import java.lang.annotation.Documented;

@Documented
public @interface MyAnnotation {

}

如果一个类使用了这个注解:

@MyAnnotation
public class MySuperClass { ... }

那么当生成MySuperClass的JavaDoc的时候,@MyAnnotation也会出现在JavaDoc当中。

通过反射获得Java注解信息

上面对注解做了一个详细介绍,具体该如何使用我们的自定义注解呢?其实在现实应用中,我们的自定义注解一般都是起到运行时指示的作用,也就是运行时注解。对于运行时注解,我们可以通过反射机制获得注解信息。

比如我们有一个自定义注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
    public String name();
    public String value();
}

并用这个注解注解了一个类:

@MyAnnotation(name = "hello name",value = "hello value")
public class MyClass {

}

获取类的注解信息

public class TestAnnotation {
    public static void main(String[] args) {
        //通过反射获得MyClass的注解信息
        MyAnnotation myAnnotation=MyClass.class.getAnnotation(MyAnnotation.class);
        System.out.println(myAnnotation.name());
        System.out.println(myAnnotation.value());
    }
}

获取方法的注解信息

public class TestAnnotation {
    public static void main(String[] args) {
        Method method = null;
        try {
            method = MyClassB.class.getMethod("method");
            Annotation annotation = method.getAnnotation(MyAnnotation.class);
            if (annotation !=null) {
                MyAnnotation myAnnotation = (MyAnnotation) annotation;
                System.out.println("name: " + myAnnotation.name());
                System.out.println("value: " + myAnnotation.value());
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

获取参数的注解信息

Method method = ... //obtain method object
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Class[] parameterTypes = method.getParameterTypes();

int i=0;
for(Annotation[] annotations : parameterAnnotations){
  Class parameterType = parameterTypes[i++];

  for(Annotation annotation : annotations){
    if(annotation instanceof MyAnnotation){
        MyAnnotation myAnnotation = (MyAnnotation) annotation;
        System.out.println("param: " + parameterType.getName());
        System.out.println("name : " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
    }
  }
}

获取字段的注解信息

Field field = ... // obtain method object
Annotation annotation = field.getAnnotation(MyAnnotation.class);

if(annotation instanceof MyAnnotation){
    MyAnnotation myAnnotation = (MyAnnotation) annotation;
    System.out.println("name: " + myAnnotation.name());
    System.out.println("value: " + myAnnotation.value());
}
时间: 2024-10-27 07:17:00

Java 注解详解 (annotation)的相关文章

java注解详解

 自Java5.0版本引入注解之后,它就成为了Java平台中非常重要的一部分.Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据. 那么什么是注解呢?举一个简单的例子. [html] view plain copy  print? @Override   public String toString() {       return "This is String Representation of c

Java 中的注解详解及示例代码_java

在Java中,注解(Annotation)引入始于Java5,用来描述Java代码的元信息,通常情况下注解不会直接影响代码的执行,尽管有些注解可以用来做到影响代码执行. 注解可以做什么 Java中的注解通常扮演以下角色 编译器指令 构建时指令 运行时指令 其中 Java内置了三种编译器指令,本文后面部分会重点介绍 Java注解可以应用在构建时,即当你构建你的项目时.构建过程包括生成源码,编译源码,生成xml文件,打包编译的源码和文件到JAR包等.软件的构建通常使用诸如Apache Ant和Mav

Android AOP 注解详解及简单使用实例(三)

Android  注解 相关文章: Android AOP注解Annotation详解(一) Android AOP之注解处理解释器详解(二) Android AOP 注解详解及简单使用实例(三) 一.简介 在Android 里面 注解主要用来干这么几件事: 和编译器一起给你一些提示警告信息. 配合一些ide 可以更加方便快捷 安全有效的编写Java代码.谷歌出的support-annotations这个库 就是主要干这个的. 和反射一起 提供一些类似于spring 可配置的功能,方便简洁. 二

Java设计模式详解之门面模式(外观模式)_java

门面模式(Facade Pattern)也叫外观模式,它隐藏系统的复杂性,并向客户端提供一个可以访问系统的接口.这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性,为子系统中的一组接口提供了一个统一的高层访问接口,这个接口使得子系统更容易被访问或使用.这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用. 简而言之,就是把一堆复杂的流程封装成一个接口供给用户更简单的使用,这个设计模式里有三个角色: 1)门面角色( facade ):

Java虚拟机详解----JVM常见问题总结

[正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考本人之前的系列文章,尤其是那篇:Java虚拟机详解04----GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾回收.类加载机制.   先把本文的目录画一个思维导图:(图的源文件在本文末尾)   一.Java引用的四种状态: 强引用: 用的最广.我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用. 如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足,Java

java关键字(详解)

基本类型 1 boolean 布尔型 2 byte 字节型 3 char 字符型 4 double 双精度 5 float 浮点 6 int 整型 7 long 长整型 8 short 短整型 9 null 空 10 true 真 11 false 假 程序控制语句 1 break 跳出中断 2 continue 继续 3 return 返回 4 do 运行 5 while 循环 6 if 如果 7 else 否则 8 for 循环 9 instanceof 实例 10 switch 观察 11

Java NIO 详解(一)

NIO即新的输入输出,这个库是在JDK1.4中才引入的.它在标准java代码中提供了高速的面向块的IO操作. 一.基本概念描述 1.1 I/O简介 I/O即输入输出,是计算机与外界世界的一个借口.IO操作的实际主题是操作系统.在java编程中,一般使用流的方式来处理IO,所有的IO都被视作是单个字节的移动,通过stream对象一次移动一个字节.流IO负责把对象转换为字节,然后再转换为对象. 关于Java IO相关知识请参考我的另一篇文章:Java IO 详解 1.2 什么是NIO NIO即New

HBase Java API详解

[本文转自HBase Java API详解] HBase是Hadoop的数据库,能够对大数据提供随机.实时读写访问.他是开源的,分布式的,多版本的,面向列的,存储模型. 在讲解的时候我首先给大家讲解一下HBase的整体结构,如下图: HBase Master是服务器负责管理所有的HRegion服务器,HBase Master并不存储HBase服务器的任何数据,HBase逻辑上的表可能会划分为多个HRegion,然后存储在HRegion Server群中,HBase Master Server中存

请教:朋友跟我想写一本关于JAVA虚拟机详解方面的书。

问题描述 朋友跟我想写一本关于JAVA虚拟机详解方面的书.书的内容主要包括JVM的原理,JVM源码分析等方面的问题.书本身内容清晰,层次很分明,也很通俗易懂.目前书已经写了一半,大概6章的内容..不知道怎么联系出版社,如果出版以后销路会如何.也不知道有没有多少读者会关注JAVA虚拟机方面的知识..大家给点意见,或者渠道..谢谢. 解决方案 解决方案二:顶,一直有个小理想,自己写本jvm分析的书,不过未能实现.感觉这种书很小众.jvm原理的书还可以,但代码分析的未必对大部分java程序员有多大价值